Implement GutenbergKitResources binary dependency build#318
Implement GutenbergKitResources binary dependency build#318
Conversation
d39e34e to
e53dfea
Compare
| /// - ``always``: Always returns `true` - cached responses are always used. | ||
| /// | ||
| package func allowsResponseWith(date: Date, currentDate: Date = .now) -> Bool { | ||
| func allowsResponseWith(date: Date, currentDate: Date = .now) -> Bool { |
There was a problem hiding this comment.
SwiftPM required packageAccess: false on the GutenbergKit target for binary target compatibility.
With that flag off, the compiler no longer passes -package-name, so the package access modifier silently degrades to fileprivate breaking visibility across files within the module.
| let gutenbergCSS = Self.loadGutenbergCSS() ?? "" | ||
| let gutenbergCSS = GutenbergKitResources.loadGutenbergCSS() ?? "" | ||
| assert(!gutenbergCSS.isEmpty, "Failed to load Gutenberg CSS from bundle. Previews will not render correctly.") |
There was a problem hiding this comment.
There might be more robust ways to handle a path error internally, but I consider them out of scope for the moment.
.gitignore
Outdated
| /ios/Sources/GutenbergKit/Gutenberg/assets | ||
| /ios/Sources/GutenbergKit/Gutenberg/index.html |
There was a problem hiding this comment.
These should not actually be generated anymore. We can keep them here for a while to avoid issues while local copies update, or get rid of them already to avoid carrying dead weight. What do you think?
| /ios/Sources/GutenbergKit/Gutenberg/assets | |
| /ios/Sources/GutenbergKit/Gutenberg/index.html |
There was a problem hiding this comment.
I suggest we remove them. Developers can clean the files as needed.
| @@ -0,0 +1,152 @@ | |||
| #!/usr/bin/env bash | |||
There was a problem hiding this comment.
It took me a while to get this to run in #315 and it's based on a setup that had it in the root and I wasn't game to move it just yet.
There was a problem hiding this comment.
https://linear.app/a8c/issue/AINFRA-1983/followup-gutenbergkitresources-setup-move-script-away-from-root tracks moving it in a better location
9e7a477 to
76639c8
Compare
d1deddd to
60a14ac
Compare
|
@dcalhoun I can't tell if the E2E tests are due to my changes or not. I noticed you recently worked on an E2E fix, which I rebased this on top of. Maybe with your extra context you can let me know? Thanks! |
Thanks for the continued efforts on this, @mokagio! 🙇🏻♂️ It seems the CI failures are likely related to the changes in this pull request. When running the iOS UI tests locally in Xcode, I see errors related to the Dev server error
I was able to circumvent the error with the following diff. diff --git a/ios/Sources/GutenbergKit/Sources/EditorViewController.swift b/ios/Sources/GutenbergKit/Sources/EditorViewController.swift
index 1993d818..828d2519 100644
--- a/ios/Sources/GutenbergKit/Sources/EditorViewController.swift
+++ b/ios/Sources/GutenbergKit/Sources/EditorViewController.swift
@@ -465,7 +465,7 @@ public final class EditorViewController: UIViewController, GutenbergEditorContro
onSelection: { [weak self] in self?.didSelectBlockInserterItem($0) },
onClose: { [weak self] in self?.notifyInserterClosed() }
)
- .environmentObject(htmlPreviewManager)
+// .environmentObject(htmlPreviewManager)
})
context.viewController = host
|
|
Thanks for the hints @dcalhoun . I'll see where they lead. |
39a064c to
74af0b9
Compare
| @curl -sX POST "https://api.buildkite.com/v2/organizations/automattic/pipelines/gutenbergkit/builds" \ | ||
| -H "Authorization: Bearer $(BUILDKITE_API_ACCESS_TOKEN)" \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"commit":"HEAD","branch":"ainfra-1967-implement-gutenbergkitresources-binary-dependency-build","message":"Release $(NEW_VERSION)","env":{"NEW_VERSION":"$(NEW_VERSION)"}}' \ |
There was a problem hiding this comment.
Noting that we should update the branch before merging.
|
|
||
| - label: ':swift: Test Swift Package' | ||
| command: swift test | ||
| command: make test-swift-package REFRESH_L10N=1 REFRESH_JS_BUILD=1 |
There was a problem hiding this comment.
After #320, we now cache the JavaScript build as an artifact to improve speed and mitigate 429 errors for translation downloads. Can we reuse that artifact for this job?
There was a problem hiding this comment.
| install_gems | ||
|
|
||
| echo "--- :xcode: Building XCFramework" | ||
| make build-resources-xcframework REFRESH_L10N=1 REFRESH_JS_BUILD=1 |
There was a problem hiding this comment.
Similar to my comment on the Buildkite configuration, should/can we reuse the JavaScript build artifact?
There was a problem hiding this comment.
Struggling to understand how these XCFramework releases coincide/integrate with GutenbergKit releases.
The existing release script for the latter bumps the version for inclusion in the JavaScript, so that accurate version numbers are reported in exceptions. Should the XCFramework and GutenbergKit versions align? Maybe that is only required for tagged GutenbergKit releases, and XCFramework releases for Git commits can simply use the last tagged version string?
I had Claude capture the issue and potential solution in its own words below as well. I'm remain uncertain as to the current state, plan, or appropriate solution.
WDYT?
Additionally, the JS assets baked into the XCFramework will carry whatever version is in
package.jsonat the time of the build — currently0.14.0-alpha.0ontrunk. This version propagates to crash report tags (gutenberg_kit_versioninexception-parser.js) and the generatedGutenbergKitVersion.swift/.ktfiles (used in User-Agent strings).Since
package.jsonis never bumped to$versionbefore the build runs, the XCFramework artifact will report a stale version at runtime even though the git tag and S3 path use the correct release version.I think this needs an
npm --no-git-tag-version version "$version"before themakecall, so the version flows through the samepackage.json→generate-version.js→ Vite build pipeline that the local release script (bin/release.sh) already uses.
There was a problem hiding this comment.
Thanks for this @dcalhoun . To be honest, I'm still unsure about how to proceed, too. So far, I've just tried to mimic the wordpress-rs behavior to have CI track the checksum and revision in the Package.swift upon "releasing".
You bring up a good point regarding the version bump. It's frustrating that there's no way to do a version bump and Package.swift update atomically.
I'll have to think about this...
| File.open(file_path, 'w') { |file| file.print lines.join } | ||
| end | ||
|
|
||
| lane :publish_release_to_github do |options| |
There was a problem hiding this comment.
What, if any, changes should we make the project's existing bin/release.sh script? Does it remain valid for a different purpose? Should we update it to align with App Infra best practices/tooling?
This may be out of scope for this PR, but it felt worthy of discussing here nonetheless.
.gitignore
Outdated
| /ios/Sources/GutenbergKit/Gutenberg/assets | ||
| /ios/Sources/GutenbergKit/Gutenberg/index.html |
There was a problem hiding this comment.
I suggest we remove them. Developers can clean the files as needed.
|
|
||
| .PHONY: release | ||
| release: ## Create and publish a new release | ||
| release: ## Create and publish a new release (local) |
There was a problem hiding this comment.
I'm unsure of the (local) designation. It's local in the sense that this script was intended to be run a local development machine, but it does publish tags and GitHub Releases—releases intended to be downloaded and consumed by a host app.
Should release and release-on-ci be labeled as GutenbergKit and GutenbergKitXCFramework respectively? My perception is that they "release" two, disparate things. Is that accurate or am I misunderstanding the state of things?
Do we plan to use both targets in tandem when publishing GutenbergKit release? I suppose this relates to my other comment about coordinating/composing release version bumps/strings.
There was a problem hiding this comment.
Transparently, I'm less familiar with the inner workings of Swift/Xcode builds. That said, this script makes sense and looks appropriate to me.
Introduces a new `GutenbergKitResources` target to host the built Gutenberg web assets as a separate module. `Package.swift` uses `GUTENBERGKIT_SWIFT_USE_LOCAL_RESOURCES` env var to switch between a local source target (development) and a pre-built XCFramework binary target (releases). Sets `packageAccess: false` on `GutenbergKit` — required for binary target compatibility within the same package. --- Generate with the help of Claude Code, https://code.claude.com Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Required by `packageAccess: false` on the GutenbergKit target, which enables binary target compatibility for GutenbergKitResources within the same package. --- Generate with the help of Claude Code, https://code.claude.com Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace `Bundle.module` lookups in `EditorViewController` and `HTMLPreviewManager` with the new module's API. --- Generate with the help of Claude Code, https://code.claude.com Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Resources are now served by GutenbergKitResources. The `Gutenberg/` directory is excluded from the target and both build output directories are gitignored. --- Generate with the help of Claude Code, https://code.claude.com Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The script archives GutenbergKitResources for device and simulator, creates an XCFramework, and outputs a zip with checksum for SPM consumption. Makefile changes: - Set GUTENBERGKIT_SWIFT_USE_LOCAL_RESOURCES for all targets - Copy dist output to GutenbergKitResources/Resources/ - Add build-resources-xcframework target --- Generate with the help of Claude Code, https://code.claude.com Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Swift tests now build resources locally first - Add XCFramework build step with artifact upload --- Generate with the help of Claude Code, https://code.claude.com Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Modeled on wordpress-rs. The `release` lane: 1. Validates the version doesn't already exist 2. Updates Package.swift version and checksum 3. Tags, pushes, and creates a GitHub release 4. Uploads XCFramework zip to S3 --- Generate with the help of Claude Code, https://code.claude.com Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
These ~58 files are now generated during the build and distributed via the GutenbergKitResources XCFramework. --- Generate with the help of Claude Code, https://code.claude.com Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
A directory named `Resources` inside a flat `.bundle` confuses `codesign`: it can't distinguish the iOS flat layout from the macOS deep layout, failing with "bundle format unrecognized, invalid, or unsuitable". Renaming to `Gutenberg` avoids the reserved name. --- Generated with the help of Claude Code, https://claude.ai/code Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
The build script names the zip with the git SHA
(`GutenbergKitResources-{sha}.xcframework.zip`), but
`Package.swift` expects the stable name
`GutenbergKitResources.xcframework.zip`.
Use the stable name as the S3 key so SPM can resolve it.
---
Generated with the help of Claude Code, https://claude.ai/code
Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
Ruby's `||` doesn't catch empty strings, so `version:` with no value silently passes through, producing S3 keys without a version segment. --- Generated with the help of Claude Code, https://claude.ai/code Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
--- Generated with the help of Claude Code, https://claude.ai/code Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
Inline `$$` escaping in pipeline YAML failed to pass `BUILDKITE_TAG` through to the shell. A standalone script with `set -euo pipefail` avoids the double-interpolation issue entirely. --- Generated with the help of Claude Code, https://claude.ai/code Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
Without it, iOS refuses to install the app: "missing or invalid CFBundleExecutable in its Info.plist". --- Generated with the help of Claude Code, https://claude.ai/code Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
SPM produces a `.o` object file for library targets. Copying it directly as the framework binary caused Xcode to statically link the code into the consuming app. This put the auto-generated `BundleFinder` class in the app binary, so `Bundle(for: BundleFinder.self)` resolved to the main bundle instead of the framework bundle—missing the resource bundle nested inside the framework directory. Linking the `.o` into a proper dylib keeps `BundleFinder` in the framework at runtime, fixing the crash: `unable to find bundle named GutenbergKit_GutenbergKitResources` --- Generated with the help of Claude Code, https://claude.ai/code Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
The `test-ios-e2e` target copied build output into `GutenbergKit/Gutenberg/` but not into the new `GutenbergKitResources/Gutenberg/` directory, which is where the editor now loads resources from. --- Generated with the help of Claude Code, https://claude.ai/code Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
The `GutenbergKit` target now excludes `Gutenberg/` in `Package.swift`—all web assets are served from the `GutenbergKitResources` target instead. --- Generated with the help of Claude Code, https://claude.ai/code Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
Separate the iOS and Android dist copy steps into dedicated Makefile targets so they can be run or debugged independently. --- Generated with the help of Claude Code, https://claude.ai/code Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
Replace the env var toggle and separate version/checksum constants with a single `DependencyMode` enum, matching the pattern used in wordpress-rs. The enum is generic and reusable across projects. The Fastlane `update_swift_package` lane now rewrites one line instead of two. --- Generated with the help of Claude Code, https://claude.ai/code Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
Trunk always builds from source now. CI rewrites this line to `.release` when cutting a tagged release. The env var toggle is no longer needed. --- Generated with the help of Claude Code, https://claude.ai/code Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
Replace the tag-triggered publish step with one guarded by `NEW_VERSION` env var. The new `.buildkite/release.sh` orchestrates the full flow: build xcframework, sign, then hand off to `fastlane release` which rewrites `Package.swift`, tags, and uploads. The tag is an output of the release process, not a trigger. --- Generated with the help of Claude Code, https://claude.ai/code Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
For end-to-end testing of the release flow. Revert before merging. --- Generated with the help of Claude Code, https://claude.ai/code Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
Match the Buildkite docs naming convention. --- Generated with the help of Claude Code, https://claude.ai/code Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
GutenbergKit xcframeworks were never signed. The signing step was copied from wordpress-rs but the CI environment doesn't have the ASC env vars configured. --- Generated with the help of Claude Code, https://claude.ai/code Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
The release step needs write access to push tags and commits. The bot credentials are managed by buildkite-ci and require the pipeline to be in the `use-bot-for-git` allow-list. --- Generated with the help of Claude Code, https://claude.ai/code Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
The old `ios/Sources/GutenbergKit/Gutenberg/` build output path is no longer used now that dist goes into `GutenbergKitResources`. PR review feedback from @dcalhoun. --- Generated with the help of Claude Code, https://claude.ai/code Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>
Must have come in with a rebase...
9e01fbb to
0184d1b
Compare
|
Closing in favor of #376 because I need to do more thinking around the XCFramework and release automation. |


