Skip to content

Commit b1a8dc0

Browse files
authored
Merge pull request #79 from loadingalias/fix/s390x-release-evidence
ci: regenerate exact-tag s390x release evidence
2 parents 4383246 + 62ccc6b commit b1a8dc0

14 files changed

Lines changed: 415 additions & 11 deletions

.github/workflows/_rust-job.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ on:
99
runner:
1010
required: true
1111
type: string
12+
checkout_ref:
13+
required: false
14+
type: string
15+
default: ""
1216
timeout_minutes:
1317
required: false
1418
type: number
@@ -119,6 +123,7 @@ jobs:
119123
with:
120124
fetch-depth: 0
121125
persist-credentials: false
126+
ref: ${{ inputs.checkout_ref || github.sha }}
122127

123128
- name: Setup
124129
uses: ./.github/actions/setup
@@ -132,6 +137,7 @@ jobs:
132137
- name: Run
133138
shell: bash
134139
env:
140+
CARGO_TARGET_S390X_UNKNOWN_LINUX_GNU_RUSTFLAGS: ${{ inputs.target == 's390x-unknown-linux-gnu' && '-C target-feature=+vector' || '' }}
135141
RSCRYPTO_CI_OPERATION: ${{ inputs.operation }}
136142
RSCRYPTO_CI_RUNNER: ${{ inputs.runner }}
137143
RSCRYPTO_CI_TEST_MODE: ${{ inputs.test_mode }}

.github/workflows/ct.yaml

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ on:
3838
required: false
3939
type: number
4040
default: 90
41+
release_tag:
42+
description: "Existing release tag whose s390x CT lane must be regenerated."
43+
required: false
44+
type: string
45+
default: ""
4146
workflow_dispatch:
4247
inputs:
4348
platforms:
@@ -79,6 +84,11 @@ on:
7984
required: false
8085
type: number
8186
default: 90
87+
release_tag:
88+
description: "Existing release tag whose s390x CT lane must be regenerated."
89+
required: false
90+
type: string
91+
default: ""
8292

8393
env:
8494
RUST_BACKTRACE: 1
@@ -94,15 +104,54 @@ jobs:
94104
name: Resolve CT Matrix
95105
runs-on: ubuntu-latest
96106
outputs:
107+
checkout_ref: ${{ steps.source.outputs.checkout_ref }}
97108
has_targets: ${{ steps.plan.outputs.has_targets }}
98109
matrix: ${{ steps.plan.outputs.matrix }}
99110
steps:
100111
- name: Checkout
101112
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
102113
with:
103-
fetch-depth: 1
114+
fetch-depth: 0
104115
persist-credentials: false
105116

