Skip to content

fix(phase4): repair candidate artifact layout and guard Linux acceptance - #75

Merged
gitcommit90 merged 1 commit into
mainfrom
fix/phase4-conveyor-belt
Aug 4, 2026
Merged

fix(phase4): repair candidate artifact layout and guard Linux acceptance#75
gitcommit90 merged 1 commit into
mainfrom
fix/phase4-conveyor-belt

Conversation

@gitcommit90

@gitcommit90 gitcommit90 commented Aug 4, 2026

Copy link
Copy Markdown
Owner

Why

The first full three-platform candidate run (30958024284) failed in every downstream lane. Root cause: the Linux candidate and channel-image artifacts each mixed dist/... and container/... upload paths. actions/upload-artifact roots the archive at the least-common-ancestor of its inputs, so a single container/ entry pushed the root to the repo root and nested every file under dist/ on download. Consumers read the download root, so:

  • accept-linux, accept-windows, and the Phase 2 deploy all reported "exact Linux candidate archive is missing"
  • the promotion assembler would have failed identically on the channel image

Changes

  1. Single-root both artifacts. Remove 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 and no consumer reads them from these artifacts. Exact bytes unchanged; no rebuild.
  2. Clear the retained self-hosted rehearsal workspace before download so a stale prior candidate can't mask a future layout error.
  3. Guard ops/platform-acceptance/linux.sh. It does a real root install (port 8123, /var/lib/1helm-oci-v1, systemd units) — only safe on a disposable runner. It now refuses unless RUNNER_ENVIRONMENT=github-hosted, no 1Helm/standalone host state pre-exists, and port 8123 is free. This encodes the prior production-clobber lesson without changing the shipped installer's hard-coded paths.

Out-of-band (already done)

The dedicated Phase 2 rehearsal host (LXC 101) had a stale pre-split installer helper; its /usr/local/sbin/1helm-candidate-install and boundary validator were updated to match the tracked versions, and its stale inbox was cleared.

Verification

  • node --test phase2/phase4/phase5 — 22/22 pass
  • npm run ci — 175 pass, 2 skipped, 0 fail
  • bash -n and YAML parse clean
  • New assertions lock in single-rooted uploads, the workspace clear, and the Linux guard

Scope

No release, tag, version bump, website deploy, or production change. Does not alter enablement variables.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Candidate artifacts now exclude OCI metadata and checksum files.
    • Deployment processing clears previous candidate data before handling new artifacts.
    • Channel image artifacts are uploaded using the correct path without unnecessary nesting.
  • Reliability

    • Linux acceptance checks now require a clean, supported runner environment and an available service port.
    • Expanded validation helps ensure candidate and acceptance workflows produce the expected artifacts.

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: Claude <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The candidate workflow now excludes OCI metadata and checksum files, clears prior candidate directories, and validates artifact paths. Linux acceptance now requires a GitHub-hosted runner, clean 1Helm state paths, and an unused port 8123.

Changes

Candidate workflow and platform acceptance

Layer / File(s) Summary
Candidate artifact handling
.github/workflows/candidate.yml, test/phase2-candidate.mjs
The workflow removes OCI metadata and checksum files, clears prior candidate directories, and tests the exact upload contents and paths.
Linux acceptance preflight
ops/platform-acceptance/linux.sh, test/phase4-platform-acceptance.mjs
Linux acceptance checks the hosted-runner environment, 1helm-standalone marker, clean state paths, free port 8123, and channel-image placement.