Note
Closed in favor of #376
What?
Adds infrastructure to build and distribute
GutenbergKitResourcesas a pre-built XCFramework, decoupling the JS build from Swift consumers.Based on the research work done on #315 .
Why?
Currently, every Swift consumer must run the full JS build to get Gutenberg editor assets.
With a binary XCFramework for resources, consumers can pull a pre-built artifact from CDN instead, significantly reducing build times and simplifying integration.
How?
GutenbergKitResourcesSPM target wrapping bundled editor assets (HTML, CSS, JS)Package.swiftusesContext.environmentto switch between local source and CDN binary targetbuild_xcframework.shassembles a proper.frameworkfrom SPM archive DerivedData (binary, swiftmodule, modulemap, headers, resource bundles) since SPM archives don't produce installable frameworksreleaselane handles versioning, GitHub release, and S3 uploadGutenbergKittarget depends onGutenbergKitResourcesinstead of bundling assets directlypackageaccess modifiers migrated tointernal(required bypackageAccess: false)Testing Instructions
make build— builds web assets and copies to all platform directoriesmake build-swift-package— builds the Swift package with local resourcesmake build-resources-xcframework— producesGutenbergKitResources-<sha>.xcframework.zipios-arm64andios-arm64_x86_64-simulatorslices with the binary, Modules, Headers, and resource bundleGenerated with the help of Claude Opus 4.6