Skip to content

fix(release): resolve release version from build settings, not Info.plist - #20

Merged
Adron merged 2 commits into
devfrom
fix/release-script-version-resolution
Sep 6, 2026
Merged

fix(release): resolve release version from build settings, not Info.plist#20
Adron merged 2 commits into
devfrom
fix/release-script-version-resolution

Conversation

@Adron

@Adron Adron commented Sep 6, 2026

Copy link
Copy Markdown
Member

Summary

Fixes a blocking bug in the Developer ID release pipeline that would have corrupted the first credentialed run, and syncs work-consolidation.md with the code as it actually stands.

scripts/notarize-and-package.sh derived the release version by reading CFBundleShortVersionString straight out of App/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 named InterlinedList-$(MARKETING_VERSION).pkg / .dmg, and pkgbuild --version / productbuild --version would 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 — resolve MARKETING_VERSION via xcodebuild -showBuildSettings on 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 reach pkgbuild. An explicit APP_VERSION override still wins, unchanged. Header docs corrected to match.
  • work-consolidation.md — three corrections, each verified against code rather than assumed:
    • G14's ImagePrep tail 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, both uploadImage call sites, and uploadVideo's live videoMaxBytes, with AppEnvironment injecting ContentLimitsService). §1 is now genuinely closed.
    • Test baseline refreshed to the counts observed 2026-09-05. Kit and Domain dip from 317/628 to 314/619 because PR refactor(lists): remove the List Folders feature, keep parent/child list nesting #19 deleted the List Folders tests along with the feature — noted inline so the drop doesn't read as a regression.
    • §3a pre-flight audit recorded — what was verified sound, the bug above, and two decisions left open for the owner.

Pre-flight audit — verified sound

Reviewed before the first credentialed run, since every failure mode in this pipeline is expensive to discover late:

Check Result
All 8 scripts/*.sh parse (bash -n) clean
ExportOptions.plist developer-id + automatic signing
Sync-agent embed paths + both entitlements files present
Sparkle code path SparkleController + UpdatesMenuCommands wired, SUFeedURL set
Appcast minimumSystemVersion (15.0) vs MACOSX_DEPLOYMENT_TARGET (15.0) match

Two decisions left for the owner (not code bugs, nothing changed here)

  1. The version number disagrees in three placesMARKETING_VERSION is 0.1.0, releases/appcast.xml advertises 0.0.1 (InterlinedList-0.0.1-alpha.pkg), and §3a's tag step says v1.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.
  2. Appcast URLSUFeedURL points at https://interlinedlist.com/appcast.xml, while the comment block inside releases/appcast.xml documents 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 **
  • App target test (CODE_SIGNING_ALLOWED=NO) → Executed 622 tests, with 0 failures (0 unexpected)** TEST SUCCEEDED **
  • swift test InterlinedKit → Executed 314 tests, with 0 failures (0 unexpected)
  • swift test InterlinedDomain → Executed 619 tests, with 0 failures (0 unexpected)
  • swift test InterlinedPersistence → Executed 135 tests, with 0 failures (0 unexpected)
  • Decision 0003 — grep -rn "^\s*import InterlinedKit" App/Features App/Navigation App/MenuCommands0 hits

The 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:

Branch Result
Default resolution (APP_VERSION unset) ==> Release: InterlinedList-0.1.0 (pkg version: 0.1.0)
Explicit APP_VERSION=1.2.3 RELEASE_LABEL=alpha ==> Release: InterlinedList-1.2.3-alpha, honoured
Unexpanded variable APP_VERSION='$(MARKETING_VERSION)' guard message, exit 1

🤖 Generated with Claude Code

https://claude.ai/code/session_013tAJgfBGnnA1HgRGAWvkRK

Adron and others added 2 commits September 5, 2026 22:03
…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
@Adron
Adron merged commit 66a9f99 into dev Sep 6, 2026
8 checks passed
@Adron
Adron deleted the fix/release-script-version-resolution branch September 6, 2026 05:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant