134 lines
5.9 KiB
YAML
134 lines
5.9 KiB
YAML
name: Release
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- 'v*'
|
|
|
|
jobs:
|
|
release:
|
|
name: Build & publish
|
|
runs-on: macos-14
|
|
permissions:
|
|
contents: write # create releases and commit appcast
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
with:
|
|
# Fetch full history so we can push the appcast commit back.
|
|
fetch-depth: 0
|
|
# Admin PAT so the appcast commit can be pushed to the protected
|
|
# `main` branch. The default GITHUB_TOKEN/bot is not an admin and is
|
|
# blocked by the required `test` status check.
|
|
token: ${{ secrets.RELEASE_TOKEN }}
|
|
|
|
- uses: maxim-lobanov/setup-xcode@v1
|
|
with:
|
|
xcode-version: latest-stable
|
|
|
|
- name: Cache .build
|
|
uses: actions/cache@v5
|
|
with:
|
|
path: .build
|
|
key: spm-v2-${{ runner.os }}-${{ hashFiles('Package.resolved') }}
|
|
restore-keys: spm-v2-${{ runner.os }}-
|
|
|
|
- name: Build app bundle
|
|
run: ./scripts/build-app.sh
|
|
|
|
# create-dmg 8.x requires Node >= 20; pin it so the runner's default node
|
|
# version can't break the release.
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 20
|
|
|
|
- name: Install create-dmg
|
|
# sindresorhus/create-dmg is the npm package (NOT the homebrew
|
|
# create-dmg/create-dmg tool, which has an incompatible CLI).
|
|
run: npm install --global create-dmg
|
|
|
|
- name: Create DMG
|
|
run: |
|
|
VERSION="$(/usr/libexec/PlistBuddy -c 'Print CFBundleShortVersionString' Info.plist)"
|
|
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
|
|
# create-dmg exits 2 when it can't Developer-ID-sign / notarize the
|
|
# image (we ship ad-hoc only) but still produces the .dmg — so don't
|
|
# let that non-zero status fail the job.
|
|
create-dmg build/Edmund.app build/ || true
|
|
# It names the output "Edmund <version>.dmg"; normalize to a
|
|
# hyphenated, URL-friendly name and fail loudly if none was produced.
|
|
DMG_SRC="$(ls build/Edmund*.dmg 2>/dev/null | head -1)"
|
|
if [ -z "$DMG_SRC" ]; then
|
|
echo "create-dmg produced no .dmg" >&2
|
|
exit 1
|
|
fi
|
|
mv "$DMG_SRC" "build/Edmund-${VERSION}.dmg"
|
|
|
|
- name: Sign archive (EdDSA)
|
|
env:
|
|
SPARKLE_ED_PRIVATE_KEY: ${{ secrets.SPARKLE_ED_PRIVATE_KEY }}
|
|
run: |
|
|
SIGN_UPDATE="$(find .build -name sign_update -type f | head -1)"
|
|
# Pass the key on stdin via --ed-key-file -. The deprecated `-s <key>`
|
|
# is fatal for newly generated keys ("no longer supported").
|
|
SIG_OUTPUT="$(echo "$SPARKLE_ED_PRIVATE_KEY" | "$SIGN_UPDATE" --ed-key-file - "build/Edmund-${VERSION}.dmg")"
|
|
echo "ED_SIG=$(echo "$SIG_OUTPUT" | grep -o 'sparkle:edSignature="[^"]*"')" >> "$GITHUB_ENV"
|
|
echo "LENGTH=$(echo "$SIG_OUTPUT" | grep -o 'length="[^"]*"' | head -1)" >> "$GITHUB_ENV"
|
|
|
|
- name: Create GitHub Release
|
|
id: create_release
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
awk "BEGIN{p=0} /^## \[${VERSION}\]/{p=1;next} p && /^## \[/{exit} p{print}" \
|
|
CHANGELOG.md > release-notes.txt
|
|
if [ ! -s release-notes.txt ]; then
|
|
echo "See CHANGELOG for details." > release-notes.txt
|
|
fi
|
|
gh release create "v${VERSION}" "build/Edmund-${VERSION}.dmg" \
|
|
--title "Edmund ${VERSION}" \
|
|
--notes-file release-notes.txt \
|
|
--latest
|
|
|
|
- name: Update appcast.xml
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
REPO="${{ github.repository }}"
|
|
ASSET_URL="https://github.com/${REPO}/releases/download/v${VERSION}/Edmund-${VERSION}.dmg"
|
|
PUB_DATE="$(date -u '+%a, %d %b %Y %H:%M:%S +0000')"
|
|
BUILD="$(/usr/libexec/PlistBuddy -c 'Print CFBundleVersion' Info.plist)"
|
|
|
|
# Release notes for Sparkle's update dialog (scrollable pane), built
|
|
# from this version's CHANGELOG section. Omitted if the section is missing.
|
|
# Built with printf (not a literal multi-line string) so this step's
|
|
# `run: |` block scalar doesn't contain an unindented line, which
|
|
# breaks YAML parsing of the whole workflow file.
|
|
# $(...) strips trailing newlines, so DESC_BLOCK can't carry its own
|
|
# trailing separator — NEW_ITEM's printf adds it explicitly instead.
|
|
DESC_HTML="$(python3 scripts/changelog-to-html.py "$VERSION")"
|
|
DESC_BLOCK=""
|
|
if [ -n "$DESC_HTML" ]; then
|
|
DESC_BLOCK="$(printf ' <description><![CDATA[\n%s\n]]></description>' "$DESC_HTML")"
|
|
fi
|
|
|
|
# Built with printf (one logical YAML line) rather than a literal
|
|
# multi-line string: an unindented line inside this value would
|
|
# break this run block's YAML indentation (as DESC_BLOCK did above).
|
|
NEW_ITEM="$(printf ' <item>\n <title>Edmund %s</title>\n <pubDate>%s</pubDate>\n%s\n <enclosure url="%s"\n sparkle:version="%s"\n sparkle:shortVersionString="%s"\n %s\n %s\n type="application/x-apple-diskimage"/>\n </item>' "$VERSION" "$PUB_DATE" "$DESC_BLOCK" "$ASSET_URL" "$BUILD" "$VERSION" "$ED_SIG" "$LENGTH")"
|
|
|
|
python3 - "$NEW_ITEM" <<'PYEOF'
|
|
import sys
|
|
new_item = sys.argv[1]
|
|
with open("appcast.xml", "r") as f:
|
|
content = f.read()
|
|
content = content.replace(" </channel>", new_item + "\n </channel>")
|
|
with open("appcast.xml", "w") as f:
|
|
f.write(content)
|
|
PYEOF
|
|
|
|
git config user.name "github-actions[bot]"
|
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
git add appcast.xml
|
|
git commit -m "appcast: add Edmund ${VERSION}"
|
|
git push origin HEAD:main
|