diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2a8d250..5653b21 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,6 +16,8 @@ jobs: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} + env: + RELEASE_TAG: ${{ github.ref_name }} steps: - name: Checkout @@ -52,54 +54,32 @@ jobs: SPARKLE_PUBLIC_KEY = $SPARKLE_PUBLIC_KEY EOF - - name: Validate and derive release metadata + - name: Validate release version run: | - TAG="$GITHUB_REF_NAME" - - if [[ ! "$TAG" =~ ^v([0-9]+\.[0-9]+\.[0-9]+)(-([0-9A-Za-z]+([.-][0-9A-Za-z]+)*))?$ ]]; then - echo "Tag must be in vX.Y.Z or vX.Y.Z-suffix format, received: $TAG" - exit 1 - fi - - MARKETING_VERSION="${BASH_REMATCH[1]}" - RELEASE_VERSION="${TAG#v}" - IS_PRERELEASE=false - if [[ "$TAG" != "v$MARKETING_VERSION" ]]; then - IS_PRERELEASE=true - fi - - PROJECT_VERSIONS="$( + BUILD_SETTINGS="$( xcodebuild \ -project BitDream.xcodeproj \ -scheme BitDream \ -configuration Release \ -destination "generic/platform=macOS" \ - -showBuildSettings | - awk '$1 == "MARKETING_VERSION" && $2 == "=" { print $3 }' | - sort -u + -showBuildSettings )" - VERSION_COUNT="$(printf '%s\n' "$PROJECT_VERSIONS" | awk 'NF { count++ } END { print count + 0 }')" + MARKETING_VERSIONS="$(printf '%s\n' "$BUILD_SETTINGS" | awk '$1 == "MARKETING_VERSION" && $2 == "=" { print $3 }' | sort -u)" + VERSION_COUNT="$(printf '%s\n' "$MARKETING_VERSIONS" | awk 'NF { count++ } END { print count + 0 }')" if [[ "$VERSION_COUNT" -ne 1 ]]; then - echo "Expected exactly one project MARKETING_VERSION, but found $VERSION_COUNT." - printf '%s\n' "$PROJECT_VERSIONS" + echo "Expected exactly one MARKETING_VERSION, but found $VERSION_COUNT." + printf '%s\n' "$MARKETING_VERSIONS" exit 1 fi - if [[ "$MARKETING_VERSION" != "$PROJECT_VERSIONS" ]]; then - echo "Release tag $TAG does not match project MARKETING_VERSION $PROJECT_VERSIONS." - echo "Expected v$PROJECT_VERSIONS or v$PROJECT_VERSIONS-suffix." + EXPECTED_TAG="v$MARKETING_VERSIONS" + if [[ "$RELEASE_TAG" != "$EXPECTED_TAG" ]]; then + echo "Release tag $RELEASE_TAG does not match MARKETING_VERSION $MARKETING_VERSIONS." + echo "Expected tag: $EXPECTED_TAG" exit 1 fi - BUILD_NUMBER="$GITHUB_RUN_NUMBER" - - echo "RELEASE_TAG=$TAG" >> "$GITHUB_ENV" - echo "RELEASE_VERSION=$RELEASE_VERSION" >> "$GITHUB_ENV" - echo "MARKETING_VERSION=$MARKETING_VERSION" >> "$GITHUB_ENV" - echo "BUILD_NUMBER=$BUILD_NUMBER" >> "$GITHUB_ENV" - echo "IS_PRERELEASE=$IS_PRERELEASE" >> "$GITHUB_ENV" - - name: Import Apple signing certificate env: P12_BASE64: ${{ secrets.APPLE_CERT_P12_BASE64 }} @@ -182,6 +162,9 @@ jobs: - name: Build macOS app archive run: | + # Sparkle compares CFBundleVersion, so released builds stamp the + # validated release version there. The project keeps a local build + # number for development builds. xcodebuild archive \ -project BitDream.xcodeproj \ -scheme BitDream \ @@ -192,8 +175,7 @@ jobs: CODE_SIGN_IDENTITY="Developer ID Application" \ DEVELOPMENT_TEAM="${{ secrets.APPLE_TEAM_ID }}" \ OTHER_CODE_SIGN_FLAGS="--keychain $KEYCHAIN_PATH" \ - MARKETING_VERSION="$MARKETING_VERSION" \ - CURRENT_PROJECT_VERSION="$BUILD_NUMBER" \ + CURRENT_PROJECT_VERSION="${RELEASE_TAG#v}" \ SPARKLE_APPCAST_URL="$SPARKLE_APPCAST_URL" \ SPARKLE_PUBLIC_KEY="$SPARKLE_PUBLIC_KEY" \ ARCHS=arm64 @@ -308,7 +290,7 @@ jobs: rm -f "$DMG_PATH" create-dmg \ - --volname "BitDream ${MARKETING_VERSION}" \ + --volname "BitDream ${RELEASE_TAG#v}" \ --volicon "$VOLUME_ICON_PATH" \ --window-size 660 420 \ --background "$DMG_BACKGROUND_PATH" \ @@ -397,6 +379,12 @@ jobs: codesign --verify --verbose=2 "$DMG_PATH" xcrun stapler validate "$DMG_PATH" + BUNDLE_VERSION="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleVersion' "$APP_PATH/Contents/Info.plist")" + if [[ "v$BUNDLE_VERSION" != "$RELEASE_TAG" ]]; then + echo "CFBundleVersion ($BUNDLE_VERSION) does not match the release tag ($RELEASE_TAG)." + exit 1 + fi + - name: Create GitHub Release env: GH_TOKEN: ${{ github.token }} @@ -405,18 +393,10 @@ jobs: if gh release view "$RELEASE_TAG" >/dev/null 2>&1; then gh release upload "$RELEASE_TAG" "$DMG_PATH" --clobber else - if [[ "${IS_PRERELEASE}" == "true" ]]; then - gh release create "$RELEASE_TAG" \ - --title "$RELEASE_TAG" \ - --generate-notes \ - --prerelease \ - "$DMG_PATH" - else - gh release create "$RELEASE_TAG" \ - --title "$RELEASE_TAG" \ - --generate-notes \ - "$DMG_PATH" - fi + gh release create "$RELEASE_TAG" \ + --title "$RELEASE_TAG" \ + --generate-notes \ + "$DMG_PATH" fi - name: Prepare Sparkle private key @@ -460,12 +440,6 @@ jobs: rm -rf "$APPCAST_WORK_DIR" "$PAGES_DIR" mkdir -p "$APPCAST_WORK_DIR" "$PAGES_DIR" - if curl -fsSL "$SPARKLE_APPCAST_URL" -o "$APPCAST_WORK_DIR/appcast.xml"; then - echo "Using existing appcast.xml from GitHub Pages" - else - echo "No existing appcast.xml found; generating a new feed" - fi - cp "$DMG_PATH" "$APPCAST_WORK_DIR/" "$GENERATE_APPCAST" \ diff --git a/BitDream.xcodeproj/project.pbxproj b/BitDream.xcodeproj/project.pbxproj index 4d7accf..8239240 100644 --- a/BitDream.xcodeproj/project.pbxproj +++ b/BitDream.xcodeproj/project.pbxproj @@ -502,7 +502,7 @@ LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 26.0; - MARKETING_VERSION = 2.0.0; + MARKETING_VERSION = 2.0.1; PRODUCT_BUNDLE_IDENTIFIER = crapshack.BitDream; PRODUCT_NAME = "$(TARGET_NAME)"; REGISTER_APP_GROUPS = YES; @@ -552,7 +552,7 @@ LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 26.0; - MARKETING_VERSION = 2.0.0; + MARKETING_VERSION = 2.0.1; PRODUCT_BUNDLE_IDENTIFIER = crapshack.BitDream; PRODUCT_NAME = "$(TARGET_NAME)"; "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "BitDream App Developer ID"; @@ -590,7 +590,7 @@ ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MACOSX_DEPLOYMENT_TARGET = 26.0; - MARKETING_VERSION = 2.0.0; + MARKETING_VERSION = 2.0.1; PRODUCT_BUNDLE_IDENTIFIER = crapshack.BitDream.BitDreamWidgets; PRODUCT_NAME = "$(TARGET_NAME)"; REGISTER_APP_GROUPS = YES; @@ -636,7 +636,7 @@ ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MACOSX_DEPLOYMENT_TARGET = 26.0; - MARKETING_VERSION = 2.0.0; + MARKETING_VERSION = 2.0.1; PRODUCT_BUNDLE_IDENTIFIER = crapshack.BitDream.BitDreamWidgets; PRODUCT_NAME = "$(TARGET_NAME)"; "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "BitDream Widget Developer ID"; diff --git a/docs/releases.md b/docs/releases.md index 73e8c96..b2cb97c 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -9,13 +9,6 @@ Run the preflight checks, then create a stable release tag: ./scripts/tag-version.sh ``` -For a prerelease, provide a suffix without the leading hyphen: - -```bash -./scripts/tag-version.sh --dry-run --prerelease beta.1 -./scripts/tag-version.sh --prerelease beta.1 -``` - Pushing the tag starts `.github/workflows/release.yml`, which builds, signs, notarizes, and publishes the macOS release. The workflow rejects tags that do not match the Xcode marketing version. See `docs/build-settings.md` and `docs/sparkle-updates.md` for configuration details. diff --git a/scripts/tag-version.sh b/scripts/tag-version.sh index 387b4fd..e4284d2 100755 --- a/scripts/tag-version.sh +++ b/scripts/tag-version.sh @@ -4,19 +4,17 @@ set -euo pipefail usage() { cat <<'EOF' -Usage: ./scripts/tag-version.sh [--dry-run] [--prerelease SUFFIX] +Usage: ./scripts/tag-version.sh [--dry-run] Create and push a release tag based on the app's MARKETING_VERSION. Options: - --dry-run Run every preflight check without creating or pushing a tag. - --prerelease SUFFIX Append a prerelease suffix, for example beta.1. - -h, --help Show this help text. + --dry-run Run every preflight check without creating or pushing a tag. + -h, --help Show this help text. EOF } dry_run=false -prerelease_suffix="" while [[ $# -gt 0 ]]; do case "$1" in @@ -24,15 +22,6 @@ while [[ $# -gt 0 ]]; do dry_run=true shift ;; - --prerelease) - if [[ $# -lt 2 || -z "$2" ]]; then - echo "--prerelease requires a suffix." >&2 - usage >&2 - exit 2 - fi - prerelease_suffix="$2" - shift 2 - ;; -h|--help) usage exit 0 @@ -45,11 +34,6 @@ while [[ $# -gt 0 ]]; do esac done -if [[ -n "$prerelease_suffix" && ! "$prerelease_suffix" =~ ^[0-9A-Za-z]+([.-][0-9A-Za-z]+)*$ ]]; then - echo "Prerelease suffix must contain alphanumeric identifiers separated by dots or hyphens: $prerelease_suffix" >&2 - exit 2 -fi - for command in git xcodebuild; do if ! command -v "$command" >/dev/null 2>&1; then echo "Required command not found: $command" >&2 @@ -124,9 +108,6 @@ if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then fi tag="v$version" -if [[ -n "$prerelease_suffix" ]]; then - tag="$tag-$prerelease_suffix" -fi if git show-ref --verify --quiet "refs/tags/$tag"; then echo "Tag already exists: $tag" >&2 @@ -146,9 +127,6 @@ fi short_commit="$(git rev-parse --short HEAD)" echo echo "Release version: $version" -if [[ -n "$prerelease_suffix" ]]; then - echo "Prerelease: $prerelease_suffix" -fi echo "Tag: $tag" echo "Commit: $short_commit"