-
Notifications
You must be signed in to change notification settings - Fork 0
166 lines (156 loc) Β· 7.06 KB
/
Copy pathpost-release-formula.yml
File metadata and controls
166 lines (156 loc) Β· 7.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
name: Post-Release Formula Update
# Runs after the Release workflow finishes uploading binaries + their .sha256
# sidecars. Pulls the real shas, rewrites the formula in CorvidLabs/homebrew-tap
# with the new version + shas. This is the only correct moment to bump the
# formula β at `fledge release` time the new version's binaries don't exist
# yet, so any pre-build sha would be a lie.
#
# Security note: every value derived from `github.event.workflow_run.*` (or
# any other potentially attacker-controlled context) flows through an `env:`
# block before reaching a shell. Direct `${{ β¦ }}` interpolation inside
# `run: |` is a code-injection sink and is intentionally avoided here.
permissions:
contents: read
on:
workflow_run:
workflows: ["Release"]
types: [completed]
# Manual recovery lever: re-sync the formula for a given tag without
# re-triggering the whole Release workflow (e.g. after rotating an expired
# TAP_GITHUB_TOKEN). The workflow_run-only trigger previously left no way to
# recover a failed formula update except re-running a past workflow_run.
workflow_dispatch:
inputs:
tag:
description: "Release tag to sync (e.g. v1.6.0)"
required: true
type: string
jobs:
update-formula:
if: ${{ github.event_name == 'workflow_dispatch' || (github.event.workflow_run.conclusion == 'success' && startsWith(github.event.workflow_run.head_branch, 'v')) }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: main
- name: Resolve release tag
id: tag
env:
# workflow_run supplies the tag via head_branch; manual dispatch via input.
HEAD_BRANCH: ${{ github.event.workflow_run.head_branch || github.event.inputs.tag }}
run: |
set -euo pipefail
if ! [[ "$HEAD_BRANCH" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]]; then
echo "::error::Refusing to run with non-semver tag '$HEAD_BRANCH'"
exit 1
fi
{
echo "tag=$HEAD_BRANCH"
echo "version=${HEAD_BRANCH#v}"
} >> "$GITHUB_OUTPUT"
- name: Fetch sha256 sidecars from release
id: shas
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.tag.outputs.tag }}
REPO: ${{ github.repository }}
run: |
set -euo pipefail
fetch() {
gh release download "$TAG" -p "$1.sha256" -R "$REPO" -O - \
| awk '{print $1}'
}
validate() {
local sha="$1"
if ! [[ "$sha" =~ ^[0-9a-fA-F]{64}$ ]]; then
echo "::error::Invalid sha256 from sidecar: '$sha'"
exit 1
fi
printf '%s' "$sha"
}
{
echo "macos_aarch64=$(validate "$(fetch fledge-macos-aarch64)")"
echo "macos_x86_64=$(validate "$(fetch fledge-macos-x86_64)")"
echo "linux_x86_64=$(validate "$(fetch fledge-linux-x86_64)")"
} >> "$GITHUB_OUTPUT"
- name: Sync formula to homebrew-tap
env:
GH_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }}
NEW_VERSION: ${{ steps.tag.outputs.version }}
MACOS_AARCH64: ${{ steps.shas.outputs.macos_aarch64 }}
MACOS_X86_64: ${{ steps.shas.outputs.macos_x86_64 }}
LINUX_X86_64: ${{ steps.shas.outputs.linux_x86_64 }}
run: |
set -euo pipefail
if [ -z "$GH_TOKEN" ]; then
echo "::error::TAP_GITHUB_TOKEN secret is required"
exit 1
fi
git clone "https://x-access-token:${GH_TOKEN}@github.com/CorvidLabs/homebrew-tap.git" /tmp/homebrew-tap
cd /tmp/homebrew-tap
# Rewrite the formula in-place
python3 - <<'PY'
import os, re, sys, pathlib
p = pathlib.Path("Formula/fledge.rb")
src = p.read_text()
src = re.sub(
r'(?m)^(\s*version\s+")(\d+\.\d+\.\d+)(")',
rf'\g<1>{os.environ["NEW_VERSION"]}\g<3>',
src,
count=1,
)
shas = [
os.environ["MACOS_AARCH64"],
os.environ["MACOS_X86_64"],
os.environ["LINUX_X86_64"],
]
parts = re.split(r'(sha256\s+"[0-9a-fA-F]{64}")', src)
if len(parts) - 1 != 6:
sys.exit(
f"Expected 3 sha256 lines in Formula/fledge.rb, found {(len(parts) - 1) // 2}"
)
for i, sha in enumerate(shas):
parts[2 * i + 1] = f'sha256 "{sha}"'
p.write_text("".join(parts))
PY
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
if git diff --quiet -- Formula/fledge.rb; then
echo "homebrew-tap Formula/fledge.rb already at v$NEW_VERSION β nothing to do."
exit 0
fi
git add Formula/fledge.rb
git commit -m "chore: update formula to v$NEW_VERSION"
git push origin main
- name: Verify channels converged
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NEW_VERSION: ${{ steps.tag.outputs.version }}
run: |
set -euo pipefail
# Assert the pushed formula version actually took effect. GitHub's
# Contents API can serve a cached blob for several seconds after a
# push, so poll with a short backoff before declaring failure β
# otherwise a sync that DID land false-fails as "did not take effect"
# (observed on the v1.6.0 backfill). This still turns a genuine no-op
# push into a red run, just without the propagation race.
tap_version=""
for attempt in 1 2 3 4 5 6; do
formula="$(gh api repos/CorvidLabs/homebrew-tap/contents/Formula/fledge.rb --jq '.content' | base64 --decode)"
tap_version="$(printf '%s' "$formula" | sed -n 's/^[[:space:]]*version[[:space:]]*"\([^"]*\)".*/\1/p' | head -n1)"
[ "$tap_version" = "$NEW_VERSION" ] && break
echo "Formula still reports '$tap_version' (attempt $attempt/6); waiting for Contents API to propagate..."
sleep 10
done
if [ "$tap_version" != "$NEW_VERSION" ]; then
echo "::error::homebrew-tap Formula/fledge.rb reports '$tap_version', expected '$NEW_VERSION' after retries β formula sync did not take effect."
exit 1
fi
echo "Verified homebrew-tap Formula/fledge.rb at v$NEW_VERSION."
# crates.io is published out of band; warn (don't fail) if it lags so
# channel drift is at least visible in the run log.
crate_version="$(curl -fsSL -A 'fledge-ci' https://crates.io/api/v1/crates/fledge \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["crate"]["max_version"])' 2>/dev/null || echo "")"
if [ -n "$crate_version" ] && [ "$crate_version" != "$NEW_VERSION" ]; then
echo "::warning::crates.io max_version '$crate_version' != release '$NEW_VERSION' β crates.io publish may be pending or missing."
fi