Skip to content

fix(settings): align preferences chrome #22

fix(settings): align preferences chrome

fix(settings): align preferences chrome #22

Workflow file for this run

name: Release DMG + Appcast
on:
push:
tags:
- "v*"
permissions:
contents: write
pages: write
id-token: write
jobs:
release:
runs-on: macos-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Configure GitHub Pages
uses: actions/configure-pages@v5
- name: Install Tuist
run: brew install tuist
- name: Install Tuist dependencies
run: TUIST_SKIP_UPDATE_CHECK=1 tuist install
- name: Validate Sparkle secrets
env:
SPARKLE_PRIVATE_ED_KEY: ${{ secrets.SPARKLE_PRIVATE_ED_KEY }}
SPARKLE_PUBLIC_ED_KEY: ${{ secrets.SPARKLE_PUBLIC_ED_KEY }}
run: |
if [ -z "$SPARKLE_PRIVATE_ED_KEY" ] || [ -z "$SPARKLE_PUBLIC_ED_KEY" ]; then
echo "SPARKLE_PRIVATE_ED_KEY and SPARKLE_PUBLIC_ED_KEY must be configured."
exit 1
fi
- name: Validate release metadata inputs
run: |
VERSION="${GITHUB_REF_NAME#v}"
if [ -z "$VERSION" ]; then
echo "Release version could not be derived from tag ${GITHUB_REF_NAME}."
exit 1
fi
if [ -z "${GITHUB_RUN_NUMBER}" ]; then
echo "GITHUB_RUN_NUMBER must be available for release builds."
exit 1
fi
- name: Import Apple signing certificate
env:
APPLE_CERTIFICATE_P12_BASE64: ${{ secrets.APPLE_CERTIFICATE_P12_BASE64 }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
if: ${{ env.APPLE_CERTIFICATE_P12_BASE64 != '' && env.APPLE_CERTIFICATE_PASSWORD != '' && env.APPLE_SIGNING_IDENTITY != '' }}
run: |
CERT_PATH="$RUNNER_TEMP/signing-certificate.p12"
KEYCHAIN_PATH="$RUNNER_TEMP/build.keychain-db"
KEYCHAIN_PASSWORD="$(uuidgen)"
echo "$APPLE_CERTIFICATE_P12_BASE64" | base64 -D > "$CERT_PATH"
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security import "$CERT_PATH" -k "$KEYCHAIN_PATH" -P "$APPLE_CERTIFICATE_PASSWORD" -f pkcs12 -A -T /usr/bin/codesign -T /usr/bin/security
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security list-keychains -d user -s "$KEYCHAIN_PATH"
security default-keychain -d user -s "$KEYCHAIN_PATH"
- name: Package DMG
id: package
env:
CODESIGN_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
MENU_STATUS_BUILD: ${{ github.run_number }}
MENU_STATUS_FEED_URL: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/appcast.xml
MENU_STATUS_PUBLIC_ED_KEY: ${{ secrets.SPARKLE_PUBLIC_ED_KEY }}
run: |
./package-app.sh "${GITHUB_REF_NAME#v}"
- name: Detect release channel
id: channel
run: |
python3 Scripts/release-appcast.py classify-version "${GITHUB_REF_NAME#v}" --github-output "$GITHUB_OUTPUT"
- name: Upload GitHub release asset
uses: softprops/action-gh-release@v2
with:
files: ${{ steps.package.outputs.artifact_path }}
generate_release_notes: true
fail_on_unmatched_files: true
prerelease: ${{ steps.channel.outputs.prerelease }}
- name: Generate appcast.xml
env:
SPARKLE_PRIVATE_ED_KEY: ${{ secrets.SPARKLE_PRIVATE_ED_KEY }}
GH_TOKEN: ${{ github.token }}
run: |
ARTIFACT_PATH="${{ steps.package.outputs.artifact_path }}"
ARTIFACT_NAME="${{ steps.package.outputs.artifact_name }}"
SPARKLE_BIN="Tuist/.build/artifacts/sparkle/Sparkle/bin"
APPCAST_WORK_DIR="$RUNNER_TEMP/appcast"
PAGES_DIR="$RUNNER_TEMP/pages"
ASSET_TAGS="$RUNNER_TEMP/asset-tags.tsv"
mkdir -p "$APPCAST_WORK_DIR" "$PAGES_DIR"
: > "$ASSET_TAGS"
# Include only the latest stable release and the latest pre-release in the appcast.
# Sparkle's client picks the highest-version visible item — older releases add no value
# and some pre-Sparkle-workflow DMGs share CFBundleVersion which would break generate_appcast.
gh release list --repo "$GITHUB_REPOSITORY" --limit 100 \
--json tagName,isPrerelease,createdAt > "$RUNNER_TEMP/releases.json"
python3 - "$RUNNER_TEMP/releases.json" "$GITHUB_REF_NAME" > "$RUNNER_TEMP/tags.txt" <<'PYEOF'
import json, sys
releases = json.load(open(sys.argv[1]))
current_tag = sys.argv[2]
releases.sort(key=lambda r: r["createdAt"], reverse=True)
latest_stable = next((r["tagName"] for r in releases if not r["isPrerelease"]), None)
latest_beta = next((r["tagName"] for r in releases if r["isPrerelease"]), None)
tags = {t for t in (latest_stable, latest_beta, current_tag) if t}
for t in sorted(tags):
print(t)
PYEOF
while read -r tag; do
[ -z "$tag" ] && continue
if [ "$tag" = "$GITHUB_REF_NAME" ]; then
printf '%s\t%s\n' "$ARTIFACT_NAME" "$tag" >> "$ASSET_TAGS"
continue
fi
assets="$(gh release view "$tag" \
--repo "$GITHUB_REPOSITORY" \
--json assets \
--jq '.assets[].name | select(endswith(".dmg"))')"
if [ -z "$assets" ]; then
echo "No DMG asset found for release $tag."
exit 1
fi
gh release download "$tag" \
--repo "$GITHUB_REPOSITORY" \
--dir "$APPCAST_WORK_DIR" \
--pattern "*.dmg" \
--skip-existing
while IFS= read -r asset; do
[ -z "$asset" ] && continue
if [ ! -f "$APPCAST_WORK_DIR/$asset" ]; then
echo "Expected DMG asset $asset from release $tag was not downloaded."
exit 1
fi
printf '%s\t%s\n' "$asset" "$tag" >> "$ASSET_TAGS"
done <<< "$assets"
done < "$RUNNER_TEMP/tags.txt"
# Always include the freshly built artifact — the release-asset upload above
# may not be visible yet when this step runs.
cp -f "$ARTIFACT_PATH" "$APPCAST_WORK_DIR/$ARTIFACT_NAME"
printf '%s\t%s\n' "$ARTIFACT_NAME" "$GITHUB_REF_NAME" >> "$ASSET_TAGS"
# generate_appcast emits one <item> per .dmg, computing Sparkle EdDSA signatures
# from binary contents. The --download-url-prefix below is a placeholder — the
# Python post-process step rewrites each enclosure URL to the correct per-tag
# GitHub Releases path. --maximum-deltas 0 disables binary delta generation:
# our DMGs are ~4MB so deltas aren't worth the workflow complexity of uploading
# .delta files to the matching release and rewriting their URLs.
echo "$SPARKLE_PRIVATE_ED_KEY" | "$SPARKLE_BIN/generate_appcast" \
--ed-key-file - \
--maximum-deltas 0 \
--download-url-prefix "https://github.com/${GITHUB_REPOSITORY}/releases/download/__placeholder__/" \
"$APPCAST_WORK_DIR"
# Post-process: tag allowed pre-release items with <sparkle:channel>beta</sparkle:channel>,
# rewrite each enclosure URL using the actual release tag that provided the asset,
# and assert no placeholder or delta entries remain.
python3 Scripts/release-appcast.py postprocess \
--appcast "$APPCAST_WORK_DIR/appcast.xml" \
--repo "$GITHUB_REPOSITORY" \
--asset-tags "$ASSET_TAGS"
cp "$APPCAST_WORK_DIR/appcast.xml" "$PAGES_DIR/appcast.xml"
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: ${{ runner.temp }}/pages
deploy-appcast:
needs: release
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy GitHub Pages
id: deployment
uses: actions/deploy-pages@v4