ci: attest release artifacts before publication - #138
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
|
@coderabbitai review |
|
🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
base='87ac0e08cceed3d1a766da13a8f8123912178192'
head='dae9186141d1a76853225f0c377d12b23b747977'
printf '%s\n' '== Repository and commit validation =='
git rev-parse --show-toplevel
git cat-file -e "${base}^{commit}"
git cat-file -e "${head}^{commit}"
git merge-base --is-ancestor "$base" "$head"
printf 'base=%s\nhead=%s\n' "$(git rev-parse "$base")" "$(git rev-parse "$head")"
printf '%s\n' '== Changed paths (exact range) =='
git diff --name-status "$base" "$head"
printf '%s\n' '== Changed-file count =='
git diff --name-only "$base" "$head" | wc -l
printf '%s\n' '== Changed file outlines =='
git diff --name-only "$base" "$head" | while IFS= read -r file; do
printf '\n--- %s ---\n' "$file"
case "$file" in
*.yml|*.yaml) ast-grep outline "$file" --lang yaml --items all 2>/dev/null || true ;;
*.sh) ast-grep outline "$file" --lang bash --items all 2>/dev/null || true ;;
*.js|*.mjs|*.cjs) ast-grep outline "$file" --lang javascript --items all 2>/dev/null || true ;;
*.ts|*.tsx) ast-grep outline "$file" --lang typescript --items all 2>/dev/null || true ;;
*) sed -n '1,80p' "$file" ;;
esac
done
printf '%s\n' '== Exact diff =='
git diff --find-renames --find-copies --check "$base" "$head"
git diff --find-renames --find-copies --no-ext-diff "$base" "$head"Length of output: 70444 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '== Release workflow: numbered relevant sections =='
nl -ba .github/workflows/release.yml | sed -n '1,260p'
printf '%s\n' '== Test workflow: numbered complete file =='
nl -ba .github/workflows/test.yml | sed -n '1,220p'
printf '%s\n' '== Contract tests: remaining and numbered content =='
for file in \
src/lib/releaseArtifactAllowlistContract.test.ts \
src/lib/releaseProvenanceContract.test.ts \
src/lib/releaseWorkflowRetryContract.test.ts
do
printf '\n--- %s ---\n' "$file"
nl -ba "$file" | sed -n '1,360p'
done
printf '%s\n' '== Release-action references, pins, and artifact namespaces =='
rg -n -C2 --glob '.github/workflows/release.yml' \
'(uses:\s|upload-artifact|download-artifact|attest@|action-gh-release|pattern:|name: (release-disksage|disksage-gpu)|files:|subject-path:)' \
.github/workflows/release.yml
printf '%s\n' '== Test discovery and scripts =='
rg -n -C3 --glob 'package.json' --glob 'vite.config.*' --glob 'vitest.config.*' \
'vitest|test|include|exclude|coverage' .
printf '%s\n' '== Line-ending and SHA checks for changed files =='
python3 - <<'PY'
from pathlib import Path
import re, subprocess
base='87ac0e08cceed3d1a766da13a8f8123912178192'
head='dae9186141d1a76853225f0c377d12b23b747977'
files=subprocess.check_output(
['git','diff','--name-only',base,head], text=True
).splitlines()
for name in files:
data=Path(name).read_bytes()
print(f'{name}: CRLF={data.count(b"\\r\\n")} bare_CR={len(re.findall(rb"\\r(?!\\n)",data))}')
workflow=Path('.github/workflows/release.yml').read_text()
for action, sha in re.findall(r'uses:\s*([^@\s]+)@([0-9a-fA-F]+)', workflow):
print(f'{action}: sha_length={len(sha)} sha={sha}')
PY
printf '%s\n' '== Exact workflow diff statistics, including deleted comment-only lines =='
git diff --word-diff=porcelain \
87ac0e08cceed3d1a766da13a8f8123912178192 \
dae9186141d1a76853225f0c377d12b23b747977 \
-- .github/workflows/release.yml .github/workflows/test.yml |
sed -n '1,520p'Length of output: 2420 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '== Release workflow: relevant sections =='
sed -n '1,260p' .github/workflows/release.yml
printf '%s\n' '== Test workflow =='
sed -n '1,220p' .github/workflows/test.yml
printf '%s\n' '== Contract tests =='
for file in \
src/lib/releaseArtifactAllowlistContract.test.ts \
src/lib/releaseProvenanceContract.test.ts \
src/lib/releaseWorkflowRetryContract.test.ts
do
printf '\n--- %s ---\n' "$file"
sed -n '1,360p' "$file"
done
printf '%s\n' '== Release-action references, pins, and artifact namespaces =='
rg -n -C2 --glob '.github/workflows/release.yml' \
'(uses:\s|upload-artifact|download-artifact|attest@|action-gh-release|pattern:|name: (release-disksage|disksage-gpu)|files:|subject-path:)' \
.github/workflows/release.yml
printf '%s\n' '== Test discovery and scripts =='
rg -n -C3 --glob 'package.json' --glob 'vite.config.*' --glob 'vitest.config.*' \
'vitest|test|include|exclude|coverage' .
printf '%s\n' '== Line-ending and SHA checks for changed files =='
python3 - <<'PY'
from pathlib import Path
import re, subprocess
base='87ac0e08cceed3d1a766da13a8f8123912178192'
head='dae9186141d1a76853225f0c377d12b23b747977'
files=subprocess.check_output(
['git','diff','--name-only',base,head], text=True
).splitlines()
for name in files:
data=Path(name).read_bytes()
print(f'{name}: CRLF={data.count(b"\\r\\n")} bare_CR={len(re.findall(rb"\\r(?!\\n)",data))}')
workflow=Path('.github/workflows/release.yml').read_text()
for action, sha in re.findall(r'uses:\s*([^@\s]+)@([0-9a-fA-F]+)', workflow):
print(f'{action}: sha_length={len(sha)} sha={sha}')
PY
printf '%s\n' '== Exact workflow diff statistics, including deleted comment-only lines =='
git diff --word-diff=porcelain \
87ac0e08cceed3d1a766da13a8f8123912178192 \
dae9186141d1a76853225f0c377d12b23b747977 \
-- .github/workflows/release.yml .github/workflows/test.yml |
sed -n '1,520p'Length of output: 42223 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '== Release workflow after attestation =='
sed -n '240,410p' .github/workflows/release.yml
printf '%s\n' '== Changed-file line endings =='
python3 - <<'PY'
from pathlib import Path
import re
import subprocess
base = '87ac0e08cceed3d1a766da13a8f8123912178192'
head = 'dae9186141d1a76853225f0c377d12b23b747977'
files = subprocess.check_output(
['git', 'diff', '--name-only', base, head], text=True
).splitlines()
for name in files:
data = Path(name).read_bytes()
crlf = data.count(b'\r\n')
bare_cr = len(re.findall(rb'\r(?!\n)', data))
print('{}: CRLF={} bare_CR={}'.format(name, crlf, bare_cr))
PY
printf '%s\n' '== Immutable action SHA lengths =='
python3 - <<'PY'
from pathlib import Path
import re
workflow = Path('.github/workflows/release.yml').read_text()
for action, sha in re.findall(r'uses:\s*([^@\s]+)@([0-9a-fA-F]+)', workflow):
print('{}: {} characters {}'.format(action, len(sha), sha))
PY
printf '%s\n' '== Exact workflow whitespace validation =='
git diff --check \
87ac0e08cceed3d1a766da13a8f8123912178192 \
dae9186141d1a76853225f0c377d12b23b747977
printf '%s\n' '== Upstream tag references for new action pins =='
for spec in \
'actions/download-artifact v7.0.0 37930b1c2abaa49bbe596cd826c3c89aef350131' \
'actions/attest v4.1.0 59d89421af93a897026c735860bf21b6eb4f7b26'
do
set -- $spec
repo=$1 tag=$2 expected=$3
printf '%s %s expected=%s actual=' "$repo" "$tag" "$expected"
gh api "repos/${repo}/git/ref/tags/${tag}" --jq '.object.sha'
done
printf '%s\n' '== Relevant changes outside implementation are additive only =='
git diff --name-status \
87ac0e08cceed3d1a766da13a8f8123912178192 \
dae9186141d1a76853225f0c377d12b23b747977 \
-- CHANGELOG.md docs/doctoring/release-artifact-provenance.mdLength of output: 10693
I found no actionable issue in the seven-file release-provenance slice. The review confirmed exact-head checkout, retry-safe concurrency, 40-character immutable action pins, tag-only attestation and publication authority, separated job permissions, artifact namespace isolation, checksum basename binding, non-regular-entry rejection, exact 17-file admission, and attestation-before-publication ordering. Both new action pins resolve to their documented upstream tags. The changed files have LF line endings only. The executable contract tests are included by I did not execute repository tests in this inspection sandbox.
|
|
@coderabbitai review Review exact current head |
🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
base='87ac0e08cceed3d1a766da13a8f8123912178192'
head='9ea4faac5ac1067efcbe91da084723a24ac9c2c5'
printf '%s\n' '== Exact commit range =='
git cat-file -e "${base}^{commit}"
git cat-file -e "${head}^{commit}"
git merge-base --is-ancestor "$base" "$head"
printf 'base=%s\nhead=%s\n' "$(git rev-parse "$base")" "$(git rev-parse "$head")"
git show -s --format='head_subject=%s%nhead_parents=%P%nhead_date=%cI' "$head"
printf '%s\n' '== Changed paths =='
git diff --name-status "$base" "$head"
printf '%s\n' '== Diff check =='
git diff --check "$base" "$head"
printf '%s\n' '== Candidate file outlines =='
for file in \
.github/workflows/release.yml \
.github/workflows/test.yml \
src/lib/releaseArtifactAllowlistContract.test.ts \
src/lib/releaseProvenanceContract.test.ts \
src/lib/releaseWorkflowRetryContract.test.ts \
package.json \
CHANGELOG.md \
docs/doctoring/release-artifact-provenance.md
do
if [ -f "$file" ]; then
printf '\n--- %s ---\n' "$file"
case "$file" in
*.yml|*.yaml) ast-grep outline "$file" --lang yaml --items all 2>/dev/null || true ;;
*.ts) ast-grep outline "$file" --lang typescript --items all 2>/dev/null || true ;;
*.json) sed -n '1,180p' "$file" ;;
*) sed -n '1,100p' "$file" ;;
esac
fi
done
printf '%s\n' '== Release workflow, numbered =='
nl -ba .github/workflows/release.yml | sed -n '1,460p'
printf '%s\n' '== Contract tests, numbered =='
for file in \
src/lib/releaseArtifactAllowlistContract.test.ts \
src/lib/releaseProvenanceContract.test.ts \
src/lib/releaseWorkflowRetryContract.test.ts
do
if [ -f "$file" ]; then
printf '\n--- %s ---\n' "$file"
nl -ba "$file" | sed -n '1,460p'
fi
done
printf '%s\n' '== Test and coverage configuration =='
rg -n -C3 --glob 'package.json' --glob 'vite.config.*' --glob 'vitest.config.*' \
'(coverage|vitest|test|include|threshold|100)' .
printf '%s\n' '== Static workflow invariants =='
python3 - <<'PY'
from pathlib import Path
import re
p=Path('.github/workflows/release.yml')
text=p.read_text(encoding='utf-8')
print('CRLF={}'.format(p.read_bytes().count(b'\r\n')))
for action, sha in re.findall(r'uses:\s*([^@\s]+)@([0-9a-fA-F]+)', text):
print('action={} sha_length={} sha={}'.format(action, len(sha), sha))
for needle in (
'github.ref_type == \'tag\'',
'github.run_attempt == 1',
'release-disksage-*',
'disksage-gpu-*',
'actions/attest@',
'actions/download-artifact@',
'attestations: write',
'id-token: write',
'contents: write',
'subject-path:',
):
print('contains[{}]={}'.format(needle, needle in text))
PY
printf '%s\n' '== Current-head delta for regression and documentation =='
git diff --no-ext-diff --find-renames \
dae9186141d1a76853225f0c377d12b23b747977 \
"$head" -- \
.github/workflows/release.yml \
src/lib/releaseArtifactAllowlistContract.test.ts \
src/lib/releaseProvenanceContract.test.ts \
src/lib/releaseWorkflowRetryContract.test.ts \
docs/doctoring/release-artifact-provenance.md \
CHANGELOG.mdLength of output: 22695 🏁 Script executed: #!/bin/bash
set -euo pipefail
base='87ac0e08cceed3d1a766da13a8f8123912178192'
head='9ea4faac5ac1067efcbe91da084723a24ac9c2c5'
numbered() {
awk '{ printf "%6d %s\n", NR, $0 }' "$1"
}
printf '%s\n' '== Release workflow at exact head =='
git show "${head}:.github/workflows/release.yml" > /tmp/release.yml
numbered /tmp/release.yml | sed -n '1,460p'
printf '%s\n' '== Test workflow at exact head =='
git show "${head}:.github/workflows/test.yml" > /tmp/test.yml
numbered /tmp/test.yml | sed -n '1,260p'
printf '%s\n' '== Contract tests at exact head =='
for file in \
src/lib/releaseArtifactAllowlistContract.test.ts \
src/lib/releaseProvenanceContract.test.ts \
src/lib/releaseWorkflowRetryContract.test.ts
do
printf '\n--- %s ---\n' "$file"
git show "${head}:${file}" > "/tmp/$(basename "$file")"
numbered "/tmp/$(basename "$file")" | sed -n '1,520p'
done
printf '%s\n' '== Coverage configuration and test discovery at exact head =='
git show "${head}:package.json"
for file in vite.config.ts vite.config.js vitest.config.ts vitest.config.js; do
if git cat-file -e "${head}:${file}" 2>/dev/null; then
printf '\n--- %s ---\n' "$file"
git show "${head}:${file}"
fi
done
printf '%s\n' '== Workflow jobs, permissions, gates, and action references =='
rg -n -C3 \
'(^(concurrency|permissions|jobs|attest-release|publish-release):|^\s{2}(build|attest-release|publish-release):|^\s{4}if:|^\s{4}needs:|^\s{4}permissions:|^\s{6}(contents|id-token|attestations):|uses:|download-artifact|upload-artifact|subject-path:|files:|pattern:|github\.run_attempt|github\.ref_type|github\.ref)' \
/tmp/release.yml
printf '%s\n' '== Exact current-head change from predecessor =='
git diff --no-ext-diff --find-renames \
dae9186141d1a76853225f0c377d12b23b747977 "$head" -- \
.github/workflows/release.yml \
.github/workflows/test.yml \
src/lib/releaseArtifactAllowlistContract.test.ts \
src/lib/releaseProvenanceContract.test.ts \
src/lib/releaseWorkflowRetryContract.test.ts \
CHANGELOG.md \
docs/doctoring/release-artifact-provenance.md
printf '%s\n' '== Static line ending and action pin checks =='
python3 - <<'PY'
import re
from pathlib import Path
text=Path('/tmp/release.yml').read_text()
data=Path('/tmp/release.yml').read_bytes()
print('release.yml_crlf={}'.format(data.count(b'\r\n')))
for action, sha in re.findall(r'uses:\s*([^@\s]+)@([0-9a-fA-F]+)', text):
print('{} sha_length={} {}'.format(action, len(sha), sha))
PYLength of output: 62362
I found no actionable current-head issue. The current-head change adds an executable non-Windows symlink regression. The test extracts and executes the source-controlled admission script against a complete 17-file fixture. It adds a symlink and requires the specific The admission script verifies checksum semantics and digests before exact-set counting. It rejects non-regular entries before attestation. It then requires exactly 17 regular files. Checksum records must contain one SHA-256 digest and the adjacent expected CLI basename. The workflow keeps retry-safe concurrency with All inspected action references use 40-character SHAs. The release workflow has LF line endings. The 100% Vitest thresholds remain configured, and the test workflow invokes I did not execute repository tests or coverage in this sandbox. This review verifies the static contract and workflow configuration only.
|
seonghobae
left a comment
There was a problem hiding this comment.
Current-head provenance review found one valid fail-closed gap in the artifact namespace boundary.
Both attest-release and publish-release download the platform artifacts with merge-multiple: true. That flattens separate artifact archives into one destination before the admission script counts duplicate filenames. A duplicate path contributed by two build artifacts can therefore be overwritten during download and appear only once to require_exactly_one_file, which makes the documented duplicate-rejection claim unverifiable.
Repair test-first on this stacked branch:
- Add a permanent workflow contract requiring both release download steps to preserve per-artifact directories (
merge-multiple: false, or omission with the documented default). - Add a realistic verifier fixture containing the same required CLI or checksum under two distinct artifact directories and assert the exact admission script rejects it.
- Preserve the artifact namespaces in both the attestation and publication jobs; the existing recursive checksum, attestation, and publication globs already support the extra directory level.
- Re-run the exact-head Test, Release, Security Scan, SAST, coverage, packaging, and review gates after the parent PR is integrated.
Do not address this by weakening the exactly-one checks or by trusting last-writer-wins extraction.
|
Exact-current-head update: The valid artifact-namespace finding is addressed test-first:
The source-controlled Bash admission block was executed against both a valid 17-file fixture and a fixture containing the same required CLI in two preserved artifact directories. The valid fixture passed; the duplicate fixture failed closed with |
Exact-head stack synchronization record
The parent commits after |
|
Superseded by bounded current-main owners after exact stale-stack convergence. Fresh direct-delta inventory for #138 contains 12 paths. The ten release-owned paths are now owned by Draft #154 at exact head The parent architecture/tenant work inherited through this stack remains a separate #137 convergence concern and is not discarded by closing this child. #138 is still non-mergeable on its stale parent base, and no predecessor checks/reviews/approvals transfer to either successor. Closing as superseded, not merged. |
Summary
actions/download-artifactandactions/attestStack order
This pull request is intentionally stacked on #137 (
docs/acquisition-architecture-contract). Keep it in Draft and based on that branch until #137 merges. Retarget tomainonly after the parent is integrated, without discarding or duplicating stack commits.Test-first and review history
211c718feb76dc1d1c90a08ee3804f9846aeced3deeaabfead889194957dae3d629ab266990b52f0548de71c2e782fe323d1ff5550e184c76cabad59through7f30e1b2881b2eb4784c3b3b924f7ede7b8f8d2c0c8d7aa14b9cbcadd8c130247f7f82c85f3916ad3cb520a2646f067a4f7bd2698e6cf834d15427d0through1e0f999140c2ef89eaf3c38838704557857c494fa96e9f10ba8313dd43b989a12db648684849d26fthrough948f36f23baec31c3e3edfe87a49800fc0cf274a8b0406c5d234867ce37ca5c9502be9425022d9a1throughdae9186141d1a76853225f0c377d12b23b7479779ea4faac5ac1067efcbe91da084723a24ac9c2c5996dd52ea6b18141cc20fe23738e694cc5f60c67through1460d8d214f976fc08eddee4f0aaaafd80919297c0f56847b3c626b56daf271663ad32468fb53f6fthroughf11a0097d7600dac57249b809c17ce9a4a2e86b483eefd08cb1b5cca335fbc6ef00811cc92b7472fande9cf4c1d2133a62bbb54ca4429db3e1df633f803f327223a9d4be30eae9c9a83c7866e15fedbd3eeThe previous exact-base Release run
31070907716was inspected directly: its Linux, Windows, and macOS artifact ZIPs preserve the expectedbundle/...paths and platform-specific CLI/checksum names used by the admission contract.Exact-current-head gate
Current head:
f327223a9d4be30eae9c9a83c7866e15fedbd3ee.The earlier
e9cf4c1d2133a62bbb54ca4429db3e1df633f803evidence is predecessor-head history only. No predecessor or generated merge-tree result authorizes this head. Fresh exact-current-head Test, Release, Security Scan, SAST, coverage, packaging, provenance, review, approval, branch-protection, and repository-policy evidence is required after the parent is integrated and this PR is safely retargeted.Security and provenance boundary
attest-releasereceives onlycontents: read,id-token: write, andattestations: writepublish-releasereceives onlycontents: writeand depends on successful attestationrelease-disksage-*, excludingdisksage-gpu-*Required before merge
No version is bumped and no release is published by this change.