Stagger build numbers by platform: iOS/watch even, macOS odd
Both platforms share one App Store Connect build-number space (same bundle ID, universal purchase), so the raw commit count would collide. The vendored script partitions on PLATFORM_NAME; watchOS rides with iOS because the embedded watch app's CFBundleVersion must match its host.
This commit is contained in:
@@ -58,7 +58,7 @@ All in `Shared/Model/`:
|
|||||||
|
|
||||||
### Platforms, Tooling & Entitlements
|
### Platforms, Tooling & Entitlements
|
||||||
- iOS 26 / watchOS 26 / macOS 26; Swift 6 with `SWIFT_STRICT_CONCURRENCY: complete` on all targets.
|
- iOS 26 / watchOS 26 / macOS 26; Swift 6 with `SWIFT_STRICT_CONCURRENCY: complete` on all targets.
|
||||||
- Build number (git commit count), `BuildDate`, and `BuildHash` are stamped by the `app-versioning` skill's `update_build_info.sh` (referenced in place from `../indie-skills`, a post-build phase; release tagging is opt-in via `RELEASE_TAGGING=1`). `Scripts/` holds the App Store / TestFlight release pipeline.
|
- Build number, `BuildDate`, and `BuildHash` are stamped by the vendored `Scripts/update_build_info.sh` (a post-build phase; release tagging is opt-in via `RELEASE_TAGGING=1`). Build numbers are **platform-partitioned** from the git commit count — iOS/watchOS get even (2×commits), macOS gets odd (2×commits+1) — because both platforms share one ASC build-number space and the embedded watch app must match the iOS app's `CFBundleVersion`. `Scripts/` also holds the App Store / TestFlight release pipeline.
|
||||||
- Entitlements: iOS uses CloudDocuments + HealthKit; watch uses HealthKit only; macOS uses app sandbox + CloudDocuments (same bundle ID as iOS — universal purchase). **No CloudKit service, no App Group.**
|
- Entitlements: iOS uses CloudDocuments + HealthKit; watch uses HealthKit only; macOS uses app sandbox + CloudDocuments (same bundle ID as iOS — universal purchase). **No CloudKit service, no App Group.**
|
||||||
- SPM packages: `IndieSync` (file-side iCloud sync core) and `IndieAbout` (in-app About / changelog / license UI).
|
- SPM packages: `IndieSync` (file-side iCloud sync core) and `IndieAbout` (in-app About / changelog / license UI).
|
||||||
|
|
||||||
|
|||||||
Executable
+92
@@ -0,0 +1,92 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
## app-versioning: platform-partitioned Build Info Script (vendored for Workouts)
|
||||||
|
##
|
||||||
|
## A vendored variant of the shared app-versioning script (see
|
||||||
|
## ../indie-skills/skills/app-versioning/scripts/update_build_info.sh). Workouts'
|
||||||
|
## iOS app and Mac app share ONE App Store Connect record (same bundle id,
|
||||||
|
## dev.rzen.indie.Workouts — universal purchase), and therefore one build-number
|
||||||
|
## sequence: stamping both with the raw commit count would make the second
|
||||||
|
## upload collide ("bundle version already used"). Partition the space instead:
|
||||||
|
## - iOS + watchOS -> EVEN build numbers (commit-count * 2)
|
||||||
|
## - macOS -> ODD build numbers (commit-count * 2 + 1)
|
||||||
|
## watchOS is grouped with iOS (not given its own parity) because the embedded
|
||||||
|
## watch app's CFBundleVersion must match its host iOS app's exactly. The
|
||||||
|
## platform is read from PLATFORM_NAME, so this one script serves every
|
||||||
|
## code-bearing target (iOS app, watch app, watch widget, iOS widget, Mac app)
|
||||||
|
## via a single postBuildScripts anchor — no per-target parameters needed.
|
||||||
|
##
|
||||||
|
## Sets, on every build, in both the target Info.plist and its dSYM:
|
||||||
|
## - CFBundleVersion = commit-count*2 (iOS/watchOS) or commit-count*2+1 (macOS)
|
||||||
|
## - BuildDate = today's date (for IndieAbout's "Built …" line)
|
||||||
|
## - BuildHash = short git commit hash
|
||||||
|
##
|
||||||
|
## Wire as a POST-BUILD Run Script phase with these Input Files:
|
||||||
|
## $(TARGET_BUILD_DIR)/$(INFOPLIST_PATH)
|
||||||
|
## $(DWARF_DSYM_FOLDER_PATH)/$(DWARF_DSYM_FILE_NAME)/Contents/Info.plist
|
||||||
|
##
|
||||||
|
## Release tagging is opt-in, not automatic. Set RELEASE_TAGGING=1 in the
|
||||||
|
## build environment (e.g. `RELEASE_TAGGING=1 xcodebuild ...`) to have this
|
||||||
|
## script run `git tag <marketing-version>`. Default builds never tag.
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
git=$(sh /etc/profile; which git || true)
|
||||||
|
if [ -z "$git" ] || [ ! -x "$git" ]; then
|
||||||
|
echo "error: update_build_info.sh: could not resolve a 'git' executable — refusing to stamp a build number." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
number_of_commits=$("$git" rev-list HEAD --count)
|
||||||
|
case "$number_of_commits" in
|
||||||
|
''|*[!0-9]*)
|
||||||
|
echo "error: update_build_info.sh: 'git rev-list HEAD --count' returned '$number_of_commits', not a positive integer — refusing to stamp a build number." >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
if [ "$number_of_commits" -le 0 ]; then
|
||||||
|
echo "error: update_build_info.sh: 'git rev-list HEAD --count' returned $number_of_commits — refusing to stamp a build number." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
git_commit=$("$git" rev-parse --short HEAD)
|
||||||
|
|
||||||
|
# Platform-aware build number (see header): iOS/watchOS even, macOS odd.
|
||||||
|
case "${PLATFORM_NAME:-}" in
|
||||||
|
macosx*) build_number=$((number_of_commits * 2 + 1)) ;;
|
||||||
|
*) build_number=$((number_of_commits * 2)) ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
target_plist="$TARGET_BUILD_DIR/$INFOPLIST_PATH"
|
||||||
|
dsym_plist="$DWARF_DSYM_FOLDER_PATH/$DWARF_DSYM_FILE_NAME/Contents/Info.plist"
|
||||||
|
|
||||||
|
bundle_version=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "$target_plist" 2>/dev/null || true)
|
||||||
|
build_date=$(date +%F)
|
||||||
|
|
||||||
|
echo "app-versioning: v$bundle_version-$git_commit b$build_number (${PLATFORM_NAME:-}) $build_date"
|
||||||
|
|
||||||
|
if [ "${RELEASE_TAGGING:-}" = "1" ]; then
|
||||||
|
if [ -n "$bundle_version" ]; then
|
||||||
|
"$git" tag "$bundle_version"
|
||||||
|
echo "app-versioning: tagged release $bundle_version"
|
||||||
|
else
|
||||||
|
echo "warning: app-versioning: RELEASE_TAGGING=1 but CFBundleShortVersionString could not be read from $target_plist; skipping git tag"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$target_plist" ] && [ ! -f "$dsym_plist" ]; then
|
||||||
|
echo "warning: update_build_info.sh: neither the target Info.plist ($target_plist) nor the dSYM Info.plist ($dsym_plist) exists — check that both are wired as Input Files on this Run Script build phase"
|
||||||
|
fi
|
||||||
|
|
||||||
|
for plist in "$target_plist" "$dsym_plist"; do
|
||||||
|
if [ -f "$plist" ]; then
|
||||||
|
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $build_number" "$plist"
|
||||||
|
|
||||||
|
/usr/libexec/PlistBuddy -c "Set :BuildDate $build_date" "$plist" 2>/dev/null || \
|
||||||
|
/usr/libexec/PlistBuddy -c "Add :BuildDate string $build_date" "$plist"
|
||||||
|
|
||||||
|
/usr/libexec/PlistBuddy -c "Set :BuildHash $git_commit" "$plist" 2>/dev/null || \
|
||||||
|
/usr/libexec/PlistBuddy -c "Add :BuildHash string $git_commit" "$plist"
|
||||||
|
|
||||||
|
echo "app-versioning: updated $plist"
|
||||||
|
fi
|
||||||
|
done
|
||||||
+11
-4
@@ -28,12 +28,19 @@ packages:
|
|||||||
url: https://git.rzen.dev/rzen/indie-backup.git
|
url: https://git.rzen.dev/rzen/indie-backup.git
|
||||||
from: "2.0.0"
|
from: "2.0.0"
|
||||||
|
|
||||||
# Shared post-build phase — stamps CFBundleVersion (git commit count), BuildDate, and
|
# Shared post-build phase — stamps CFBundleVersion, BuildDate, and BuildHash into each
|
||||||
# BuildHash into each target's (and dSYM's) Info.plist. Aliased into every code-bearing
|
# target's (and dSYM's) Info.plist. Vendored at Scripts/update_build_info.sh (not the
|
||||||
# target below so the app, watch app, and both widget extensions stamp identical metadata.
|
# shared indie-skills copy) because the iOS app and Workouts Mac share ONE App Store
|
||||||
|
# Connect record (dev.rzen.indie.Workouts, universal purchase), so both stamping the
|
||||||
|
# raw commit count would collide on upload. The script partitions the space instead:
|
||||||
|
# iOS + watchOS targets get an EVEN CFBundleVersion (commit-count*2), the Mac target
|
||||||
|
# gets ODD (commit-count*2+1) — derived from PLATFORM_NAME inside the script, so this
|
||||||
|
# one anchor still serves all five code-bearing targets unmodified. watchOS is grouped
|
||||||
|
# with iOS (not given its own parity) because the embedded watch app's CFBundleVersion
|
||||||
|
# must match its host iOS app's exactly.
|
||||||
scriptAnchors:
|
scriptAnchors:
|
||||||
updateBuildInfo: &updateBuildInfo
|
updateBuildInfo: &updateBuildInfo
|
||||||
- script: '"${SRCROOT}/../indie-skills/skills/app-versioning/scripts/update_build_info.sh"'
|
- script: '"${SRCROOT}/Scripts/update_build_info.sh"'
|
||||||
name: Update Build Info
|
name: Update Build Info
|
||||||
shell: /bin/sh
|
shell: /bin/sh
|
||||||
basedOnDependencyAnalysis: false
|
basedOnDependencyAnalysis: false
|
||||||
|
|||||||
Reference in New Issue
Block a user