From e930e82436ff6d9a42ad9be63e9aba6946c5a1d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eren=20Ar=C4=B1?= Date: Sat, 19 Sep 2026 19:54:11 +0300 Subject: [PATCH 1/3] research: add one-shot scholarly release workflow --- .github/workflows/scholarly-release-v1.yml | 200 +++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 .github/workflows/scholarly-release-v1.yml diff --git a/.github/workflows/scholarly-release-v1.yml b/.github/workflows/scholarly-release-v1.yml new file mode 100644 index 00000000..c394f5d1 --- /dev/null +++ b/.github/workflows/scholarly-release-v1.yml @@ -0,0 +1,200 @@ +name: scholarly-release-v1 + +on: + push: + branches: [main] + paths: + - ".github/workflows/scholarly-release-v1.yml" + +permissions: + contents: write + actions: read + id-token: write + attestations: write + +concurrency: + group: scholarly-release-v1 + cancel-in-progress: false + +env: + RESEARCH_TAG: research-v1 + +jobs: + publish: + name: Publish immutable research v1 release + runs-on: ubuntu-24.04 + timeout-minutes: 20 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + persist-credentials: true + + - name: Refuse an existing research v1 tag or release + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + git fetch --tags --force + if git show-ref --verify --quiet "refs/tags/${RESEARCH_TAG}"; then + echo "tag already exists: ${RESEARCH_TAG}" >&2 + exit 1 + fi + if gh release view "${RESEARCH_TAG}" --repo "${GITHUB_REPOSITORY}" >/dev/null 2>&1; then + echo "release already exists: ${RESEARCH_TAG}" >&2 + exit 1 + fi + test "${GITHUB_REF}" = "refs/heads/main" + test -z "$(git status --porcelain)" + + - name: Static archive checks + run: | + set -euo pipefail + python3 -m py_compile scripts/research/archive-v1.py + bash -n scripts/research/test-archive-v1.sh + jq -e '.schema_version == "bpfcompat.research.archive-plan.v1"' research/archive/v1/archive-plan.json >/dev/null + jq -e '.schema_version == "bpfcompat.research.archive-lock.v1"' research/archive/v1/archive-lock.json >/dev/null + + - name: Download pinned canonical source artifacts + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + pilot_id="$(jq -r '.artifact_sources.pilot.artifact_id' research/archive/v1/archive-plan.json)" + repeat_id="$(jq -r '.artifact_sources.repeat.artifact_id' research/archive/v1/archive-plan.json)" + materialization_id="$(jq -r '.artifact_sources.materialization.artifact_id' research/archive/v1/archive-plan.json)" + gh api "/repos/${GITHUB_REPOSITORY}/actions/artifacts/${pilot_id}/zip" > /tmp/pilot.zip + gh api "/repos/${GITHUB_REPOSITORY}/actions/artifacts/${repeat_id}/zip" > /tmp/repeat.zip + gh api "/repos/${GITHUB_REPOSITORY}/actions/artifacts/${materialization_id}/zip" > /tmp/materialization.zip + + - name: Rebuild and verify release-shaped archive + run: | + set -euo pipefail + rm -rf /tmp/archive-v1 + python3 scripts/research/archive-v1.py build --pilot-zip /tmp/pilot.zip --repeat-zip /tmp/repeat.zip --materialization-zip /tmp/materialization.zip --out-dir /tmp/archive-v1 + python3 scripts/research/archive-v1.py verify --pilot-zip /tmp/pilot.zip --repeat-zip /tmp/repeat.zip --materialization-zip /tmp/materialization.zip --bundle-dir /tmp/archive-v1 + diff -u research/archive/v1/archive-lock.json /tmp/archive-v1/archive-lock.json + + - name: Verify release lock invariants + run: | + set -euo pipefail + lock=/tmp/archive-v1/archive-lock.json + jq -e ' + .manifest.file_count == 668 and + .manifest.total_bytes == 27743323 and + .manifest.sha256 == "sha256:6ed6d38d57db57e75964829278a62c7bb4a12cd8fc5db97baebb05a1fdd5e927" and + .payload_zip.size_bytes == 12899271 and + .payload_zip.sha256 == "sha256:143a8e8a93e43aebbacfd73465a659ca4cb055db7a576960c9d50ed9bc90a817" and + (.excluded_rebuildable | length) == 2 + ' "$lock" >/dev/null + + - name: Stage release files + run: | + set -euo pipefail + mkdir -p dist/research-v1 + cp /tmp/archive-v1/archive-manifest.json dist/research-v1/ + cp /tmp/archive-v1/archive-lock.json dist/research-v1/ + cp /tmp/archive-v1/bpfcompat-research-v1-payload.zip dist/research-v1/ + cp /tmp/archive-v1/RELEASE-CHECKSUMS.txt dist/research-v1/ + + - name: Attest scholarly archive provenance + uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1 + with: + subject-path: | + dist/research-v1/archive-manifest.json + dist/research-v1/archive-lock.json + dist/research-v1/bpfcompat-research-v1-payload.zip + dist/research-v1/RELEASE-CHECKSUMS.txt + + - name: Prepare release notes + run: | + set -euo pipefail + cat > /tmp/research-release-notes.md </dev/null 2>&1; then + git push --delete origin "${RESEARCH_TAG}" || true + fi + } + trap cleanup_tag EXIT + + gh release create "${RESEARCH_TAG}" --repo "${GITHUB_REPOSITORY}" --verify-tag --title "BPFCompat Research Dataset v1" --notes-file /tmp/research-release-notes.md --latest=false dist/research-v1/archive-manifest.json dist/research-v1/archive-lock.json dist/research-v1/bpfcompat-research-v1-payload.zip dist/research-v1/RELEASE-CHECKSUMS.txt + + trap - EXIT + + - name: Verify published tag and release + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + git fetch --tags --force + test "$(git rev-list -n1 "${RESEARCH_TAG}")" = "${GITHUB_SHA}" + + gh release view "${RESEARCH_TAG}" --repo "${GITHUB_REPOSITORY}" --json tagName,isDraft,isPrerelease,assets,url > /tmp/release.json + + jq -e --arg tag "${RESEARCH_TAG}" ' + .tagName == $tag and + .isDraft == false and + .isPrerelease == false and + ([.assets[].name] | sort) == + ([ + "RELEASE-CHECKSUMS.txt", + "archive-lock.json", + "archive-manifest.json", + "bpfcompat-research-v1-payload.zip" + ] | sort) + ' /tmp/release.json >/dev/null + + cat /tmp/release.json From 1c11450363cc66fa65dcbccfc47c6135ca37383a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eren=20Ar=C4=B1?= Date: Sat, 19 Sep 2026 19:55:38 +0300 Subject: [PATCH 2/3] release: isolate scholarly research publisher from product pipeline --- scripts/check-release-consistency.sh | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/scripts/check-release-consistency.sh b/scripts/check-release-consistency.sh index babd49bd..c74caa6b 100755 --- a/scripts/check-release-consistency.sh +++ b/scripts/check-release-consistency.sh @@ -113,11 +113,35 @@ if grep -Eq '^[[:space:]]+push:' "$promotion_workflow"; then fail "${promotion_workflow} must never have an automatic push trigger" fi +scholarly_workflow=".github/workflows/scholarly-release-v1.yml" +[[ -f "$scholarly_workflow" ]] || + fail "scholarly research release workflow is missing" +for required_control in \ + 'branches: [main]' \ + '- ".github/workflows/scholarly-release-v1.yml"' \ + 'RESEARCH_TAG: research-v1' \ + 'Refuse an existing research v1 tag or release' \ + 'Rebuild and verify release-shaped archive' \ + 'Create annotated research tag' \ + 'Publish GitHub research release' \ + '--verify-tag' \ + '--latest=false'; do + grep -Fq -- "$required_control" "$scholarly_workflow" || + fail "$scholarly_workflow is missing required control: $required_control" +done +if grep -Eq 'workflow_dispatch:|refs/tags/v|tags:[[:space:]]*\[.*v\*|scripts/promote-release\.sh|softprops/action-gh-release@|value=latest' "$scholarly_workflow"; then + fail "$scholarly_workflow must remain isolated from product release/promotion paths" +fi +if [[ "$(grep -Ec '^[[:space:]]+gh release create ' "$scholarly_workflow")" -ne 1 ]]; then + fail "$scholarly_workflow must contain exactly one gh release create call" +fi + unexpected_release_writers="$( grep -ERl --include='*.yml' --include='*.yaml' \ 'gh release (create|upload|edit)|softprops/action-gh-release@' .github/workflows | grep -vFx "$release_workflow" | - grep -vFx "$promotion_workflow" || true + grep -vFx "$promotion_workflow" | + grep -vFx "$scholarly_workflow" || true )" if [[ -n "$unexpected_release_writers" ]]; then printf '%s\n' "$unexpected_release_writers" >&2 From 3dfd60650bade593b7612796700aeddf76224288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eren=20Ar=C4=B1?= Date: Sat, 19 Sep 2026 19:55:49 +0300 Subject: [PATCH 3/3] release: regression-test scholarly publisher isolation --- scripts/check-release-consistency_test.sh | 24 ++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/scripts/check-release-consistency_test.sh b/scripts/check-release-consistency_test.sh index 9065e2eb..268d4944 100755 --- a/scripts/check-release-consistency_test.sh +++ b/scripts/check-release-consistency_test.sh @@ -10,7 +10,13 @@ unset GITHUB_REF_TYPE GITHUB_REF_NAME script="scripts/check-release-consistency.sh" tmp="$(mktemp -d)" -trap 'rm -rf "$tmp"' EXIT +scholarly_workflow=".github/workflows/scholarly-release-v1.yml" +cp "$scholarly_workflow" "$tmp/scholarly-release-v1.yml" +restore() { + cp "$tmp/scholarly-release-v1.yml" "$scholarly_workflow" + rm -rf "$tmp" +} +trap restore EXIT current_release="$( awk -F': ' '$1 == "release_version" {print $2; exit}' release.yaml )" @@ -65,4 +71,20 @@ if GITHUB_REF_TYPE=tag GITHUB_REF_NAME=v9.9.9 \ exit 1 fi +sed -i 's/RESEARCH_TAG: research-v1/RESEARCH_TAG: v9.9.9/' "$scholarly_workflow" +if BPFCOMPAT_RELEASE_METADATA=release.yaml \ + "$script" >"$tmp/bad-scholarly-tag.log" 2>&1; then + echo "[release-consistency-test] accepted scholarly workflow with product-style tag" >&2 + exit 1 +fi +cp "$tmp/scholarly-release-v1.yml" "$scholarly_workflow" + +sed -i '/--latest=false/d' "$scholarly_workflow" +if BPFCOMPAT_RELEASE_METADATA=release.yaml \ + "$script" >"$tmp/bad-scholarly-latest.log" 2>&1; then + echo "[release-consistency-test] accepted scholarly workflow without --latest=false" >&2 + exit 1 +fi +cp "$tmp/scholarly-release-v1.yml" "$scholarly_workflow" + echo "[release-consistency-test] PASS"