fix(release): resolve release version from build settings, not Info.plist - #20
Merged
Conversation
…fo.plist
`notarize-and-package.sh` derived APP_VERSION by reading
CFBundleShortVersionString directly out of App/Resources/Info.plist. That
file stores the *unexpanded* build variable `$(MARKETING_VERSION)` — Xcode
substitutes it at build time, so PlistBuddy returns the literal seven-
character string, not a version.
Consequences of the old behaviour, all of which only surface after a full
archive + notarization cycle has already run:
- artifacts named `InterlinedList-$(MARKETING_VERSION).pkg` / `.dmg`
- `pkgbuild --version` and `productbuild --version` given that same
literal, producing an installer with a nonsense version
- the appcast enclosure filename could never match the built artifact
Resolve MARKETING_VERSION through `xcodebuild -showBuildSettings` on the
Release configuration instead, and add a guard that hard-fails with an
actionable message when the resolved value is empty or still contains
`$(` — cheap up front, versus discovering it after notarization. An
explicit APP_VERSION override still wins, unchanged.
The header docs for APP_VERSION are corrected to match: Info.plist is no
longer read for the version.
Verification (no Swift sources touched, so the usual BDD quartet does not
apply; the script has no unit-test harness in this repo, so the three
branches were exercised directly against the real project paths):
- default resolution → `InterlinedList-0.1.0 (pkg version: 0.1.0)`
- explicit APP_VERSION → `InterlinedList-1.2.3-alpha`, honoured
- unexpanded variable → guard message, exit 1
- `bash -n` clean on all 8 scripts/*.sh
E2E gate re-run green: build SUCCEEDED, App 622/622, Kit 314, Domain 619,
Persistence 135, Decision-0003 import guard 0 hits.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013tAJgfBGnnA1HgRGAWvkRK
…udit Three corrections to work-consolidation.md, all verified against code rather than assumed: 1. **G14's `ImagePrep` tail was already done** — the doc still listed it as the last open §1 follow-up. In fact the media size ceilings are server-driven end to end: `ImagePrep.Limits` + `prepare(_:limits:)`, `ContentLimits.imagePrepLimits`, both `uploadImage` call sites, and `uploadVideo`'s live `videoMaxBytes`, with `AppEnvironment` injecting `ContentLimitsService` into both services. §1 is now genuinely closed, so the §1 callout and the "where we are now" paragraph are updated to stop pointing at a finished item. 2. **Test baseline refreshed** to the counts observed 2026-09-05: Kit 314 / Domain 619 / Persistence 135 / App 622. Kit and Domain dipped from 317/628 because PR #19 deleted the List Folders tests with the feature — noted inline so the drop doesn't read as regression. 3. **§3a pre-flight audit recorded** — what was verified sound (script syntax, export options, sync-agent embed paths, Sparkle wiring, appcast minimumSystemVersion matching the 15.0 deployment target), the one blocking version-resolution bug that was found and fixed, and the two decisions left for the owner: the version number disagrees across MARKETING_VERSION (0.1.0), the appcast (0.0.1-alpha) and the tag step (v1.0.0); and SUFeedURL's path disagrees with the appcast's own comment block. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013tAJgfBGnnA1HgRGAWvkRK
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes a blocking bug in the Developer ID release pipeline that would have corrupted the first credentialed run, and syncs
work-consolidation.mdwith the code as it actually stands.scripts/notarize-and-package.shderived the release version by readingCFBundleShortVersionStringstraight out ofApp/Resources/Info.plist— but that file stores the unexpanded build variable$(MARKETING_VERSION), which Xcode substitutes only at build time. PlistBuddy therefore returned the literal string. Every artifact would have been namedInterlinedList-$(MARKETING_VERSION).pkg/.dmg, andpkgbuild --version/productbuild --versionwould have received the same literal — producing an installer with a nonsense version whose filename could never match the appcast enclosure. The failure only surfaces after a full archive + notarization cycle has already run, which is what makes it worth catching before the certs are in play.Changes
scripts/notarize-and-package.sh— resolveMARKETING_VERSIONviaxcodebuild -showBuildSettingson the Release configuration instead of reading Info.plist. Added a guard that hard-fails with an actionable message when the resolved value is empty or still contains$(, so a bad version can never reachpkgbuild. An explicitAPP_VERSIONoverride still wins, unchanged. Header docs corrected to match.work-consolidation.md— three corrections, each verified against code rather than assumed:ImagePreptail was already done and is no longer an open §1 item. The media size ceilings are server-driven end to end (ImagePrep.Limits+prepare(_:limits:),ContentLimits.imagePrepLimits, bothuploadImagecall sites, anduploadVideo's livevideoMaxBytes, withAppEnvironmentinjectingContentLimitsService). §1 is now genuinely closed.Pre-flight audit — verified sound
Reviewed before the first credentialed run, since every failure mode in this pipeline is expensive to discover late:
scripts/*.shparse (bash -n)ExportOptions.plistdeveloper-id+ automatic signingSparkleController+UpdatesMenuCommandswired,SUFeedURLsetminimumSystemVersion(15.0) vsMACOSX_DEPLOYMENT_TARGET(15.0)Two decisions left for the owner (not code bugs, nothing changed here)
MARKETING_VERSIONis0.1.0,releases/appcast.xmladvertises0.0.1(InterlinedList-0.0.1-alpha.pkg), and §3a's tag step saysv1.0.0. The appcast enclosure filename must match the artifact the script emits or Sparkle 404s on every update check. Pick one before cutting a release.SUFeedURLpoints athttps://interlinedlist.com/appcast.xml, while the comment block insidereleases/appcast.xmldocuments the feed at…/downloads/apple/appcast.xml. Harmless today; reconcile so the served path and the polled path can't drift.Verification
Full E2E gate, all legs observed green on this branch:
xcodebuild -scheme InterlinedList -destination 'platform=macOS' build→** BUILD SUCCEEDED **CODE_SIGNING_ALLOWED=NO) →Executed 622 tests, with 0 failures (0 unexpected)→** TEST SUCCEEDED **swift testInterlinedKit →Executed 314 tests, with 0 failures (0 unexpected)swift testInterlinedDomain →Executed 619 tests, with 0 failures (0 unexpected)swift testInterlinedPersistence →Executed 135 tests, with 0 failures (0 unexpected)grep -rn "^\s*import InterlinedKit" App/Features App/Navigation App/MenuCommands→ 0 hitsThe script change touches no Swift sources, so the BDD quartet does not apply and the repo has no unit-test harness for shell. The three branches were exercised directly against the real project paths instead:
APP_VERSIONunset)==> Release: InterlinedList-0.1.0 (pkg version: 0.1.0)APP_VERSION=1.2.3 RELEASE_LABEL=alpha==> Release: InterlinedList-1.2.3-alpha, honouredAPP_VERSION='$(MARKETING_VERSION)'🤖 Generated with Claude Code
https://claude.ai/code/session_013tAJgfBGnnA1HgRGAWvkRK