Skip to content

Commit 929f8de

Browse files
committed
refactor(release): move appcast updates to dedicated post-build workflows
The appcast commit step inside the build job races with developer pushes to main. The build takes ~10 minutes; any push during that window causes a non-fast-forward rejection. Fix: strip appcast logic out of release.yml and nightly-dev.yml entirely. Add two new workflows that trigger on workflow_run completed+success: - update-appcast.yml : triggered by 'Release SAM' success - update-dev-appcast.yml : triggered by 'Weekly Development Release' success Both check out main fresh (ref: main, not the tag), download the ZIP from the just-created GitHub Release, sign it, and push a single commit to main. The job is ~30 seconds with no competing pushes possible. This is the same pattern already used by update-homebrew-cask.yml.
1 parent d22c77f commit 929f8de

File tree

4 files changed

+164
-70
lines changed

4 files changed

+164
-70
lines changed

.github/workflows/nightly-dev.yml

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -109,40 +109,6 @@ jobs:
109109
echo "Distribution files ready:"
110110
ls -lh dist/SAM-${DEV_VERSION}.*
111111
112-
- name: Update development appcast
113-
env:
114-
SPARKLE_PRIVATE_KEY: ${{ secrets.SPARKLE_PRIVATE_KEY }}
115-
run: |
116-
# Create temporary file for private key
117-
TEMP_KEY_FILE=$(mktemp)
118-
echo "$SPARKLE_PRIVATE_KEY" > "$TEMP_KEY_FILE"
119-
chmod 600 "$TEMP_KEY_FILE"
120-
121-
# Update appcast-dev-items.xml with new development release
122-
./scripts/update_dev_appcast.sh "${DEV_VERSION}" "dist/SAM-${DEV_VERSION}.zip" "$TEMP_KEY_FILE"
123-
124-
# Generate appcast-dev.xml from items + stable releases
125-
./scripts/generate-dev-appcast.sh
126-
127-
# Clean up temporary key file
128-
rm -f "$TEMP_KEY_FILE"
129-
130-
- name: Commit and push appcast changes
131-
run: |
132-
git config user.name "GitHub Actions"
133-
git config user.email "actions@github.com"
134-
git add appcast-dev-items.xml appcast-dev.xml
135-
if git diff --staged --quiet; then
136-
echo "No changes to development appcast files"
137-
else
138-
git commit -m "chore(dev-release): update appcast-dev.xml for v${DEV_VERSION}"
139-
# The build takes time; commits may have landed on main. Fetch and rebase
140-
# so our appcast commit lands on top before pushing.
141-
git fetch origin main
142-
git rebase origin/main
143-
git push origin HEAD:main
144-
fi
145-
146112
- name: Create GitHub pre-release
147113
env:
148114
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/release.yml

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -94,42 +94,6 @@ jobs:
9494
ls -lh dist/SAM-${VERSION}.*
9595
9696
- name: Update appcast.xml
97-
env:
98-
SPARKLE_PRIVATE_KEY: ${{ secrets.SPARKLE_PRIVATE_KEY }}
99-
VERSION: ${{ steps.version.outputs.VERSION }}
100-
run: |
101-
# Create temporary file for private key
102-
TEMP_KEY_FILE=$(mktemp)
103-
echo "$SPARKLE_PRIVATE_KEY" > "$TEMP_KEY_FILE"
104-
chmod 600 "$TEMP_KEY_FILE"
105-
106-
# Update appcast with signature
107-
./scripts/update_appcast.sh "${VERSION}" "dist/SAM-${VERSION}.zip" "$TEMP_KEY_FILE"
108-
109-
# Clean up temporary key file
110-
rm -f "$TEMP_KEY_FILE"
111-
112-
- name: Commit and push appcast changes
113-
env:
114-
VERSION: ${{ steps.version.outputs.VERSION }}
115-
run: |
116-
git config user.name "GitHub Actions"
117-
git config user.email "actions@github.com"
118-
119-
git add appcast.xml
120-
if git diff --staged --quiet; then
121-
echo "No changes to appcast.xml"
122-
else
123-
git commit -m "chore(release): update appcast.xml for v${VERSION}"
124-
# Checkout is triggered by a tag, so HEAD is detached.
125-
# The build takes ~10 minutes, so commits may have landed on main since
126-
# we checked out. Fetch and rebase so our appcast commit lands on top,
127-
# then push. This is race-condition-proof without force-pushing.
128-
git fetch origin main
129-
git rebase origin/main
130-
git push origin HEAD:main
131-
fi
132-
13397
- name: Create GitHub Release
13498
env:
13599
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
name: Update Appcast
2+
3+
# Runs after Release SAM succeeds. Checks out main fresh (not the tag),
4+
# so there is no race between the ~10-minute build and developer pushes.
5+
# The ZIP is already in the GitHub Release at this point - we just sign
6+
# it and commit the appcast entry.
7+
on:
8+
workflow_run:
9+
workflows: ["Release SAM"]
10+
types:
11+
- completed
12+
workflow_dispatch:
13+
inputs:
14+
version:
15+
description: 'Version to update appcast for (e.g., 20260315.2)'
16+
required: true
17+
type: string
18+
19+
permissions:
20+
contents: write
21+
22+
jobs:
23+
update-appcast:
24+
name: Update appcast.xml
25+
runs-on: [self-hosted]
26+
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
27+
28+
steps:
29+
- name: Extract version
30+
id: version
31+
env:
32+
EVENT_NAME: ${{ github.event_name }}
33+
INPUT_VERSION: ${{ github.event.inputs.version }}
34+
HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
35+
run: |
36+
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
37+
VERSION="$INPUT_VERSION"
38+
else
39+
VERSION="$HEAD_BRANCH"
40+
fi
41+
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
42+
echo "Updating appcast for version: $VERSION"
43+
44+
- name: Checkout main
45+
uses: actions/checkout@v6
46+
with:
47+
ref: main
48+
fetch-depth: 0
49+
50+
- name: Download ZIP from release
51+
env:
52+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
53+
VERSION: ${{ steps.version.outputs.VERSION }}
54+
run: |
55+
mkdir -p dist
56+
gh release download "${VERSION}" --pattern "SAM-${VERSION}.zip" --dir dist
57+
ls -lh "dist/SAM-${VERSION}.zip"
58+
59+
- name: Update appcast.xml
60+
env:
61+
SPARKLE_PRIVATE_KEY: ${{ secrets.SPARKLE_PRIVATE_KEY }}
62+
VERSION: ${{ steps.version.outputs.VERSION }}
63+
run: |
64+
TEMP_KEY_FILE=$(mktemp)
65+
echo "$SPARKLE_PRIVATE_KEY" > "$TEMP_KEY_FILE"
66+
chmod 600 "$TEMP_KEY_FILE"
67+
./scripts/update_appcast.sh "${VERSION}" "dist/SAM-${VERSION}.zip" "$TEMP_KEY_FILE"
68+
rm -f "$TEMP_KEY_FILE"
69+
70+
- name: Commit and push
71+
env:
72+
VERSION: ${{ steps.version.outputs.VERSION }}
73+
run: |
74+
git config user.name "GitHub Actions"
75+
git config user.email "actions@github.com"
76+
git add appcast.xml
77+
if git diff --staged --quiet; then
78+
echo "No changes to appcast.xml"
79+
else
80+
git commit -m "chore(release): update appcast.xml for v${VERSION}"
81+
git push origin main
82+
fi
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
name: Update Dev Appcast
2+
3+
# Runs after Weekly Development Release succeeds. Checks out main fresh,
4+
# downloads the ZIP from the just-created pre-release, signs it, and
5+
# commits the appcast-dev entries - no race with developer pushes.
6+
on:
7+
workflow_run:
8+
workflows: ["Weekly Development Release"]
9+
types:
10+
- completed
11+
workflow_dispatch:
12+
inputs:
13+
version:
14+
description: 'Dev version to update appcast for (e.g., 20260315.2-dev.1)'
15+
required: true
16+
type: string
17+
18+
permissions:
19+
contents: write
20+
21+
jobs:
22+
update-dev-appcast:
23+
name: Update appcast-dev.xml
24+
runs-on: [self-hosted]
25+
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
26+
27+
steps:
28+
- name: Extract version
29+
id: version
30+
env:
31+
EVENT_NAME: ${{ github.event_name }}
32+
INPUT_VERSION: ${{ github.event.inputs.version }}
33+
HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
34+
run: |
35+
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
36+
VERSION="$INPUT_VERSION"
37+
else
38+
VERSION="$HEAD_BRANCH"
39+
fi
40+
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
41+
echo "Updating dev appcast for version: $VERSION"
42+
43+
- name: Checkout main
44+
uses: actions/checkout@v6
45+
with:
46+
ref: main
47+
fetch-depth: 0
48+
49+
- name: Download ZIP from pre-release
50+
env:
51+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
52+
VERSION: ${{ steps.version.outputs.VERSION }}
53+
run: |
54+
mkdir -p dist
55+
gh release download "${VERSION}" --pattern "SAM-${VERSION}.zip" --dir dist
56+
ls -lh "dist/SAM-${VERSION}.zip"
57+
58+
- name: Update development appcast
59+
env:
60+
SPARKLE_PRIVATE_KEY: ${{ secrets.SPARKLE_PRIVATE_KEY }}
61+
VERSION: ${{ steps.version.outputs.VERSION }}
62+
run: |
63+
TEMP_KEY_FILE=$(mktemp)
64+
echo "$SPARKLE_PRIVATE_KEY" > "$TEMP_KEY_FILE"
65+
chmod 600 "$TEMP_KEY_FILE"
66+
./scripts/update_dev_appcast.sh "${VERSION}" "dist/SAM-${VERSION}.zip" "$TEMP_KEY_FILE"
67+
./scripts/generate-dev-appcast.sh
68+
rm -f "$TEMP_KEY_FILE"
69+
70+
- name: Commit and push
71+
env:
72+
VERSION: ${{ steps.version.outputs.VERSION }}
73+
run: |
74+
git config user.name "GitHub Actions"
75+
git config user.email "actions@github.com"
76+
git add appcast-dev-items.xml appcast-dev.xml
77+
if git diff --staged --quiet; then
78+
echo "No changes to development appcast files"
79+
else
80+
git commit -m "chore(dev-release): update appcast-dev.xml for v${VERSION}"
81+
git push origin main
82+
fi

0 commit comments

Comments
 (0)