mirror of
https://github.com/yattee/yattee.git
synced 2026-05-13 10:55:03 +00:00
Integrate Sparkle auto-updates for macOS Developer ID builds
New Release-DeveloperID configuration gates Sparkle behind a SPARKLE compile flag so the App Store Release build stays Sparkle-free. Adds SPUStandardUpdaterController wrapper, Check for Updates menu command, Advanced Settings section with beta channel toggle, and a Ruby script plus GitHub Actions job that signs each release and publishes the appcast to gh-pages for consumption by Sparkle and Homebrew cask.
This commit is contained in:
142
.github/workflows/release.yml
vendored
142
.github/workflows/release.yml
vendored
@@ -15,9 +15,16 @@ on:
|
||||
type: boolean
|
||||
default: false
|
||||
build_mac_notarized:
|
||||
description: 'Build macOS (notarized)'
|
||||
description: 'Build macOS (notarized Developer ID + Sparkle appcast)'
|
||||
type: boolean
|
||||
default: false
|
||||
default: true
|
||||
release_channel:
|
||||
description: 'Sparkle / Developer ID channel (also toggles GitHub prerelease flag)'
|
||||
type: choice
|
||||
options:
|
||||
- beta
|
||||
- stable
|
||||
default: beta
|
||||
create_release:
|
||||
description: 'Create GitHub release'
|
||||
type: boolean
|
||||
@@ -174,15 +181,18 @@ jobs:
|
||||
- uses: maierj/fastlane-action@v3.0.0
|
||||
with:
|
||||
lane: mac build_and_notarize
|
||||
- run: |
|
||||
echo "APP_PATH=fastlane/builds/${{ env.VERSION_NUMBER }}-${{ env.BUILD_NUMBER }}/macOS/Yattee.app" >> $GITHUB_ENV
|
||||
echo "ZIP_PATH=fastlane/builds/${{ env.VERSION_NUMBER }}-${{ env.BUILD_NUMBER }}/macOS/Yattee-${{ env.VERSION_NUMBER }}-macOS.zip" >> $GITHUB_ENV
|
||||
- name: ZIP build
|
||||
run: /usr/bin/ditto -c -k --keepParent ${{ env.APP_PATH }} ${{ env.ZIP_PATH }}
|
||||
- name: Resolve artifact paths
|
||||
run: |
|
||||
DIR="fastlane/builds/${{ env.VERSION_NUMBER }}-${{ env.BUILD_NUMBER }}/macOS"
|
||||
echo "APP_PATH=$DIR/Yattee.app" >> $GITHUB_ENV
|
||||
echo "ZIP_PATH=$DIR/Yattee-${{ env.VERSION_NUMBER }}-macOS.zip" >> $GITHUB_ENV
|
||||
echo "DMG_PATH=$DIR/Yattee-${{ env.VERSION_NUMBER }}-macOS.dmg" >> $GITHUB_ENV
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: mac-notarized-build
|
||||
path: ${{ env.ZIP_PATH }}
|
||||
path: |
|
||||
${{ env.ZIP_PATH }}
|
||||
${{ env.DMG_PATH }}
|
||||
if-no-files-found: error
|
||||
|
||||
release:
|
||||
@@ -192,9 +202,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
outputs:
|
||||
tag: ${{ steps.compute_tag.outputs.tag }}
|
||||
env:
|
||||
BUILD_NUMBER: ${{ needs.determine_build_number.outputs.build_number }}
|
||||
VERSION_NUMBER: ${{ needs.determine_build_number.outputs.version_number }}
|
||||
RELEASE_CHANNEL: ${{ inputs.release_channel }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -212,14 +225,121 @@ jobs:
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
- name: Compute release tag
|
||||
id: compute_tag
|
||||
run: |
|
||||
if [ "$RELEASE_CHANNEL" = "beta" ]; then
|
||||
echo "tag=${VERSION_NUMBER}-beta.${BUILD_NUMBER}" >> "$GITHUB_OUTPUT"
|
||||
echo "prerelease=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "tag=${VERSION_NUMBER}-${BUILD_NUMBER}" >> "$GITHUB_OUTPUT"
|
||||
echo "prerelease=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
- uses: ncipollo/release-action@v1
|
||||
with:
|
||||
artifacts: artifacts/**/*.ipa,artifacts/**/*.zip,artifacts/**/*.pkg
|
||||
artifacts: artifacts/**/*.ipa,artifacts/**/*.zip,artifacts/**/*.pkg,artifacts/**/*.dmg
|
||||
commit: ${{ github.ref_name }}
|
||||
tag: ${{ env.VERSION_NUMBER }}-${{ env.BUILD_NUMBER }}
|
||||
prerelease: true
|
||||
tag: ${{ steps.compute_tag.outputs.tag }}
|
||||
prerelease: ${{ steps.compute_tag.outputs.prerelease }}
|
||||
bodyFile: CHANGELOG.md
|
||||
|
||||
publish_appcast:
|
||||
if: ${{ inputs.build_mac_notarized && inputs.create_release && !cancelled() && !failure() }}
|
||||
needs: [determine_build_number, mac_notarized, release]
|
||||
name: Publish Sparkle appcast
|
||||
runs-on: macos-26
|
||||
permissions:
|
||||
contents: write
|
||||
env:
|
||||
BUILD_NUMBER: ${{ needs.determine_build_number.outputs.build_number }}
|
||||
VERSION_NUMBER: ${{ needs.determine_build_number.outputs.version_number }}
|
||||
RELEASE_CHANNEL: ${{ inputs.release_channel }}
|
||||
RELEASE_TAG: ${{ needs.release.outputs.tag }}
|
||||
SPARKLE_ED_PRIVATE_KEY: ${{ secrets.SPARKLE_ED_PRIVATE_KEY }}
|
||||
REPO: ${{ github.repository }}
|
||||
steps:
|
||||
- name: Guard — secret configured
|
||||
run: |
|
||||
if [ -z "$SPARKLE_ED_PRIVATE_KEY" ]; then
|
||||
echo "::error::SPARKLE_ED_PRIVATE_KEY secret is not set. Configure it with the base64-encoded private key exported via 'generate_keys -x'."
|
||||
exit 1
|
||||
fi
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.REPO_TOKEN }}
|
||||
- name: Download notarized mac artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: mac-notarized-build
|
||||
path: mac-artifacts
|
||||
- name: Locate sign_update binary
|
||||
id: find_sign_update
|
||||
run: |
|
||||
# Sparkle's `sign_update` ships as a package artifact. We need SPM to
|
||||
# resolve the Sparkle package so the binary is present on disk.
|
||||
xcodebuild -resolvePackageDependencies -project Yattee.xcodeproj -scheme Yattee >/dev/null
|
||||
SIGN=$(find "$HOME/Library/Developer/Xcode/DerivedData" -name sign_update -type f 2>/dev/null | head -1)
|
||||
if [ -z "$SIGN" ]; then
|
||||
SIGN=$(find ~ -name sign_update -type f 2>/dev/null | head -1)
|
||||
fi
|
||||
if [ -z "$SIGN" ]; then
|
||||
echo "::error::Could not locate sign_update binary"
|
||||
exit 1
|
||||
fi
|
||||
echo "sign_update=$SIGN" >> "$GITHUB_OUTPUT"
|
||||
- name: Checkout gh-pages (create if missing)
|
||||
run: |
|
||||
git fetch origin gh-pages || true
|
||||
if git rev-parse --verify origin/gh-pages >/dev/null 2>&1; then
|
||||
git worktree add gh-pages origin/gh-pages
|
||||
else
|
||||
# First run — create orphan gh-pages with only appcast scaffolding.
|
||||
git worktree add --detach gh-pages HEAD
|
||||
cd gh-pages
|
||||
git checkout --orphan gh-pages
|
||||
git rm -rf . >/dev/null 2>&1 || true
|
||||
cp ../scripts/sparkle/appcast_template.xml appcast.xml
|
||||
cd ..
|
||||
fi
|
||||
- name: Write private key to a temp file
|
||||
id: ed_key
|
||||
run: |
|
||||
KEY_FILE=$(mktemp)
|
||||
printf '%s' "$SPARKLE_ED_PRIVATE_KEY" > "$KEY_FILE"
|
||||
echo "path=$KEY_FILE" >> "$GITHUB_OUTPUT"
|
||||
- name: Sign update and update appcast.xml
|
||||
run: |
|
||||
ZIP=$(find mac-artifacts -name '*.zip' | head -1)
|
||||
if [ -z "$ZIP" ]; then
|
||||
echo "::error::No .zip found in mac-artifacts"
|
||||
exit 1
|
||||
fi
|
||||
./scripts/sparkle/update_appcast.rb \
|
||||
--zip "$ZIP" \
|
||||
--version "$VERSION_NUMBER" \
|
||||
--build "$BUILD_NUMBER" \
|
||||
--channel "$RELEASE_CHANNEL" \
|
||||
--tag "$RELEASE_TAG" \
|
||||
--sign-update-bin "${{ steps.find_sign_update.outputs.sign_update }}" \
|
||||
--ed-key-file "${{ steps.ed_key.outputs.path }}" \
|
||||
--appcast gh-pages/appcast.xml \
|
||||
--repo "$REPO"
|
||||
- name: Scrub private key
|
||||
if: always()
|
||||
run: rm -f "${{ steps.ed_key.outputs.path }}"
|
||||
- name: Commit & push appcast.xml
|
||||
run: |
|
||||
cd gh-pages
|
||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git add appcast.xml
|
||||
if git diff --cached --quiet; then
|
||||
echo "No appcast changes to publish"
|
||||
else
|
||||
git commit -m "Publish Sparkle appcast: ${VERSION_NUMBER} (${BUILD_NUMBER}) [${RELEASE_CHANNEL}]"
|
||||
git push origin gh-pages
|
||||
fi
|
||||
|
||||
update_altstore:
|
||||
needs: [release]
|
||||
uses: ./.github/workflows/update-altstore.yml
|
||||
|
||||
Reference in New Issue
Block a user