3535 commit : ${{ steps.identity.outputs.commit }}
3636 version : ${{ steps.identity.outputs.version }}
3737 ci-run-id : ${{ steps.identity.outputs.ci_run_id }}
38+ image-digest : ${{ steps.identity.outputs.image_digest }}
3839 steps :
3940 - name : Check out the exact successful CI commit
4041 uses : actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
7677 sudo apt-get update
7778 sudo apt-get install -y podman
7879
80+ - name : Resolve exact OCI and production dependency cache identities
81+ id : packaging-cache
82+ run : |
83+ set -euo pipefail
84+ builder_image=docker.io/library/node:22
85+ podman pull "$builder_image"
86+ builder_digest="$(podman image inspect "$builder_image" --format '{{.Digest}}' | sed 's/^sha256://')"
87+ node_abi="$(podman run --rm "$builder_image" node -p process.versions.modules)"
88+ case "$(uname -m)" in x86_64|amd64) image_arch=amd64; native_arch=x64 ;; aarch64|arm64) image_arch=arm64; native_arch=arm64 ;; *) exit 1 ;; esac
89+ base_digest="$(sed -n 's/^FROM .*@sha256:\([a-f0-9]\{64\}\)$/\1/p' container/Containerfile.oci)"
90+ containerfile_sha="$(sha256sum container/Containerfile.oci | awk '{print $1}')"
91+ context_sha="$(git ls-files -z container | while IFS= read -r -d '' file; do
92+ case "$file" in container/channel-machine.oci.tar|container/channel-machine.oci.sha256|container/channel-machine.oci.json) continue ;; esac
93+ printf '%s\0' "$file"
94+ sha256sum "$file" | awk '{printf "%s\0", $1}'
95+ done | sha256sum | awk '{print $1}')"
96+ oci_key="$(printf '1helm-channel-image-v1\n%s\n%s\n%s\n%s\n' "$image_arch" "$base_digest" "$containerfile_sha" "$context_sha" | sha256sum | awk '{print $1}')"
97+ dependency_key="$(printf '%s\n%s\n%s\n%s\n%s' "$(sha256sum package-lock.json | awk '{print $1}')" "$(sha256sum config/linux-runtime-package.json | awk '{print $1}')" "$node_abi" "$native_arch" "$builder_digest" | sha256sum | awk '{print $1}')"
98+ [[ "$builder_digest" =~ ^[a-f0-9]{64}$ && "$node_abi" =~ ^[0-9]+$ && "$oci_key" =~ ^[a-f0-9]{64}$ && "$dependency_key" =~ ^[a-f0-9]{64}$ ]]
99+ printf 'oci_key=%s\ndependency_key=%s\n' "$oci_key" "$dependency_key" >> "$GITHUB_OUTPUT"
100+
101+ - name : Restore only the exact sealed OCI cache
102+ uses : actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
103+ with :
104+ path : dist/cache/channel-images
105+ key : 1helm-phase5-channel-image-${{ steps.packaging-cache.outputs.oci_key }}
106+
107+ - name : Restore only the exact production dependency cache
108+ uses : actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
109+ with :
110+ path : dist/cache/production-dependencies
111+ key : 1helm-phase5-production-dependencies-${{ steps.packaging-cache.outputs.dependency_key }}
112+
79113 - name : Build sealed OCI image and ready-to-run Linux archive
80114 env :
81115 HELM_CANDIDATE_REPOSITORY : gitcommit90/1Helm
@@ -91,6 +125,17 @@ jobs:
91125 set -euo pipefail
92126 npm run package:channel-image
93127 npm run package:linux
128+ image_name="$(node -p 'require("./container/channel-machine.oci.json").artifact.name')"
129+ cp container/channel-machine.oci.tar "dist/$image_name"
130+ cp container/channel-machine.oci.json "dist/${image_name%.oci.tar}.json"
131+
132+ - name : Measure split artifact composition and enforce regression budgets
133+ run : |
134+ set -euo pipefail
135+ node scripts/artifact-size-report.mjs \
136+ --json dist/artifact-size-report.json \
137+ --text dist/artifact-size-report.txt \
138+ --check
94139
95140 - name : Generate candidate manifest and evidence
96141 id : identity
@@ -101,22 +146,31 @@ jobs:
101146 set -euo pipefail
102147 version="$(node -p 'require("./package.json").version')"
103148 archive="dist/1Helm-${version}-linux-node.tgz"
149+ offline="dist/1Helm-${version}-linux-node-offline.tgz"
150+ split="dist/1Helm-${version}-linux-split.json"
104151 evidence="dist/candidate-evidence"
105152 mkdir -p "$evidence"
106- HELM_CANDIDATE_ARCHIVE="$archive" HELM_CANDIDATE_MANIFEST="$evidence/candidate.json" \
153+ HELM_CANDIDATE_ARCHIVE="$archive" \
154+ HELM_CANDIDATE_OFFLINE_ARCHIVE="$offline" \
155+ HELM_CANDIDATE_SPLIT_MANIFEST="$split" \
156+ HELM_CANDIDATE_MANIFEST="$evidence/candidate.json" \
107157 node scripts/candidate-manifest.mjs
108158 cp "$archive.sha256" "$evidence/archive.sha256"
109159 sha256sum "$evidence/candidate.json" > "$evidence/manifest.sha256"
110160 printf 'artifact_name=1helm-candidate-%s\n' "$CI_HEAD_SHA" >> "$GITHUB_OUTPUT"
111161 printf 'commit=%s\n' "$CI_HEAD_SHA" >> "$GITHUB_OUTPUT"
112162 printf 'version=%s\n' "$version" >> "$GITHUB_OUTPUT"
113163 printf 'ci_run_id=%s\n' "$HELM_CANDIDATE_CI_RUN_ID" >> "$GITHUB_OUTPUT"
164+ printf 'image_digest=%s\n' "$(node -p 'require("./container/channel-machine.oci.json").sha256')" >> "$GITHUB_OUTPUT"
114165
115166 - name : Attest archive provenance on the hosted builder
116167 id : attest
117168 uses : actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3
118169 with :
119- subject-path : dist/1Helm-*-linux-node.tgz
170+ subject-path : |
171+ dist/1Helm-*-linux-node.tgz
172+ dist/1Helm-*-linux-node-offline.tgz
173+ container/channel-machine.oci.tar
120174
121175 - name : Retain signed provenance bundle
122176 env :
@@ -132,13 +186,29 @@ jobs:
132186 name : ${{ steps.identity.outputs.artifact_name }}
133187 path : |
134188 dist/1Helm-*-linux-node.tgz
189+ dist/1Helm-*-linux-node-offline.tgz
190+ dist/1Helm-*-linux-split.json
191+ dist/artifact-size-report.json
192+ dist/artifact-size-report.txt
193+ container/channel-machine.oci.json
135194 dist/candidate-evidence/candidate.json
136195 dist/candidate-evidence/archive.sha256
137196 dist/candidate-evidence/manifest.sha256
138197 dist/candidate-evidence/provenance.bundle.json
139198 if-no-files-found : error
140199 retention-days : 30
141200
201+ - name : Retain immutable digest-addressed channel image candidate
202+ uses : actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
203+ with :
204+ name : 1helm-channel-image-${{ steps.identity.outputs.image_digest }}
205+ path : |
206+ dist/1Helm-channel-machine-v1-*.oci.tar
207+ dist/1Helm-channel-machine-v1-*.json
208+ container/channel-machine.oci.sha256
209+ if-no-files-found : error
210+ retention-days : 90
211+
142212 build-macos :
143213 name : Build signed notarized exact Mac candidate
144214 if : >-
@@ -259,6 +329,9 @@ jobs:
259329 archive="$(find candidate-download -maxdepth 1 -type f -name '1Helm-*-linux-node.tgz' -print -quit)"
260330 test -n "$archive"
261331 install -m 0600 "$archive" /var/lib/1helm-candidate/inbox/candidate.tgz
332+ offline="$(find candidate-download -maxdepth 1 -type f -name '1Helm-*-linux-node-offline.tgz' -print -quit)"
333+ test -n "$offline"
334+ install -m 0600 "$offline" /var/lib/1helm-candidate/inbox/candidate-offline.tgz
262335 install -m 0600 candidate-download/candidate-evidence/candidate.json /var/lib/1helm-candidate/inbox/candidate.json
263336 install -m 0600 candidate-download/candidate-evidence/provenance.bundle.json /var/lib/1helm-candidate/inbox/provenance.bundle.json
264337 sudo -n /usr/local/sbin/1helm-candidate-install
@@ -318,7 +391,9 @@ jobs:
318391 test "${{ github.event.workflow_run.event }}" = push
319392 test "${{ github.event.workflow_run.head_repository.full_name }}" = "$GITHUB_REPOSITORY"
320393 export HELM_CANDIDATE_ARCHIVE="$(find candidate-download -maxdepth 1 -type f -name '1Helm-*-linux-node.tgz' -print -quit)"
394+ export HELM_CANDIDATE_OFFLINE_ARCHIVE="$(find candidate-download -maxdepth 1 -type f -name '1Helm-*-linux-node-offline.tgz' -print -quit)"
321395 test -n "$HELM_CANDIDATE_ARCHIVE"
396+ test -n "$HELM_CANDIDATE_OFFLINE_ARCHIVE"
322397 bash ops/platform-acceptance/linux.sh
323398
324399 - name : Upload exact Linux acceptance evidence
@@ -431,6 +506,9 @@ jobs:
431506 $archive = Get-ChildItem candidate-download -Filter '1Helm-*-linux-node.tgz' | Select-Object -First 1 -ExpandProperty FullName
432507 if (-not $archive) { throw 'Exact Linux candidate archive is missing.' }
433508 $env:HELM_CANDIDATE_ARCHIVE = $archive
509+ $offline = Get-ChildItem candidate-download -Filter '1Helm-*-linux-node-offline.tgz' | Select-Object -First 1 -ExpandProperty FullName
510+ if (-not $offline) { throw 'Exact Linux offline candidate archive is missing.' }
511+ $env:HELM_CANDIDATE_OFFLINE_ARCHIVE = $offline
434512 & .\ops\platform-acceptance\windows.ps1
435513 if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
436514
@@ -463,6 +541,8 @@ jobs:
463541
464542 - uses : actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
465543 with : { name: "${{ needs.build.outputs.artifact-name }}", path: candidate-download }
544+ - uses : actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
545+ with : { name: "1helm-channel-image-${{ needs.build.outputs.image-digest }}", path: channel-image-download }
466546 - uses : actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
467547 with : { name: "${{ needs.build-macos.outputs.artifact-name }}", path: mac-candidate-download }
468548 - uses : actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
@@ -477,6 +557,7 @@ jobs:
477557 - name : Assemble only the retained complete matrix and evidence
478558 env :
479559 HELM_CANDIDATE_DOWNLOAD : candidate-download
560+ HELM_CHANNEL_IMAGE_DOWNLOAD : channel-image-download
480561 HELM_MAC_CANDIDATE_DOWNLOAD : mac-candidate-download
481562 HELM_REHEARSAL_EVIDENCE : rehearsal-download/dress-rehearsal.json
482563 HELM_LINUX_ACCEPTANCE_EVIDENCE : linux-acceptance-download/linux-acceptance.json
0 commit comments