Estimated code review effort: 2 (Simple) | ~15 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the artifact layout fix and Linux acceptance safety guards.
Description check ✅ Passed The description clearly explains the problem, changes, scope, acceptance evidence, and verification results, despite not using every template heading.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/phase4-conveyor-belt

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ops/platform-acceptance/linux.sh`:
- Around line 40-43: Update the port validation before the root installation in
the Linux acceptance script to fail closed when ss is unavailable or its
inspection command fails. Require ss explicitly, run ss port inspection
separately, handle any nonzero status by printing an error and exiting, and only
then use grep to reject an occupied port 8123.

In `@test/phase4-platform-acceptance.mjs`:
- Around line 118-120: Extend the channelUpload assertions in the phase4
acceptance test to verify the complete metadata contract: match the retained
dist/1Helm-channel-machine-v1-*.json metadata file and assert that the
.oci.sha256 sidecar is absent, while preserving the existing single-root and OCI
tar path checks.
- Around line 121-124: Update the Linux acceptance assertions in the phase4
platform test to require the guarded host-state paths /var/lib/1helm-oci-v1 and
/opt/1helm, in addition to the existing 1helm-standalone check. Keep the current
RUNNER_ENVIRONMENT and port-8123 assertions unchanged so the test covers every
host-state rejection path in the Linux acceptance script.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 51199e68-4992-4e42-bbe5-ae78e83b56c4

📥 Commits

Reviewing files that changed from the base of the PR and between 1c35aa4 and 78755be.

📒 Files selected for processing (4)
  • .github/workflows/candidate.yml
  • ops/platform-acceptance/linux.sh
  • test/phase2-candidate.mjs
  • test/phase4-platform-acceptance.mjs

Comment on lines +40 to +43
if command -v ss >/dev/null 2>&1 && ss -ltn 2>/dev/null | grep -qE '[:.]8123[[:space:]]'; then
echo "Linux acceptance refuses to run while port 8123 is already in use." >&2
exit 1
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Fail closed when port inspection is unavailable.

If ss is absent or returns an error, the if condition is false and the script continues to Line 64, where it performs the root installation. The runner can therefore proceed while port 8123 is occupied. Require ss and handle inspection errors before running grep.

Proposed fix
-if command -v ss >/dev/null 2>&1 && ss -ltn 2>/dev/null | grep -qE '[:.]8123[[:space:]]'; then
+command -v ss >/dev/null 2>&1 \
+  || { echo "Linux acceptance cannot verify port 8123 because ss is unavailable." >&2; exit 1; }
+ports="$(ss -ltn 2>/dev/null)" \
+  || { echo "Linux acceptance cannot verify port 8123." >&2; exit 1; }
+if grep -qE '[:.]8123[[:space:]]' <<<"$ports"; then
   echo "Linux acceptance refuses to run while port 8123 is already in use." >&2
   exit 1
 fi

As per PR objectives, Linux acceptance must verify that port 8123 is available before root installation.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if command -v ss >/dev/null 2>&1 && ss -ltn 2>/dev/null | grep -qE '[:.]8123[[:space:]]'; then
echo "Linux acceptance refuses to run while port 8123 is already in use." >&2
exit 1
fi
command -v ss >/dev/null 2>&1 \
|| { echo "Linux acceptance cannot verify port 8123 because ss is unavailable." >&2; exit 1; }
ports="$(ss -ltn 2>/dev/null)" \
|| { echo "Linux acceptance cannot verify port 8123." >&2; exit 1; }
if grep -qE '[:.]8123[[:space:]]' <<<"$ports"; then
echo "Linux acceptance refuses to run while port 8123 is already in use." >&2
exit 1
fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ops/platform-acceptance/linux.sh` around lines 40 - 43, Update the port
validation before the root installation in the Linux acceptance script to fail
closed when ss is unavailable or its inspection command fails. Require ss
explicitly, run ss port inspection separately, handle any nonzero status by
printing an error and exiting, and only then use grep to reject an occupied port
8123.

