Skip to content

Commit 5c23cdc

Browse files
gitcommit90claude
andauthored
fix(phase4): repair candidate artifact layout and guard Linux acceptance (#75)
The first full three-platform candidate run failed in every downstream lane from one root cause: the Linux candidate and channel-image artifacts each mixed `dist/...` and `container/...` upload paths. actions/upload-artifact uses the least-common-ancestor of its inputs as the archive root, so a single `container/` entry made the root the repository root and nested every file under `dist/` on download. Consumers look at the download root, so Linux, Windows, and Phase 2 all reported "candidate archive is missing", and the promotion assembler would have failed the same way on the channel image. Remove the redundant `container/channel-machine.oci.json` from the candidate artifact and `container/channel-machine.oci.sha256` from the channel-image artifact. Both duplicates remain retained elsewhere (the immutable channel image artifact and each release's own container tree); no consumer reads them from these artifacts. The exact candidate and image bytes are unchanged. Also clear the retained self-hosted dress-rehearsal workspace before download so a stale prior candidate can never mask a layout error again. Harden ops/platform-acceptance/linux.sh: it performs a real root install (port 8123, /var/lib/1helm-oci-v1, systemd units), which is only safe on a disposable hosted runner. Refuse unless RUNNER_ENVIRONMENT=github-hosted, no 1Helm/standalone host state already exists, and port 8123 is free. This encodes the prior production-clobber lesson without changing the shipped installer's paths. Tests updated to assert single-rooted uploads, the workspace clear, and the Linux isolation guard. Full `npm run ci` passes. No release, tag, version bump, website deploy, or production change. Co-authored-by: Joseph Yaksich <gitcommit90@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 1c35aa4 commit 5c23cdc

4 files changed

Lines changed: 35 additions & 2 deletions

File tree

.github/workflows/candidate.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,6 @@ jobs:
190190
dist/1Helm-*-linux-split.json
191191
dist/artifact-size-report.json
192192
dist/artifact-size-report.txt
193-
container/channel-machine.oci.json
194193
dist/candidate-evidence/candidate.json
195194
dist/candidate-evidence/archive.sha256
196195
dist/candidate-evidence/manifest.sha256
@@ -205,7 +204,6 @@ jobs:
205204
path: |
206205
dist/1Helm-channel-machine-v1-*.oci.tar
207206
dist/1Helm-channel-machine-v1-*.json
208-
container/channel-machine.oci.sha256
209207
if-no-files-found: error
210208
retention-days: 90
211209

@@ -308,6 +306,9 @@ jobs:
308306
actions: read
309307
attestations: read
310308
steps:
309+
- name: Clear retained runner workspace from prior candidates
310+
run: rm -rf -- candidate-download candidate-result
311+
311312
- name: Download this workflow's exact candidate
312313
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
313314
with:

ops/platform-acceptance/linux.sh

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,23 @@ export HELM_ACCEPTANCE_STARTED_AT="$STARTED_AT"
2424
export HELM_PHASE4_RUNNER_LABEL=ubuntu-latest
2525
node "$ROOT/scripts/pending-acceptance-evidence.mjs"
2626
[[ "$(id -u)" -ne 0 ]] || { echo "Linux acceptance must begin as the hosted ordinary runner user." >&2; exit 1; }
27+
28+
# This lane performs a REAL root install of 1Helm on its runner: it binds port
29+
# 8123, writes /var/lib/1helm-oci-v1, and installs the 1helm systemd units. That
30+
# is only safe on a disposable GitHub-hosted runner that holds no user or
31+
# production data. Refuse anywhere that looks persistent, self-hosted, or
32+
# already-inhabited so a misrouted job can never clobber a real host's live
33+
# 1Helm or standalone state. Blocked evidence was already retained above.
34+
[[ "${RUNNER_ENVIRONMENT:-}" == "github-hosted" ]] \
35+
|| { echo "Linux acceptance refuses to boot a real 1Helm outside a disposable GitHub-hosted runner." >&2; exit 1; }
36+
for guarded in /var/lib/1helm-oci-v1 /var/lib/1helm-standalone /opt/1helm; do
37+
[[ ! -e "$guarded" ]] \
38+
|| { echo "Linux acceptance refuses to run where 1Helm host state already exists: $guarded" >&2; exit 1; }
39+
done
40+
if command -v ss >/dev/null 2>&1 && ss -ltn 2>/dev/null | grep -qE '[:.]8123[[:space:]]'; then
41+
echo "Linux acceptance refuses to run while port 8123 is already in use." >&2
42+
exit 1
43+
fi
2744
[[ "$(sha256sum "$ARCHIVE" | awk '{print $1}')" == "$DIGEST" ]] \
2845
|| { echo "Linux candidate digest mismatch." >&2; exit 1; }
2946
[[ "$(sha256sum "$OFFLINE_ARCHIVE" | awk '{print $1}')" == "$OFFLINE_DIGEST" ]] \

test/phase2-candidate.mjs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,17 +115,22 @@ test("rollback fixtures remain local-only and cannot satisfy normal candidate va
115115

116116
test("candidate workflow and guest boundary exclude PR code and broad root access", () => {
117117
const workflow = read(".github/workflows/candidate.yml");
118+
const candidateUpload = workflow.match(/- name: Upload exact candidate and evidence[\s\S]*?retention-days: 30/)?.[0] || "";
118119
const helper = read("ops/dress-rehearsal/1helm-candidate-install");
119120
const hook = read("ops/dress-rehearsal/runner-job-started.sh");
120121
const sudoersExample = "%actions ALL=(root) NOPASSWD: /usr/local/sbin/1helm-candidate-install \"\"\n";
121122
assert.match(workflow, /workflow_run:[\s\S]*workflows: \[CI\][\s\S]*branches: \[main\]/);
122123
assert.match(workflow, /workflow_run\.event == 'push'/);
123124
assert.match(workflow, /head_repository\.full_name == github\.repository/);
124125
assert.match(workflow, /runs-on: \[1helm-dress-rehearsal-phase2\]/);
126+
assert.match(workflow, /Clear retained runner workspace[\s\S]*rm -rf -- candidate-download candidate-result[\s\S]*Download this workflow's exact candidate/);
125127
assert.match(workflow, /github\.sha == github\.event\.workflow_run\.head_sha/);
126128
assert.match(workflow, /attest-build-provenance@[a-f0-9]{40}/);
127129
assert.match(workflow, /candidate-download\/candidate-evidence\/candidate\.json/);
128130
assert.match(workflow, /candidate-download\/candidate-evidence\/provenance\.bundle\.json/);
131+
assert.match(candidateUpload, /dist\/1Helm-\*-linux-node\.tgz/);
132+
assert.match(candidateUpload, /dist\/candidate-evidence\/candidate\.json/);
133+
assert.doesNotMatch(candidateUpload, /container\/channel-machine\.oci\.json/);
129134
assert.match(helper, /--signer-workflow gitcommit90\/1Helm\/\.github\/workflows\/candidate\.yml/);
130135
assert.match(helper, /--source-ref refs\/heads\/main/);
131136
assert.match(helper, /--source-digest "\$commit"/);

test/phase4-platform-acceptance.mjs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,16 @@ test("workflow routes no PR/fork code, uses unique labels, fans acceptance out,
112112
assert.doesNotMatch(workflow, /pull_request_target|workflow_dispatch/);
113113
assert.doesNotMatch(workflow.match(/assemble-promotion:[\s\S]*?(?=\n candidate-status:)/)?.[0] || "", /npm (ci|install|run build|run package)/);
114114
assert.match(workflow, /Upload exact Linux acceptance evidence\n if: always\(\)/);
115+
// Both retained artifacts must be single-rooted under dist/ so consumers find
116+
// files at the download root, not nested under dist/ (the layout bug that
117+
// broke Linux, Windows, and Phase 2 candidate discovery in one run).
118+
const channelUpload = workflow.match(/- name: Retain immutable digest-addressed channel image candidate[\s\S]*?retention-days: 90/)?.[0] || "";
119+
assert.doesNotMatch(channelUpload, /container\//);
120+
assert.match(channelUpload, /dist\/1Helm-channel-machine-v1-\*\.oci\.tar/);
121+
const linuxAccept = read("ops/platform-acceptance/linux.sh");
122+
assert.match(linuxAccept, /RUNNER_ENVIRONMENT.*==.*"github-hosted"/);
123+
assert.match(linuxAccept, /1helm-standalone/);
124+
assert.match(linuxAccept, /refuses to run while port 8123 is already in use/);
115125
assert.match(workflow.match(/accept-macos:[\s\S]*?(?=\n accept-windows:)/)?.[0] || "", /if: always\(\)[\s\S]*name: 1helm-macos-acceptance-/);
116126
assert.match(workflow.match(/accept-windows:[\s\S]*?(?=\n assemble-promotion:)/)?.[0] || "", /if: always\(\)[\s\S]*name: 1helm-windows-acceptance-/);
117127
});

0 commit comments

Comments
 (0)