Skip to content

fix: stamp required version keys into GutenbergKitResources framework Info.plist#551

Open
jkmassel wants to merge 2 commits into
trunkfrom
jkmassel/detect-plist-package-json-drift
Open

fix: stamp required version keys into GutenbergKitResources framework Info.plist#551
jkmassel wants to merge 2 commits into
trunkfrom
jkmassel/detect-plist-package-json-drift

Conversation

@jkmassel

Copy link
Copy Markdown
Contributor

What?

GutenbergKitResources.framework's Info.plist was missing CFBundleShortVersionString and MinimumOSVersion, which App Store Connect requires on every embedded framework. This adds those keys (plus CFBundleVersion), sources every value from a single manifest, and adds a post-build check so the plist can't silently drift again.

Why?

build_xcframework.sh hand-writes the framework Info.plist with only four keys — CFBundleExecutable, CFBundleIdentifier, CFBundleName, CFBundlePackageType. codesign (fastlane xcframework_sign) doesn't validate version keys, so the deficient framework passes signing, zips, and publishes to the CDN unnoticed. altool is the first tool in the chain that validates embedded-framework plists — so the failure only surfaces at a consuming app's TestFlight upload, three hops downstream:

  • missing CFBundleShortVersionString → altool 90057
  • missing MinimumOSVersion → altool 90360

This blocked a wordpress-ios TestFlight upload: build 33156.

Note: this fix is forward-only — the XCFramework already on the CDN still carries the bad plist, so it won't unblock a consumer on its own. It needs a new GutenbergKit release carrying this change, then a consumer bump to pick it up.

How?

1. Stamp the required keys

The heredoc now also emits CFBundleShortVersionString, CFBundleVersion, MinimumOSVersion, and CFBundleInfoDictionaryVersion. CFBundleVersion wasn't in altool's error list, but a framework carrying CFBundleShortVersionString without it is malformed by convention — cheaper to include now than to eat a second upload round-trip.

2. Single source of truth, fail-closed

  • CFBundleShortVersionString / CFBundleVersionpackage.json version
  • MinimumOSVersionPackage.swift's declared iOS platform, via swift package dump-package | jq (SwiftPM normalizes .v17"17.0", the exact format the key wants)

Both abort the build if the value can't be read, rather than stamping a silently-wrong placeholder — a wrong-but-present version is exactly the drift we want to prevent. MINIMUM_IOS_VERSION is no longer hardcoded, so the dylib link target (-target arch-apple-ios${MINIMUM_IOS_VERSION}) tracks Package.swift too.

3. Verify before the artifact leaves the step

After create-xcframework, verify_framework_plist runs against every shipped slice and exit 1s unless: plutil -lint passes, CFBundleShortVersionString matches package.json, CFBundleVersion is present, and MinimumOSVersion matches. A dropped or stale key now reddens GutenbergKit's own build-xcframework job instead of a downstream App Store upload.

Testing Instructions

  1. make build-resources-xcframework (or ./build_xcframework.sh).
  2. Confirm the new --- Verifying framework Info.plist step prints OK: for each slice and the script exits 0.
  3. Inspect a slice's plist and confirm the keys resolve:
    plutil -p build/GutenbergKitResources-*.xcframework/ios-*/GutenbergKitResources.framework/Info.plist
    
    Expect CFBundleShortVersionString = the package.json version and MinimumOSVersion = 17.0.
  4. Negative check: temporarily edit package.json's version, rebuild, and confirm the verification step fails the build with a CFBundleShortVersionString ... does not match error.

Accessibility Testing Instructions

N/A — build tooling only, no UI change.

@github-actions github-actions Bot added the [Type] Bug An existing feature does not function as intended label Jul 15, 2026
@wpmobilebot

wpmobilebot commented Jul 15, 2026

Copy link
Copy Markdown

XCFramework Build

This PR's XCFramework is available for testing. Add the following to your Package.swift:

.package(url: "https://github.com/wordpress-mobile/GutenbergKit", branch: "pr-build/551")

Built from 352520d

@jkmassel jkmassel added [Type] Build Tooling Issues or PRs related to build tooling iOS and removed [Type] Bug An existing feature does not function as intended labels Jul 15, 2026
… Info.plist

build_xcframework.sh hand-wrote the framework Info.plist with only four keys, omitting CFBundleShortVersionString and MinimumOSVersion. App Store Connect requires both on any embedded framework, so consuming apps failed to upload to TestFlight (altool 90057 / 90360) even though GutenbergKit's own CI was green: codesign doesn't validate version keys, so the deficient framework sailed through signing and publishing.

Stamp the version keys from package.json and MinimumOSVersion from Package.swift (swift package dump-package), both fail-closed, and verify every shipped slice's plist before it leaves the build step so this can't silently recur.
@jkmassel jkmassel force-pushed the jkmassel/detect-plist-package-json-drift branch from 26a4c2b to 4783614 Compare July 15, 2026 04:07
@jkmassel jkmassel marked this pull request as ready for review July 15, 2026 04:37
@jkmassel jkmassel requested a review from crazytonyli July 15, 2026 04:37
Comment thread build_xcframework.sh Outdated
# The `|| ''` collapses a missing/empty/undefined `version` to the empty
# string so the guard below catches it — `node -p` would otherwise print the
# literal "undefined" and stamp it as the version, the exact drift we prevent.
MARKETING_VERSION="$(node -p "require('./package.json').version || ''" 2>/dev/null || true)"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use jq instead of node?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah, nice. Done in 352520d

The MINIMUM_IOS_VERSION lookup already requires jq (swift package dump-package | jq), so reading MARKETING_VERSION with jq too drops the node dependency from this script and keeps both version reads consistent.

`.version // empty` preserves the fail-closed behavior: a missing, null, or empty version yields empty output so the guard fires, rather than stamping a literal "null" (the jq analogue of the node "undefined" case the previous comment guarded against). Addresses review feedback on #551.
@jkmassel jkmassel requested a review from crazytonyli July 15, 2026 17:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

iOS [Type] Build Tooling Issues or PRs related to build tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants