From 230db49939376c879804ba1f28bd0a26c4f7fc34 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 13 Oct 2026 15:03:03 +0900 Subject: [PATCH 1/3] Keep fast-mlsirm release license selection in central workflow --- .github/workflows/fast-mlsirm-publish-pypi.yml | 12 ++++++++++++ tests/test_fast_mlsirm_self_hosted_workflows.py | 13 +++++++++++++ 2 files changed, 25 insertions(+) diff --git a/.github/workflows/fast-mlsirm-publish-pypi.yml b/.github/workflows/fast-mlsirm-publish-pypi.yml index 35ba47ced3..b52c49955e 100644 --- a/.github/workflows/fast-mlsirm-publish-pypi.yml +++ b/.github/workflows/fast-mlsirm-publish-pypi.yml @@ -536,6 +536,9 @@ jobs: # MATURIN_, ...) into the manylinux container, so SOURCE_DATE_EPOCH must # be passed explicitly via docker-options for the Linux legs. Native # macOS/Windows builds inherit it from the job env. + - name: Select reviewed license bundle for wheel target + shell: bash + run: bash tools/select_third_party_license.sh "${{ matrix.target }}" - name: Build wheel uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b env: @@ -548,6 +551,8 @@ jobs: docker-options: -e SOURCE_DATE_EPOCH before-script-linux: $CARGO_BUILD_PYTHON trusted-control/scripts/ci/capture_release_build_scope.py args: --release --out dist -i ${{ matrix.interpreter }} + - name: Verify wheel license bytes + run: python tools/verify_wheel_license.py dist - name: Capture first native-runner build graph if: ${{ runner.os != 'Linux' }} env: @@ -671,6 +676,10 @@ jobs: env: RELEASE_COMMIT: ${{ needs.verify-release.outputs.release_commit }} run: python trusted-control/scripts/ci/release_sdist_consumer.py prepare sdist-proof/sdist.tsv sdist-input sdist-consumer + - name: Select reviewed license bundle for sdist consumer target + working-directory: sdist-consumer/source + shell: bash + run: bash tools/select_third_party_license.sh "${{ matrix.target }}" - name: Build this wheel target from the verified sdist uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b with: @@ -682,6 +691,9 @@ jobs: maturin-version: v1.15.0 docker-options: -e SOURCE_DATE_EPOCH args: --release --out dist-consumer -i ${{ matrix.interpreter }} + - name: Verify sdist consumer wheel license bytes + working-directory: sdist-consumer/source + run: python tools/verify_wheel_license.py dist-consumer - name: Capture target sdist consumer wheel shell: bash env: diff --git a/tests/test_fast_mlsirm_self_hosted_workflows.py b/tests/test_fast_mlsirm_self_hosted_workflows.py index 16905cce94..7173ae4168 100644 --- a/tests/test_fast_mlsirm_self_hosted_workflows.py +++ b/tests/test_fast_mlsirm_self_hosted_workflows.py @@ -67,6 +67,19 @@ def test_fast_mlsirm_reusable_workflows_keep_isolated_runner_and_gate_contracts( assert "cargo test --locked --manifest-path crates/fast-mlsirm-py/Cargo.toml" in ci +def test_release_wheel_and_sdist_consumer_keep_selected_license_bytes(): + """Keep target selection and byte verification around both actual builds.""" + text = workflow_text("fast-mlsirm-publish-pypi.yml") + wheel_selection = ' - name: Select reviewed license bundle for wheel target\n shell: bash\n run: bash tools/select_third_party_license.sh "${{ matrix.target }}"\n' + wheel_verification = ' - name: Verify wheel license bytes\n run: python tools/verify_wheel_license.py dist\n' + consumer_selection = ' - name: Select reviewed license bundle for sdist consumer target\n working-directory: sdist-consumer/source\n shell: bash\n run: bash tools/select_third_party_license.sh "${{ matrix.target }}"\n' + consumer_verification = ' - name: Verify sdist consumer wheel license bytes\n working-directory: sdist-consumer/source\n run: python tools/verify_wheel_license.py dist-consumer\n' + for step in (wheel_selection, wheel_verification, consumer_selection, consumer_verification): + assert text.count(step) == 1 + assert text.index(wheel_selection) < text.index(" - name: Build wheel\n") < text.index(wheel_verification) < text.index(" - name: Capture first native-runner build graph\n") + assert text.index(" - name: Verify and unpack the same-run sdist\n") < text.index(consumer_selection) < text.index(" - name: Build this wheel target from the verified sdist\n") < text.index(consumer_verification) < text.index(" - name: Capture target sdist consumer wheel\n") + + def test_disabled_release_legs_fail_the_caller_instead_of_reporting_success(): """Expose the intentional release hold as a failing required job.""" text = workflow_text("fast-mlsirm-publish-pypi.yml") From b21c936edb30e7ca11728fa0f255d4ecb87eca0f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 13 Oct 2026 15:49:05 +0900 Subject: [PATCH 2/3] Fail early when release source lacks license helpers --- .github/workflows/fast-mlsirm-publish-pypi.yml | 9 +++++++++ tests/test_fast_mlsirm_self_hosted_workflows.py | 15 +++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/.github/workflows/fast-mlsirm-publish-pypi.yml b/.github/workflows/fast-mlsirm-publish-pypi.yml index b52c49955e..312535da98 100644 --- a/.github/workflows/fast-mlsirm-publish-pypi.yml +++ b/.github/workflows/fast-mlsirm-publish-pypi.yml @@ -129,6 +129,15 @@ jobs: f"release tag does not match project version: expected {expected!r}, got {actual!r}" ) PY + - name: Require reviewed release license helpers + run: | + set -euo pipefail + for helper in tools/select_third_party_license.sh tools/verify_wheel_license.py; do + if [ ! -f "$helper" ] || [ -L "$helper" ]; then + echo "::error::Release source lacks the reviewed license helper: $helper" + exit 1 + fi + done # Reproducible-build clock: the committer time of the exact release # commit, never the wall clock. maturin stamps it into the embedded # CycloneDX SBOM (timestamp/serialNumber) and archive metadata; without it diff --git a/tests/test_fast_mlsirm_self_hosted_workflows.py b/tests/test_fast_mlsirm_self_hosted_workflows.py index 7173ae4168..b456032a3e 100644 --- a/tests/test_fast_mlsirm_self_hosted_workflows.py +++ b/tests/test_fast_mlsirm_self_hosted_workflows.py @@ -1,4 +1,5 @@ from pathlib import Path +import subprocess import textwrap from xml.etree import ElementTree as ET @@ -80,6 +81,20 @@ def test_release_wheel_and_sdist_consumer_keep_selected_license_bytes(): assert text.index(" - name: Verify and unpack the same-run sdist\n") < text.index(consumer_selection) < text.index(" - name: Build this wheel target from the verified sdist\n") < text.index(consumer_verification) < text.index(" - name: Capture target sdist consumer wheel\n") +def test_older_release_source_without_license_helpers_fails_before_build(tmp_path): + """Historical commits without the new helpers cannot enter the matrix.""" + text = workflow_text("fast-mlsirm-publish-pypi.yml") + step = text.split(" - name: Require reviewed release license helpers\n", 1)[1] + script = textwrap.dedent(step.split(" run: |\n", 1)[1].split("\n # Reproducible-build clock:", 1)[0]) + assert text.index(" - name: Require reviewed release license helpers\n") < text.index(" sdist:\n") + assert subprocess.run(["bash", "-e", "-c", script], cwd=tmp_path, capture_output=True).returncode == 1 + tools = tmp_path / "tools" + tools.mkdir() + for name in ("select_third_party_license.sh", "verify_wheel_license.py"): + (tools / name).write_text("reviewed helper fixture") + assert subprocess.run(["bash", "-e", "-c", script], cwd=tmp_path, capture_output=True).returncode == 0 + + def test_disabled_release_legs_fail_the_caller_instead_of_reporting_success(): """Expose the intentional release hold as a failing required job.""" text = workflow_text("fast-mlsirm-publish-pypi.yml") From 0098f5a4b4be280456a28817fa286eff84255d42 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 14 Oct 2026 06:42:31 +0900 Subject: [PATCH 3/3] ci(fast-mlsirm): pin license gate to merged control plane --- .github/workflows/fast-mlsirm-publish-pypi.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/fast-mlsirm-publish-pypi.yml b/.github/workflows/fast-mlsirm-publish-pypi.yml index 312535da98..325974b0c4 100644 --- a/.github/workflows/fast-mlsirm-publish-pypi.yml +++ b/.github/workflows/fast-mlsirm-publish-pypi.yml @@ -1105,7 +1105,7 @@ jobs: dependency-gate: name: complete dependency licence and Strix gate needs: [verify-release, reproducibility-record] - uses: ContextualWisdomLab/.github/.github/workflows/release-dependency-license-strix-gate.yml@b6cebb36dc11afe409a7fee8a3262827255c029c + uses: ContextualWisdomLab/.github/.github/workflows/release-dependency-license-strix-gate.yml@e249236e3f420c4e9c470f26925640dae59f0d93 with: source_repository: ${{ github.repository }} source_sha: ${{ needs.verify-release.outputs.release_commit }}