Comment on lines +118 to +120
const channelUpload = workflow.match(/- name: Retain immutable digest-addressed channel image candidate[\s\S]*?retention-days: 90/)?.[0] || "";
assert.doesNotMatch(channelUpload, /container\//);
assert.match(channelUpload, /dist\/1Helm-channel-machine-v1-\*\.oci\.tar/);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Assert the complete channel-image metadata contract.

The test checks only the OCI tar path. The workflow at .github/workflows/candidate.yml Lines 205-206 retains dist/1Helm-channel-machine-v1-*.json and removes the .oci.sha256 sidecar. Assert both conditions so a metadata regression cannot pass this test.

Proposed assertions
   assert.doesNotMatch(channelUpload, /container\//);
   assert.match(channelUpload, /dist\/1Helm-channel-machine-v1-\*\.oci\.tar/);
+  assert.match(channelUpload, /dist\/1Helm-channel-machine-v1-\*\.json/);
+  assert.doesNotMatch(channelUpload, /channel-machine\.oci\.sha256/);

As per PR objectives, the channel-image artifact must retain its intended single-root layout and metadata.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const channelUpload = workflow.match(/- name: Retain immutable digest-addressed channel image candidate[\s\S]*?retention-days: 90/)?.[0] || "";
assert.doesNotMatch(channelUpload, /container\//);
assert.match(channelUpload, /dist\/1Helm-channel-machine-v1-\*\.oci\.tar/);
const channelUpload = workflow.match(/- name: Retain immutable digest-addressed channel image candidate[\s\S]*?retention-days: 90/)?.[0] || "";
assert.doesNotMatch(channelUpload, /container\//);
assert.match(channelUpload, /dist\/1Helm-channel-machine-v1-\*\.oci\.tar/);
assert.match(channelUpload, /dist\/1Helm-channel-machine-v1-\*\.json/);
assert.doesNotMatch(channelUpload, /channel-machine\.oci\.sha256/);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/phase4-platform-acceptance.mjs` around lines 118 - 120, Extend the
channelUpload assertions in the phase4 acceptance test to verify the complete
metadata contract: match the retained dist/1Helm-channel-machine-v1-*.json
metadata file and assert that the .oci.sha256 sidecar is absent, while
preserving the existing single-root and OCI tar path checks.

Comment on lines +121 to +124
const linuxAccept = read("ops/platform-acceptance/linux.sh");
assert.match(linuxAccept, /RUNNER_ENVIRONMENT.*==.*"github-hosted"/);
assert.match(linuxAccept, /1helm-standalone/);
assert.match(linuxAccept, /refuses to run while port 8123 is already in use/);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Assert every guarded host-state path.

The test checks only 1helm-standalone. It does not require /var/lib/1helm-oci-v1 or /opt/1helm from ops/platform-acceptance/linux.sh Lines 36-39. A regression could remove either guard while this test still passes.

Proposed assertions
   assert.match(linuxAccept, /RUNNER_ENVIRONMENT.*==.*"github-hosted"/);
-  assert.match(linuxAccept, /1helm-standalone/);
+  assert.match(linuxAccept, /\/var\/lib\/1helm-oci-v1/);
+  assert.match(linuxAccept, /\/var\/lib\/1helm-standalone/);
+  assert.match(linuxAccept, /\/opt\/1helm/);
   assert.match(linuxAccept, /refuses to run while port 8123 is already in use/);

As per PR objectives, Linux acceptance must reject existing 1Helm and standalone host state.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const linuxAccept = read("ops/platform-acceptance/linux.sh");
assert.match(linuxAccept, /RUNNER_ENVIRONMENT.*==.*"github-hosted"/);
assert.match(linuxAccept, /1helm-standalone/);
assert.match(linuxAccept, /refuses to run while port 8123 is already in use/);
const linuxAccept = read("ops/platform-acceptance/linux.sh");
assert.match(linuxAccept, /RUNNER_ENVIRONMENT.*==.*"github-hosted"/);
assert.match(linuxAccept, /\/var\/lib\/1helm-oci-v1/);
assert.match(linuxAccept, /\/var\/lib\/1helm-standalone/);
assert.match(linuxAccept, /\/opt\/1helm/);
assert.match(linuxAccept, /refuses to run while port 8123 is already in use/);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/phase4-platform-acceptance.mjs` around lines 121 - 124, Update the Linux
acceptance assertions in the phase4 platform test to require the guarded
host-state paths /var/lib/1helm-oci-v1 and /opt/1helm, in addition to the
existing 1helm-standalone check. Keep the current RUNNER_ENVIRONMENT and
port-8123 assertions unchanged so the test covers every host-state rejection
path in the Linux acceptance script.

@gitcommit90
gitcommit90 merged commit 5c23cdc into main Aug 4, 2026
6 checks passed
@gitcommit90
gitcommit90 deleted the fix/phase4-conveyor-belt branch August 4, 2026 23:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant