Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions .github/workflows/baked-tools-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ jobs:
name: Validate baked tools in image
runs-on: ubuntu-latest
timeout-minutes: 20
# #323: lockstep-heal needs contents:write to push the sync commit to the PR head
permissions:
contents: write
checks: read

steps:
- name: Checkout builder repository
Expand All @@ -37,6 +41,10 @@ jobs:
fetch-depth: 1

- name: Manifest <-> Dockerfile lockstep gate
id: gate
# #323: let the heal step run on mismatch; a hard-fail step would stop the job here.
# The 'Fail if still red' step below re-asserts the red when healing is impossible.
continue-on-error: true
run: |
python3 - << 'GATE'
import json, re, sys
Expand Down Expand Up @@ -81,6 +89,100 @@ jobs:
print('Lockstep OK: all Dockerfile pins match baked-tools.json')
GATE

# #323 lockstep-heal: when the ONLY failures are 'Dockerfile pin X != baked-tools.json Y'
# (Dockerfile leads — dependabot FROM bumps, manual @getpaseo bumps) and the PR did
# NOT hand-edit baked-tools.json, sync baked-tools.json to the Dockerfile pins and
# push to the PR head. This run then fails intentionally ('HEALED — rerun verifies');
# the pushed commit re-triggers the checks and auto-merge proceeds on the new SHA.
# Never heals the reverse direction (baked-tools.json leading) — that needs a human.
- name: Checkout PR head (for heal)
id: healsrc
if: steps.gate.outcome == 'failure'
uses: actions/checkout@v7
with:
ref: ${{ github.event.pull_request.head.ref }}
path: healsrc
fetch-depth: 1

- name: Heal lockstep drift (Dockerfile pin leads)
id: heal
if: steps.gate.outcome == 'failure'
run: |
set -euo pipefail
if [ "${{ github.event_name }}" != "pull_request" ] \
|| [ "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]; then
echo "not healable (fork PR or non-PR context) — leaving red for a human"
exit 0
fi
HEAD_BRANCH="${{ github.event.pull_request.head.ref }}"
if gh api "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" \
--paginate --jq '.[].filename' 2>/dev/null | grep -qx 'managed-tools/baked-tools.json'; then
echo "PR touches baked-tools.json — heal disabled (human edit in flight)"
exit 0
fi
cd healsrc
set +e
python3 - <<'HEAL'
import json, re, sys
df = open('Dockerfile.dockerfile').read()
path = 'managed-tools/baked-tools.json'
data = json.load(open(path))
manifest = {t['name']: t for t in data['tools']}
pins = {
'docker': (r'FROM docker:([0-9][\w.]*)-dind', None),
'dockerd': (r'FROM docker:([0-9][\w.]*)-dind', None),
'paseo': (r'@getpaseo/cli@([0-9][\w.]*)', r'@getpaseo/server@([0-9][\w.]*)'),
'paseo-relay': (r'ARG PASEO_RELAY_VERSION=([0-9][\w.]*)', None),
'code-server': (r'ARG CODE_SERVER_VERSION=([0-9][\w.]*)', None),
}
mismatch = re.compile(r'^(\S+): (?:secondary )?Dockerfile pin ([0-9][\w.]*) != baked-tools\.json ([0-9][\w.]*)$')
healed = []
only_mismatches = True
# recompute failures exactly like the gate, but capture them for classification
for tool, (rx, rx2) in pins.items():
if tool not in manifest:
only_mismatches = False; continue
want = manifest[tool]['currentVersion']
got = re.search(rx, df)
if not got:
only_mismatches = False; continue
if got.group(1) != want:
healed.append((tool, got.group(1), want))
if rx2:
got2 = re.search(rx2, df)
if got2 and got2.group(1) != want:
pass # same tool, already captured above
if not only_mismatches or not healed:
print('no healable drift (non-mismatch failures or clean) — leaving red')
sys.exit(1)
for tool, new, old in healed:
manifest[tool]['currentVersion'] = new
print(f'healed {tool}: {old} -> {new} (Dockerfile leads)')
json.dump(data, open(path, 'w'), indent=2)
open(path, 'a').write('\n')
sys.exit(42)
HEAL
rc=$?
set -e
if [ "$rc" -ne 42 ]; then
echo "heal: nothing pushed (rc=$rc)"
exit 0
fi
git config user.name "lockstep-heal[bot]"
git config user.email "action@github.com"
git add managed-tools/baked-tools.json
git commit -q -m "chore(baked-tools): lockstep heal — sync baked-tools.json to Dockerfile pins (automated, #323)"
git push origin "HEAD:refs/heads/$HEAD_BRANCH"
echo "pushed=true" >> "$GITHUB_OUTPUT"
echo "HEALED — sync commit pushed; this run fails intentionally so nothing merges on the pre-heal SHA."
exit 1

- name: Fail if still red and not healed
if: steps.gate.outcome == 'failure' && steps.heal.outputs.pushed != 'true'
run: |
echo "Lockstep gate failed and healing did not apply — see gate output above."
exit 1

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4

Expand Down
Loading