Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
with:
enable-cache: false
- id: wheels
uses: LedFx/release-ci/actions/plan@c4a56af516e431cbdb18519889fd0ffa7166f217 # v0.3.0
uses: LedFx/release-ci/actions/plan@442e00705d4e5edd589021851e756761363f9d84 # v0.3.1

lint:
name: Lint (prek)
Expand Down Expand Up @@ -240,7 +240,7 @@ jobs:
id: prepare
env:
GH_TOKEN: ${{ steps.release-token.outputs.token }}
uses: LedFx/release-ci/actions/release@c4a56af516e431cbdb18519889fd0ffa7166f217 # v0.3.0
uses: LedFx/release-ci/actions/release@442e00705d4e5edd589021851e756761363f9d84 # v0.3.1
with:
phase: prepare
project: release-tools
Expand All @@ -259,22 +259,29 @@ jobs:
id: upload
env:
GH_TOKEN: ${{ steps.release-token.outputs.token }}
uses: LedFx/release-ci/actions/release@c4a56af516e431cbdb18519889fd0ffa7166f217 # v0.3.0
uses: LedFx/release-ci/actions/release@442e00705d4e5edd589021851e756761363f9d84 # v0.3.1
with:
phase: check-upload
project: release-tools
dist: dist
snapshot: ${{ runner.temp }}/release-snapshot.json
wheel-plan: ${{ needs.plan.outputs.wheel-plan }}
- name: Stage verified distributions for PyPI
if: steps.upload.outputs.pypi_upload == 'true'
working-directory: ${{ github.workspace }}
run: |
mkdir pypi-dist
cp -- dist/* pypi-dist/
- name: Publish matching missing Python distributions
if: steps.upload.outputs.pypi_upload == 'true'
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
with:
packages-dir: pypi-dist/
skip-existing: true # Both preflights require matching remote SHA-256.
- name: Verify provenance and finalize existing GitHub draft
env:
GH_TOKEN: ${{ steps.release-token.outputs.token }}
uses: LedFx/release-ci/actions/release@c4a56af516e431cbdb18519889fd0ffa7166f217 # v0.3.0
uses: LedFx/release-ci/actions/release@442e00705d4e5edd589021851e756761363f9d84 # v0.3.1
with:
phase: finalize
project: release-tools
Expand Down
85 changes: 78 additions & 7 deletions ci/test_shared_release.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
"""Consumer authority remains explicit around the pinned shared transaction."""

import tomllib
import os
import re
import subprocess
import textwrap
import tomllib
from pathlib import Path
from tempfile import TemporaryDirectory

ROOT = Path(__file__).resolve().parents[1]

Expand All @@ -27,7 +31,7 @@ def test_release_workflow_preserves_identity_gates_and_same_run_artifacts() -> N
assert "run-id:" not in job
assert workflow.count("id-token: write") == 1
pins = re.findall(
r"uses: LedFx/release-ci/actions/release@([0-9a-f]{40}) # (v[0-9]+\.[0-9]+\.[0-9]+)\s*$",
r"uses: LedFx/release-ci/actions/release@([0-9a-f]{40})(?:[ \t]+#.*)?[ \t]*$",
job,
re.MULTILINE,
)
Expand All @@ -36,16 +40,21 @@ def test_release_workflow_preserves_identity_gates_and_same_run_artifacts() -> N
assert job.count("project: release-tools") == 3
assert job.count("wheel-plan: ${{ needs.plan.outputs.wheel-plan }}") == 3
planning = re.findall(
r"uses: LedFx/release-ci/actions/plan@([0-9a-f]{40}) # (v[0-9]+\.[0-9]+\.[0-9]+)\s*$",
r"uses: LedFx/release-ci/actions/plan@([0-9a-f]{40})(?:[ \t]+#.*)?[ \t]*$",
workflow,
re.MULTILINE,
)
assert planning == [pins[0]]
assert "sparse-checkout-cone-mode: false" in job
assert "pyproject.toml" in job
assert "policy:" not in workflow
assert "uv run --frozen --only-group wheel-build python -m cibuildwheel ." in workflow
assert '--config-file pyproject.toml --platform "$PLATFORM" --archs "$ARCH"' in workflow
assert (
"uv run --frozen --only-group wheel-build python -m cibuildwheel ." in workflow
)
assert (
'--config-file pyproject.toml --platform "$PLATFORM" --archs "$ARCH"'
in workflow
)
assert "uses: pypa/cibuildwheel@" not in workflow
assert (
job.index("phase: prepare")
Expand All @@ -65,7 +74,9 @@ def test_pyproject_keeps_native_portable_matrix() -> None:
assert all({"runner", "platform", "arch"} <= set(row) for row in rows)
dependencies = config["dependency-groups"]["wheel-build"]
assert len(dependencies) == 1
assert re.fullmatch(r"cibuildwheel(?:\[uv\])?==[0-9]+\.[0-9]+\.[0-9]+", dependencies[0])
assert re.fullmatch(
r"cibuildwheel(?:\[uv\])?==[0-9]+\.[0-9]+\.[0-9]+", dependencies[0]
)
assert config["project"]["name"] == "pyfastnoiselite-ledfx"
assert set(config["tool"]["release-ci"]) == {"targets"}
assert not (ROOT / ".github/release-policy.json").exists()
Expand All @@ -76,7 +87,67 @@ def test_pyproject_keeps_native_portable_matrix() -> None:
assert any(row["arch"] == "armv7l" for row in rows)


def test_upload_sidecars_leave_frozen_inputs_unchanged(tmp_path: Path) -> None:
workflow = (ROOT / ".github/workflows/build.yml").read_text()
match = re.search(
r"(?m)^ - name: Stage verified distributions for PyPI\n"
r"(?:(?:^ .*\n)|(?:^\n))*?^ run: \|\n"
r"((?:^ .*\n|^\n)+)",
workflow,
)
assert match is not None, "The uploader needs a separate verified input copy"
stage = workflow.split(" - name: Stage verified distributions for PyPI\n", 1)[
1
].split("\n - ", 1)[0]
assert "if: steps.upload.outputs.pypi_upload == 'true'" in stage
assert "working-directory: ${{ github.workspace }}" in stage
assert workflow.index("phase: check-upload") < workflow.index(
"Stage verified distributions for PyPI"
)
uploader = workflow.split("uses: pypa/gh-action-pypi-publish@", 1)[1].split(
"\n - ", 1
)[0]
assert "packages-dir: pypi-dist/" in uploader
script = textwrap.dedent(match.group(1))
original = tmp_path / "dist"
original.mkdir()
frozen = {
"example-1.0-py3-none-any.whl": b"tested wheel",
"example-1.0.tar.gz": b"tested sdist",
}
for name, data in frozen.items():
(original / name).write_bytes(data)
result = subprocess.run(
["bash", "-euo", "pipefail", "-c", script],
cwd=tmp_path,
env={**os.environ, "GITHUB_WORKSPACE": str(tmp_path)},
capture_output=True,
check=False,
)
assert result.returncode == 0, result.stderr
staging = tmp_path / "pypi-dist"
assert {p.name: p.read_bytes() for p in staging.iterdir()} == frozen
for name in frozen:
(staging / (name + ".publish.attestation")).write_bytes(
b"generated PyPI sidecar"
)
assert {p.name: p.read_bytes() for p in original.iterdir()} == frozen
before_retry = {p.name: p.read_bytes() for p in staging.iterdir()}
retry = subprocess.run(
["bash", "-euo", "pipefail", "-c", script],
cwd=tmp_path,
env={**os.environ, "GITHUB_WORKSPACE": str(tmp_path)},
capture_output=True,
check=False,
)
assert retry.returncode != 0
assert {p.name: p.read_bytes() for p in staging.iterdir()} == before_retry
assert {p.name: p.read_bytes() for p in original.iterdir()} == frozen


if __name__ == "__main__":
test_release_workflow_preserves_identity_gates_and_same_run_artifacts()
test_pyproject_keeps_native_portable_matrix()
print("2 shared publication contracts passed")
with TemporaryDirectory() as directory:
test_upload_sidecars_leave_frozen_inputs_unchanged(Path(directory))
print("3 shared publication contracts passed")
Loading