feat(release): unify cross-platform versioning

This commit is contained in:
2026-07-28 08:22:58 +02:00
parent fc1d27bf45
commit 407a0d2d60
35 changed files with 500 additions and 220 deletions

View File

@@ -52,7 +52,7 @@ The macOS app ships through two targets that build identical sources:
```bash
make build-apple-macos-direct # unsigned compile-check of the direct target
make build-apple-dmg VERSION=x.y.z # signed (+ notarized) .dmg
make build-apple-dmg # signed (+ notarized) .dmg
```
Full signing, notarization, appcast, and cask flow: see

View File

@@ -1,3 +1,6 @@
// Product and build versions are shared by every platform.
#include "../version.properties"
// VniDrop development and CI builds are intentionally unsigned.
CODE_SIGNING_ALLOWED = NO
CODE_SIGNING_REQUIRED = NO

View File

@@ -6,7 +6,7 @@ import UIKit
@MainActor
func makeAppDependencies(externalInvitations: ExternalInvitationController) -> AppDependencies {
let device = UIDevice.current
let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "0.1.0"
let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "unknown"
let env = PlatformEnvironment(
name: "\(device.systemName) \(device.systemVersion)",
appVersion: version,

View File

@@ -5,7 +5,7 @@ import AppKit
/// Builds the macOS dependency graph, mirroring `rememberIosAppDependencies`.
@MainActor
func makeAppDependencies(externalInvitations: ExternalInvitationController) -> AppDependencies {
let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "0.1.0"
let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "unknown"
let host = Host.current().localizedName ?? "Mac"
let env = PlatformEnvironment(
name: "macOS " + ProcessInfo.processInfo.operatingSystemVersionString,

View File

@@ -65,11 +65,8 @@ targetTemplates:
base:
PRODUCT_NAME: VniDrop
PRODUCT_BUNDLE_IDENTIFIER: com.vnidrop.app
MARKETING_VERSION: "0.1.0"
# Placeholder only — the real CFBundleVersion is stamped at build time as a
# UTC YYMMDD.HHMM timestamp by the "Stamp build number" phase below, so every
# build is monotonic and self-describing (shown as "MARKETING_VERSION (build)").
CURRENT_PROJECT_VERSION: "1"
MARKETING_VERSION: "$(PRODUCT_VERSION)"
CURRENT_PROJECT_VERSION: "$(APPLE_BUILD_NUMBER)"
GENERATE_INFOPLIST_FILE: NO
INFOPLIST_FILE: VniDrop/Resources/Info.plist
CODE_SIGN_ENTITLEMENTS: VniDrop/Resources/VniDrop.entitlements
@@ -111,23 +108,6 @@ targetTemplates:
echo "error: SwiftLint not installed — run 'brew install swiftlint'"
exit 1
fi
postBuildScripts:
# Stamp CFBundleVersion as a UTC YYMMDD.HHMM timestamp into the built
# Info.plist before code signing. Runs for every build (Xcode GUI archive and
# CLI alike), so both the App Store and direct-download channels get a
# monotonic, meaningful build id. CI/reproducible builds can pin it via the
# VNIDROP_BUILD env var. MARKETING_VERSION stays the human X.Y.Z version.
- name: Stamp build number (UTC timestamp)
basedOnDependencyAnalysis: false
script: |
build="${VNIDROP_BUILD:-$(date -u +%y%m%d.%H%M)}"
plist="${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"
if [ -f "$plist" ]; then
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $build" "$plist"
echo "Stamped CFBundleVersion = $build"
else
echo "warning: Info.plist not found at $plist; CFBundleVersion not stamped"
fi
targets:
# App Store / TestFlight target. iOS + macOS, sandboxed, no self-updater.

View File

@@ -7,8 +7,7 @@
# This is the direct-distribution counterpart to the App Store archive flow; it
# never touches the App Store `VniDrop` target. The Rust crate is not modified.
#
# Usage: apple/scripts/build-dmg.sh [version]
# version MAJOR.MINOR.PATCH; defaults to MARKETING_VERSION / the git tag.
# Usage: apple/scripts/build-dmg.sh
#
# Environment:
# DEVELOPER_ID_APP Codesign identity, e.g. "Developer ID Application: … (TEAMID)".
@@ -29,29 +28,11 @@ PROJECT="$APPLE_DIR/VniDrop.xcodeproj"
SCHEME="VniDropDirect"
CONFIG="Release-Direct"
APP_NAME="VniDrop"
VERSION_RESOLVER="$REPO_ROOT/packaging/version/resolve-version.sh"
# --- Resolve version (arg > git tag > project MARKETING_VERSION) -------------
resolve_version() {
local v="${1:-}"
if [ -z "$v" ] && [ "${GITHUB_REF_TYPE:-}" = "tag" ]; then
v="${GITHUB_REF_NAME#v}"
fi
if [ -z "$v" ]; then
v="$(sed -nE 's/.*MARKETING_VERSION: "([0-9.]+)".*/\1/p' "$APPLE_DIR/project.yml" | head -1)"
fi
if [[ ! "$v" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "version must be MAJOR.MINOR.PATCH (got '$v')" >&2
exit 1
fi
printf '%s' "$v"
}
VERSION="$(resolve_version "${1:-}")"
# CFBundleVersion is a UTC YYMMDD.HHMM timestamp stamped by the target's
# "Stamp build number" build phase. Pin it here (one value for the whole archive)
# so the app, DMG, and appcast all agree; Sparkle compares it to order updates.
BUILD_NUMBER="$(date -u +%y%m%d.%H%M)"
export VNIDROP_BUILD="$BUILD_NUMBER"
VERSION="$("$VERSION_RESOLVER" product)"
BUILD_NUMBER="$("$VERSION_RESOLVER" apple-build)"
"$VERSION_RESOLVER" verify >/dev/null
# --- Resolve signing identity ------------------------------------------------
if [ -z "${DEVELOPER_ID_APP:-}" ]; then

View File

@@ -9,7 +9,7 @@
# keychain). The resulting appcast.xml is uploaded as a release asset; the app's
# SUFeedURL (/releases/latest/download/appcast.xml) always resolves to the newest.
#
# Usage: apple/scripts/generate-appcast.sh [version]
# Usage: apple/scripts/generate-appcast.sh
#
# Environment:
# DIST_DIR Folder holding the DMG(s). Default: apple/dist
@@ -24,12 +24,10 @@ REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
APPLE_DIR="$REPO_ROOT/apple"
DIST_DIR="${DIST_DIR:-$APPLE_DIR/dist}"
RELEASE_REPO="${RELEASE_REPO:-sudosylabs/vnidrop}"
VERSION_RESOLVER="$REPO_ROOT/packaging/version/resolve-version.sh"
VERSION="${1:-}"
if [ -z "$VERSION" ] && [ "${GITHUB_REF_TYPE:-}" = "tag" ]; then
VERSION="${GITHUB_REF_NAME#v}"
fi
[ -n "$VERSION" ] || { echo "error: version required (arg or tag)" >&2; exit 1; }
VERSION="$("$VERSION_RESOLVER" product)"
"$VERSION_RESOLVER" verify >/dev/null
# Enclosure URLs resolve to the specific release's assets.
DOWNLOAD_PREFIX="https://github.com/$RELEASE_REPO/releases/download/v$VERSION"