117+
- name: Resolve CT source
118+
id: source
119+
shell: bash
120+
env:
121+
ARTIFACT_RETENTION_DAYS: ${{ inputs.artifact_retention_days }}
122+
BINSEC_TIMEOUT: ${{ inputs.binsec_timeout }}
123+
DUDECT_FILTER: ${{ inputs.dudect_filter }}
124+
DUDECT_GATE: ${{ inputs.dudect_gate }}
125+
DUDECT_TIMEOUT: ${{ inputs.dudect_timeout }}
126+
EVENT_NAME: ${{ github.event_name }}
127+
PLATFORMS: ${{ inputs.platforms }}
128+
RELEASE_TAG: ${{ inputs.release_tag }}
129+
UPLOAD_RAW_ARTIFACTS: ${{ inputs.upload_raw_artifacts }}
130+
run: |
131+
set -euo pipefail
132+
if [[ -z "$RELEASE_TAG" ]]; then
133+
echo "checkout_ref=" >> "$GITHUB_OUTPUT"
134+
exit 0
135+
fi
136+
if [[ "$EVENT_NAME" != "workflow_dispatch" || "$GITHUB_REF" != "refs/heads/main" ]]; then
137+
echo "release CT recovery must run from protected main" >&2
138+
exit 1
139+
fi
140+
if [[ ! "$RELEASE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]]; then
141+
echo "release CT recovery tag is invalid: $RELEASE_TAG" >&2
142+
exit 1
143+
fi
144+
git rev-parse -q --verify "${RELEASE_TAG}^{tag}" >/dev/null \
145+
|| { echo "release CT recovery requires an annotated tag" >&2; exit 1; }
146+
if [[ "$PLATFORMS" != "ibm-s390x" || "$DUDECT_GATE" != "required" \
147+
|| -n "$DUDECT_FILTER" || "$UPLOAD_RAW_ARTIFACTS" != "true" \
148+
|| "$ARTIFACT_RETENTION_DAYS" != "90" || "$DUDECT_TIMEOUT" != "1800" \
149+
|| "$BINSEC_TIMEOUT" != "900" ]]; then
150+
echo "release CT recovery must run the complete raw ibm-s390x lane with release timeouts and 90-day retention" >&2
151+
exit 1
152+
fi
153+
echo "checkout_ref=$RELEASE_TAG" >> "$GITHUB_OUTPUT"
154+
106155
- name: Build CT Matrix
107156
id: plan
108157
shell: bash
@@ -121,6 +170,7 @@ jobs:
121170
include: ${{ fromJSON(needs.plan.outputs.matrix) }}
122171
uses: ./.github/workflows/_rust-job.yaml
123172
with:
173+
checkout_ref: ${{ needs.plan.outputs.checkout_ref }}
124174
runner: ${{ matrix.runner }}
125175
timeout_minutes: ${{ matrix.timeout_minutes }}
126176
cache_key: ct-${{ matrix.artifact_suffix }}

.github/workflows/release.yaml

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ on:
1010
description: Existing signed release tag to recover
1111
required: true
1212
type: string
13+
s390x_ct_run:
14+
description: Optional reviewed exact-tag s390x CT recovery run ID
15+
required: false
16+
type: string
1317

1418
env:
1519
RUST_BACKTRACE: 1
@@ -46,6 +50,8 @@ jobs:
4650
weekly_commit: ${{ steps.evidence.outputs.weekly_commit }}
4751
weekly_version: ${{ steps.evidence.outputs.weekly_version }}
4852
weekly_evidence_mode: ${{ steps.evidence.outputs.weekly_evidence_mode }}
53+
s390x_ct_run_id: ${{ steps.s390x_ct.outputs.s390x_run_id }}
54+
s390x_ct_run_url: ${{ steps.s390x_ct.outputs.s390x_run_url }}
4955
steps:
5056
- name: Checkout
5157
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
@@ -101,15 +107,6 @@ jobs:
101107
RELEASE_COMMIT: ${{ steps.identity.outputs.release_commit }}
102108
run: scripts/ci/release-evidence-check.sh --commit "$RELEASE_COMMIT"
103109

104-
- name: Setup
105-
uses: ./.github/actions/setup
106-
with:
107-
cache-key: release-preflight
108-
tools-mode: release
109-
toolchain-components: ""
110-
enable-magic-cache: false
111-
enable-rust-cache: true
112-
113110
- name: Checkout reviewed recovery tooling
114111
if: github.event_name == 'workflow_dispatch'
115112
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
@@ -119,6 +116,27 @@ jobs:
119116
persist-credentials: false
120117
ref: ${{ github.sha }}
121118

119+
- name: Verify s390x CT recovery evidence
120+
if: github.event_name == 'workflow_dispatch' && inputs.s390x_ct_run != ''
121+
id: s390x_ct
122+
env:
123+
GH_TOKEN: ${{ github.token }}
124+
S390X_CT_RUN: ${{ inputs.s390x_ct_run }}
125+
WORKFLOW_COMMIT: ${{ github.sha }}
126+
run: |
127+
target/release-automation/scripts/ci/release-ct-recovery-check.sh \
128+
--run-id "$S390X_CT_RUN" \
129+
--workflow-commit "$WORKFLOW_COMMIT"
130+
131+
- name: Setup
132+
uses: ./.github/actions/setup
133+
with:
134+
cache-key: release-preflight
135+
tools-mode: release
136+
toolchain-components: ""
137+
enable-magic-cache: false
138+
enable-rust-cache: true
139+
122140
- name: Install recovery SemVer checker
123141
if: github.event_name == 'workflow_dispatch'
124142
run: |
@@ -250,6 +268,19 @@ jobs:
250268
github-token: ${{ github.token }}
251269
run-id: ${{ needs.preflight.outputs.weekly_run_id }}
252270

271+
- name: Remove replaced s390x CT evidence
272+
if: needs.preflight.outputs.s390x_ct_run_id != ''
273+
run: rm -rf ct-release-artifacts/ct-raw-ibm-s390x
274+
275+
- name: Download recovered s390x CT evidence
276+
if: needs.preflight.outputs.s390x_ct_run_id != ''
277+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
278+
with:
279+
name: ct-raw-ibm-s390x
280+
path: ct-release-artifacts/ct-raw-ibm-s390x
281+
github-token: ${{ github.token }}
282+
run-id: ${{ needs.preflight.outputs.s390x_ct_run_id }}
283+
253284
- name: Download RISC-V CT evidence
254285
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
255286
with:
@@ -371,6 +402,7 @@ jobs:
371402
CT_EVIDENCE_MODE: ${{ needs.preflight.outputs.weekly_evidence_mode }}
372403
CT_EVIDENCE_WEEKLY_RUN: ${{ needs.preflight.outputs.weekly_run_url }}
373404
CT_EVIDENCE_RISCV_RUN: ${{ needs.preflight.outputs.riscv_run_url }}
405+
CT_EVIDENCE_S390X_RUN: ${{ needs.preflight.outputs.s390x_ct_run_url }}
374406
REPOSITORY_CONTROLS_NAME: ${{ steps.repository_controls.outputs.evidence_name }}
375407
REPOSITORY_CONTROLS_PATH: ${{ steps.repository_controls.outputs.evidence_path }}
376408
REPOSITORY_CONTROLS_SHA256: ${{ steps.repository_controls.outputs.evidence_sha256 }}
@@ -383,6 +415,10 @@ jobs:
383415
run: |
384416
set -euo pipefail
385417
notes_file="$(mktemp)"
418+
s390x_recovery_note=""
419+
if [[ -n "$CT_EVIDENCE_S390X_RUN" ]]; then
420+
s390x_recovery_note="s390x recovery evidence run: $CT_EVIDENCE_S390X_RUN"
421+
fi
386422
cat > "$notes_file" <<EOF
387423
Release tag: \`${RELEASE_TAG}\`
388424
Release commit: \`${RELEASE_COMMIT}\`
@@ -401,6 +437,7 @@ jobs:
401437
Evidence commit: \`${CT_EVIDENCE_COMMIT}\` (\`${CT_EVIDENCE_MODE}\`)
402438
Weekly evidence run: ${CT_EVIDENCE_WEEKLY_RUN}
403439
RISC-V evidence run: ${CT_EVIDENCE_RISCV_RUN}
440+
${s390x_recovery_note}
404441
405442
Repository controls: \`${REPOSITORY_CONTROLS_NAME}\`
406443
SHA-256: \`${REPOSITORY_CONTROLS_SHA256}\`

docs/release.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,29 @@ reviewed recovery path from `main`:
212212
gh workflow run release.yaml --ref main -f tag=vX.Y.Z
213213
```
214214

215+
If release packaging rejects only the s390x CT artifact, regenerate that lane
216+
against the existing tag before dispatching recovery:
217+
218+
```bash
219+
gh workflow run ct.yaml --ref main \
220+
-f platforms=ibm-s390x \
221+
-f dudect_gate=required \
222+
-f upload_raw_artifacts=true \
223+
-f artifact_retention_days=90 \
224+
-f release_tag=vX.Y.Z
225+
226+
gh workflow run release.yaml --ref main \
227+
-f tag=vX.Y.Z \
228+
-f s390x_ct_run=RUN_ID
229+
```
230+
231+
Run both dispatches from the same reviewed `main` commit. The CT recovery is
232+
limited to the complete native s390x lane and checks out the immutable tag;
233+
the release preflight rejects any replacement run from another workflow,
234+
branch, repository, or commit. The normal release evidence packager then
235+
validates the replacement artifact's tag commit, crate version, cases, hashes,
236+
and target provenance before publication.
237+
215238
Recovery checks out the existing annotated tag, verifies its allowed signature,
216239
and binds the package, evidence, release manifest, and release notes to the
217240
tag's commit rather than the newer workflow commit. Confirm that Preflight

justfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ check-actions:
235235
@scripts/ci/check-worktree-test.sh
236236
@scripts/ci/pre-push-test.sh
237237
@scripts/ci/release-evidence-check-test.sh
238+
@scripts/ci/release-ct-recovery-check-test.sh
238239
@scripts/ci/repository-controls-evidence-test.sh
239240
@scripts/ci/release-identity-test.sh
240241
@scripts/ci/publish-immutable-release-test.sh

scripts/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ the recipes reported by `just --list`.
4242
| `ci/pre-push-test.sh` | `just check-actions` |
4343
| `ci/release-evidence-check.sh` | `just release-tag`, `release.yaml`, `ci/release-evidence-check-test.sh` |
4444
| `ci/release-evidence-check-test.sh` | `just check-actions` |
45+
| `ci/release-ct-recovery-check.sh` | `release.yaml`, `ci/release-ct-recovery-check-test.sh` |
46+
| `ci/release-ct-recovery-check-test.sh` | `just check-actions` |
4547
| `ci/repository-controls-evidence.sh` | `just release-tag`, `release.yaml`, `ci/repository-controls-evidence-test.sh` |
4648
| `ci/repository-controls-evidence-test.sh` | `just check-actions` |
4749
| `ci/package-release-source.sh` | `release.yaml`, `ci/release-identity-test.sh` |

scripts/ci/check-ci-ownership-test.sh

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,36 @@ sed -i.bak 's#refs/heads/main#refs/heads/recovery#' \
5454
rm -f "$unprotected_recovery/.github/workflows/release.yaml.bak"
5555
expect_failure "$unprotected_recovery" "release recovery accepts unprotected workflow code"
5656

57+
missing_ct_recovery_tag="$TMP_ROOT/missing-ct-recovery-tag"
58+
make_fixture "$missing_ct_recovery_tag"
59+
yq eval 'del(.on.workflow_dispatch.inputs.release_tag)' -i \
60+
"$missing_ct_recovery_tag/.github/workflows/ct.yaml"
61+
expect_failure "$missing_ct_recovery_tag" "CT recovery has no immutable tag identity"
62+
63+
unprotected_ct_recovery="$TMP_ROOT/unprotected-ct-recovery"
64+
make_fixture "$unprotected_ct_recovery"
65+
yq eval '(.jobs.plan.steps[] | select(.name == "Resolve CT source") | .run) |= sub("refs/heads/main"; "refs/heads/recovery")' -i \
66+
"$unprotected_ct_recovery/.github/workflows/ct.yaml"
67+
expect_failure "$unprotected_ct_recovery" "CT recovery accepts unprotected workflow code"
68+
69+
mutable_ct_recovery_checkout="$TMP_ROOT/mutable-ct-recovery-checkout"
70+
make_fixture "$mutable_ct_recovery_checkout"
71+
yq eval '(.jobs.ct.with.checkout_ref) = "${{ github.sha }}"' -i \
72+
"$mutable_ct_recovery_checkout/.github/workflows/ct.yaml"
73+
expect_failure "$mutable_ct_recovery_checkout" "CT recovery ignores the immutable tag source"
74+
75+
missing_s390x_vector_environment="$TMP_ROOT/missing-s390x-vector-environment"
76+
make_fixture "$missing_s390x_vector_environment"
77+
yq eval 'del(.jobs.run.steps[] | select(.name == "Run") | .env.CARGO_TARGET_S390X_UNKNOWN_LINUX_GNU_RUSTFLAGS)' -i \
78+
"$missing_s390x_vector_environment/.github/workflows/_rust-job.yaml"
79+
expect_failure "$missing_s390x_vector_environment" "s390x sibling CT processes lose the vector target environment"
80+
81+
unvalidated_s390x_recovery="$TMP_ROOT/unvalidated-s390x-recovery"
82+
make_fixture "$unvalidated_s390x_recovery"
83+
yq eval '(.jobs.preflight.steps[] | select(.name == "Verify s390x CT recovery evidence") | .run) = "true"' -i \
84+
"$unvalidated_s390x_recovery/.github/workflows/release.yaml"
85+
expect_failure "$unvalidated_s390x_recovery" "release accepts unvalidated replacement s390x evidence"
86+
5787
mutable_publish_checkout="$TMP_ROOT/mutable-publish-checkout"
5888
make_fixture "$mutable_publish_checkout"
5989
yq eval '(.jobs.publish.steps[] | select(.name == "Checkout") | .with.ref) = "${{ github.ref }}"' -i \

scripts/ci/check-ci-ownership.sh

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ CI_CHECK="$ROOT/scripts/ci/ci-check.sh"
4848
RUN_RUST_JOB="$ROOT/scripts/ci/run-rust-job.sh"
4949
RELEASE_PREFLIGHT="$ROOT/scripts/ci/release-preflight.sh"
5050
RELEASE_EVIDENCE="$ROOT/scripts/ci/release-evidence-check.sh"
51+
RELEASE_CT_RECOVERY="$ROOT/scripts/ci/release-ct-recovery-check.sh"
52+
RELEASE_CT_RECOVERY_TEST="$ROOT/scripts/ci/release-ct-recovery-check-test.sh"
5153
RELEASE_SOURCE="$ROOT/scripts/ci/package-release-source.sh"
5254
RELEASE_MANIFEST="$ROOT/scripts/ci/write-release-manifest.sh"
5355
RELEASE_IDENTITY_TEST="$ROOT/scripts/ci/release-identity-test.sh"
@@ -146,6 +148,8 @@ require_file "$CI_CHECK"
146148
require_file "$RUN_RUST_JOB"
147149
require_file "$RELEASE_PREFLIGHT"
148150
require_file "$RELEASE_EVIDENCE"
151+
require_file "$RELEASE_CT_RECOVERY"
152+
require_file "$RELEASE_CT_RECOVERY_TEST"
149153
require_file "$RELEASE_SOURCE"
150154
require_file "$RELEASE_MANIFEST"
151155
require_file "$RELEASE_IDENTITY_TEST"
@@ -234,6 +238,16 @@ done < <(
234238
|| fail "the reusable Rust job operation must be required"
235239
[[ $(yq eval '.on.workflow_call.inputs.operation.type' "$RUST_JOB") == "string" ]] \
236240
|| fail "the reusable Rust job operation must be typed as a string"
241+
[[ $(yq eval '.on.workflow_call.inputs.checkout_ref.type' "$RUST_JOB") == "string" ]] \
242+
|| fail "the reusable Rust job checkout ref must be typed as a string"
243+
# shellcheck disable=SC2016 # GitHub expression is an intentional literal workflow contract.
244+
[[ $(yq eval '.jobs.run.steps[] | select(.name == "Checkout") | .with.ref' "$RUST_JOB") \
245+
== '${{ inputs.checkout_ref || github.sha }}' ]] \
246+
|| fail "the reusable Rust job must bind an explicit source ref before execution"
247+
# shellcheck disable=SC2016 # GitHub expression is an intentional literal workflow contract.
248+
[[ $(yq eval '.jobs.run.steps[] | select(.name == "Run") | .env.CARGO_TARGET_S390X_UNKNOWN_LINUX_GNU_RUSTFLAGS' "$RUST_JOB") \
249+
== '${{ inputs.target == '\''s390x-unknown-linux-gnu'\'' && '\''-C target-feature=+vector'\'' || '\'''\'' }}' ]] \
250+
|| fail "s390x CT jobs must share one explicit vector target environment"
237251
[[ $(yq eval '[.jobs.run.steps[] | select(has("run"))] | length' "$RUST_JOB") -eq 1 ]] \
238252
|| fail "the reusable Rust job must expose one fixed command step"
239253
[[ $(yq eval '.jobs.run.steps[] | select(has("run")) | .run' "$RUST_JOB") == "scripts/ci/run-rust-job.sh" ]] \
@@ -413,6 +427,8 @@ grep -Eq 'HOST_ARGS\+=\(--feature-matrix\)' "$CHECK_ALL" \
413427
|| fail "release recovery must require an explicit existing tag"
414428
[[ $(yq eval '.on.workflow_dispatch.inputs.tag.type' "$RELEASE") == "string" ]] \
415429
|| fail "release recovery tag input must be a string"
430+
[[ $(yq eval '.on.workflow_dispatch.inputs.s390x_ct_run.type' "$RELEASE") == "string" ]] \
431+
|| fail "release recovery s390x CT run input must be a string"
416432
# shellcheck disable=SC2016 # GitHub expressions are intentional literal workflow contracts.
417433
[[ $(yq eval '.jobs.preflight.steps[] | select(.name == "Checkout") | .with.ref' "$RELEASE") \
418434
== '${{ github.event_name == '\''workflow_dispatch'\'' && inputs.tag || github.ref }}' ]] \
@@ -427,6 +443,11 @@ grep -Fq 'refs/heads/main' <<<"$identity_step" \
427443
recovery_tool_step=$(yq eval '.jobs.preflight.steps[] | select(.name == "Install recovery SemVer checker") | .run' "$RELEASE")
428444
grep -Fq 'install-tools.sh" semver' <<<"$recovery_tool_step" \
429445
|| fail "release recovery must use the authenticated SemVer tool installer"
446+
ct_recovery_step=$(yq eval '.jobs.preflight.steps[] | select(.name == "Verify s390x CT recovery evidence") | .run' "$RELEASE")
447+
grep -Fq 'release-ct-recovery-check.sh' <<<"$ct_recovery_step" \
448+
|| fail "release recovery must validate replacement s390x CT evidence"
449+
grep -Fq -- '--workflow-commit "$WORKFLOW_COMMIT"' <<<"$ct_recovery_step" \
450+
|| fail "replacement s390x CT evidence must come from the reviewed workflow commit"
430451
# shellcheck disable=SC2016 # `$crate` is an intentional literal in the release-preflight contract regex.
431452
[[ $(count_matches 'cargo semver-checks --package "\$crate" --all-features' "$RELEASE_PREFLIGHT") -eq 1 ]] \
432453
|| fail "tag preflight must have exactly one final-version SemVer owner"
@@ -626,6 +647,28 @@ grep -Fq 'run-id: ${{ needs.preflight.outputs.weekly_run_id }}' "$RELEASE" \
626647
|| fail "release must consume non-RISC-V CT artifacts from the validated Weekly run"
627648
grep -Fq 'run-id: ${{ needs.preflight.outputs.riscv_run_id }}' "$RELEASE" \
628649
|| fail "release must consume RISC-V CT artifacts from the validated RISC-V run"
650+
grep -Fq 'run-id: ${{ needs.preflight.outputs.s390x_ct_run_id }}' "$RELEASE" \
651+
|| fail "release must consume recovered s390x CT artifacts from the validated recovery run"
652+
[[ $(yq eval '.on.workflow_dispatch.inputs.release_tag.type' "$CT") == "string" ]] \
653+
|| fail "CT recovery release tag input must be a string"
654+
ct_source_step=$(yq eval '.jobs.plan.steps[] | select(.name == "Resolve CT source") | .run' "$CT")
655+
grep -Fq 'refs/heads/main' <<<"$ct_source_step" \
656+
|| fail "release CT recovery must reject workflow code outside protected main"
657+
grep -Fq 'checkout_ref=$RELEASE_TAG' <<<"$ct_source_step" \
658+
|| fail "release CT recovery must bind execution to the immutable tag"
659+
grep -Fq 'PLATFORMS" != "ibm-s390x' <<<"$ct_source_step" \
660+
|| fail "release CT recovery must be limited to the s390x lane"
661+
grep -Fq 'DUDECT_TIMEOUT" != "1800"' <<<"$ct_source_step" \
662+
|| fail "release CT recovery must preserve the release DudeCT timeout"
663+
grep -Fq 'BINSEC_TIMEOUT" != "900"' <<<"$ct_source_step" \
664+
|| fail "release CT recovery must preserve the release BINSEC timeout"
665+
grep -Fq 'UPLOAD_RAW_ARTIFACTS" != "true"' <<<"$ct_source_step" \
666+
|| fail "release CT recovery must retain raw evidence"
667+
grep -Fq 'ARTIFACT_RETENTION_DAYS" != "90"' <<<"$ct_source_step" \
668+
|| fail "release CT recovery must retain evidence for the release lifetime"
669+
# shellcheck disable=SC2016 # GitHub expression is an intentional literal workflow contract.
670+
[[ $(yq eval '.jobs.ct.with.checkout_ref' "$CT") == '${{ needs.plan.outputs.checkout_ref }}' ]] \
671+
|| fail "release CT recovery must execute the resolved immutable tag source"
629672
if grep -Eq 'uses: ./\.github/workflows/(ct|rsa)\.yaml' "$RELEASE"; then
630673
fail "tag workflow must promote exact-commit evidence instead of rerunning CT or RSA"
631674
fi

0 commit comments

Comments
 (0)