-
Notifications
You must be signed in to change notification settings - Fork 27
244 lines (216 loc) · 11.1 KB
/
Copy pathbump-opencode.yml
File metadata and controls
244 lines (216 loc) · 11.1 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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
name: Bump OpenCode pin
# Weekly cron + manual dispatch. Compares the version in
# .github/opencode-version.txt against the latest published OpenCode release.
# If they drift, opens a PR bumping the pin. The PR triggers the full Tests
# workflow (Linux/macOS/Windows units + Linux/macOS/Windows E2E), so a bad
# OpenCode release fails on the PR before merge instead of breaking main.
#
# Why this exists: all three E2E harnesses (Linux Docker, macOS native,
# Windows native) read the pin from .github/opencode-version.txt to avoid
# the GitHub API anonymous rate-limit on macOS runners (60 req/hr per
# egress IP — repeatedly hit on shared macOS infra). Pinning is the right
# call for stability, but pinning forever means we silently drift behind
# upstream and miss real OpenCode regressions. This workflow is the
# auto-refresh that closes the loop.
#
# This workflow uses `gh` with the default GITHUB_TOKEN which gets the
# 1000 req/hr authenticated GitHub API budget, so it never hits the
# anonymous rate limit the OpenCode installer does.
#
# TOKENS: the org blocks `GITHUB_TOKEN` from creating pull requests
# outright ("GitHub Actions is not permitted to create or approve pull
# requests"), so the PR step mints a CK CI app token scoped to this repo
# and uses it for both the branch push and `gh pr create`. App-token PR
# events trigger downstream workflows normally, so Tests runs as a real
# PR check. If the app secrets are ever removed, this lane fails at the
# mint step with a clear error rather than at PR creation.
on:
schedule:
# Every Monday 06:00 UTC. Far enough from typical release Friday
# evenings that any urgent post-release fixes are usually in.
- cron: "0 6 * * 1"
workflow_dispatch: {}
permissions:
contents: write
pull-requests: write
jobs:
bump:
name: Check for OpenCode upstream drift
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Read current pin
id: current
run: |
version=$(tr -d '[:space:]' < .github/opencode-version.txt)
if [ -z "$version" ]; then
echo "::error::.github/opencode-version.txt is empty"
exit 1
fi
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "Current pin: $version"
- name: Resolve latest OpenCode release
id: latest
env:
# gh uses GITHUB_TOKEN automatically; explicit env makes the auth
# boundary visible in logs and lets `gh` skip its login probe.
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# The OpenCode installer hits this same endpoint anonymously
# (see https://opencode.ai/install). With auth we get 1000 req/hr,
# so this never hits the rate limit that motivates the pin.
tag=$(gh api repos/anomalyco/opencode/releases/latest --jq '.tag_name')
if [ -z "$tag" ] || [ "$tag" = "null" ]; then
echo "::error::Failed to resolve latest OpenCode release tag"
exit 1
fi
# Strip leading 'v' if present (1.14.39 not v1.14.39).
version="${tag#v}"
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "Latest release: $version (tag: $tag)"
- name: Compare versions
id: compare
run: |
if [ "${{ steps.current.outputs.version }}" = "${{ steps.latest.outputs.version }}" ]; then
echo "Pin is already at latest. Nothing to do."
echo "drift=false" >> "$GITHUB_OUTPUT"
else
echo "Drift: pin=${{ steps.current.outputs.version }} latest=${{ steps.latest.outputs.version }}"
echo "drift=true" >> "$GITHUB_OUTPUT"
fi
- name: Update pin file
if: steps.compare.outputs.drift == 'true'
run: |
echo "${{ steps.latest.outputs.version }}" > .github/opencode-version.txt
# Org policy blocks GITHUB_TOKEN from creating pull requests
# ("GitHub Actions is not permitted to create or approve pull
# requests"), which made every run of this lane fail at the PR step
# for its entire history. The CK CI app can create PRs, and PR events
# from an app token trigger downstream workflows the way PAT events
# do, so mint an app token scoped to this repo and use it for both
# the branch push and `gh pr create`.
- name: Mint PR token
if: steps.compare.outputs.drift == 'true'
id: pr_token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.CK_CI_APP_ID }}
private-key: ${{ secrets.CK_CI_APP_PRIVATE_KEY }}
owner: cortexkit
repositories: aft
- name: Open PR
if: steps.compare.outputs.drift == 'true'
id: pr
env:
GH_TOKEN: ${{ steps.pr_token.outputs.token }}
PAT_PRESENT: "yes"
run: |
set -euo pipefail
old="${{ steps.current.outputs.version }}"
new="${{ steps.latest.outputs.version }}"
branch="ci/bump-opencode-${new}"
# If the bump branch already exists (we opened the same PR previously
# and it's still open / unmerged), just refresh it. Otherwise create
# a new branch.
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
# Push using the same token we'll use for `gh pr create`. When PAT is
# set, this writes the branch as the PAT user and triggers PR events
# normally. With the default GITHUB_TOKEN, the push lands but won't
# trigger Tests via the `pull_request` event — see the dispatch step
# below.
remote_url="https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
git remote set-url origin "$remote_url"
git checkout -b "$branch"
git add .github/opencode-version.txt
git commit -m "ci(opencode): bump pinned OpenCode to v${new}
Auto-generated by .github/workflows/bump-opencode.yml.
Bumps .github/opencode-version.txt from ${old} -> ${new}.
All three E2E harnesses (Linux Docker, macOS native, Windows native)
read this file, so PR-time CI exercises the new OpenCode version on
every supported platform before merge. If any platform regresses,
the PR will fail and you'll know immediately."
git push -f origin "$branch"
echo "branch=$branch" >> "$GITHUB_OUTPUT"
# Surface PAT presence to downstream steps via this step's outputs
# so we don't have to re-evaluate the secret in another step (where
# secret-conditional logic is awkward).
if [ -n "${PAT_PRESENT:-}" ]; then
echo "trigger_note=_Tests is triggered automatically by the \`pull_request\` event (PAT in use)._" >> "$GITHUB_OUTPUT"
echo "needs_dispatch=false" >> "$GITHUB_OUTPUT"
else
echo "trigger_note=_PR opened with \`GITHUB_TOKEN\`, which does NOT trigger downstream workflows. The bump job dispatches Tests separately via \`workflow_dispatch\` against this branch — look for the matching \"Tests\" run in the Actions tab. For PR-driven runs that appear as a check on this PR itself, set the \`BUMP_OPENCODE_PAT\` repo secret to a PAT with \`repo\` + \`workflow\` scope._" >> "$GITHUB_OUTPUT"
echo "needs_dispatch=true" >> "$GITHUB_OUTPUT"
fi
# Open the PR (or surface the existing one if open). Body lists the
# release notes link so reviewer can scan upstream changes quickly.
existing=$(gh pr list --head "$branch" --state open --json number --jq '.[0].number' || echo "")
if [ -n "$existing" ]; then
echo "PR already open: #$existing — branch was force-pushed."
exit 0
fi
gh pr create \
--head "$branch" \
--base main \
--title "ci(opencode): bump pinned OpenCode to v${new}" \
--body "$(cat <<EOF
Automated bump of \`.github/opencode-version.txt\` from \`${old}\` to \`${new}\`.
Upstream release notes: https://github.com/anomalyco/opencode/releases/tag/v${new}
### What this changes
All three E2E harnesses now install OpenCode v${new}:
- \`tests/docker/Dockerfile.linux-x64\` (Linux Docker E2E)
- \`tests/macos-e2e/run.sh\` (macOS native E2E)
- \`tests/windows-e2e/run.ps1\` (Windows native E2E)
### Merge gate
The Tests workflow on this PR exercises the new version against the
full E2E matrix. If any platform regresses, **do not merge** — file
an upstream issue or hold the bump until upstream fixes the
regression. Pin will stay at \`${old}\` until this PR is merged.
${PAT_PRESENT:+_Tests runs automatically because this PR was opened with a PAT._}
${PAT_PRESENT:-_PR opened with default \`GITHUB_TOKEN\`. Tests does not auto-run on \`pull_request\` here; the bump workflow dispatched Tests via \`workflow_dispatch\` against this branch instead — see the Actions tab. Set \`BUMP_OPENCODE_PAT\` secret for PR-driven Tests checks._}
EOF
)"
# When we don't have a PAT, the `pull_request` event from the bump PR
# won't trigger Tests. Dispatch it explicitly so the bumped pin still
# gets exercised end-to-end. Skipped when a PAT is configured because
# the PR-event trigger covers it.
- name: Dispatch Tests on bump branch (GITHUB_TOKEN fallback)
if: steps.compare.outputs.drift == 'true' && steps.pr.outputs.needs_dispatch == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
branch="${{ steps.pr.outputs.branch }}"
if [ -z "$branch" ]; then
echo "::error::No bump branch recorded; cannot dispatch Tests."
exit 1
fi
# `workflow_dispatch` is the documented exception that lets
# GITHUB_TOKEN trigger another workflow run. The dispatched run
# appears in the Actions tab tagged to this branch but is NOT
# attached as a PR check. That's fine — the cron+human review
# is the merge gate, not branch protection.
dispatch_started=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
gh workflow run tests.yml --ref "$branch" \
-f ref="$branch"
echo "Waiting for Tests workflow dispatch to register..."
run_id=""
for attempt in 1 2 3 4 5 6; do
run_id=$(gh run list \
--workflow tests.yml \
--branch "$branch" \
--event workflow_dispatch \
--limit 20 \
--json databaseId,createdAt \
--jq "map(select(.createdAt >= \"${dispatch_started}\")) | .[0].databaseId // \"\"")
if [ -n "$run_id" ]; then
break
fi
echo "Tests run not visible yet (attempt $attempt/6); retrying..."
sleep 5
done
if [ -z "$run_id" ]; then
echo "::error::Tests dispatch did not create a workflow_dispatch run for $branch."
exit 1
fi
echo "Dispatched Tests run: $run_id"