mirror of
https://github.com/elder-plinius/OBLITERATUS.git
synced 2026-08-17 16:37:30 +02:00
ci: verify installed distributions
This commit is contained in:
@@ -17,5 +17,6 @@
|
||||
- [ ] The full test suite passes locally or the CI result is linked.
|
||||
- [ ] Documentation or examples were updated when user-facing behavior changed.
|
||||
- [ ] No secrets, credentials, generated provider files, or unrelated changes are included.
|
||||
- [ ] Any CI action or standalone-tool pin change also updates `ci/digests.txt`.
|
||||
|
||||
Legacy cleanup PRs may receive missing tests as a one-time maintainer courtesy when the change is already otherwise clean. New changes are expected to include relevant tests and keep the full suite passing.
|
||||
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
requirements*.txt
|
||||
|
||||
- name: Install build tooling
|
||||
run: python -m pip install "build==1.2.2.post1"
|
||||
run: python -m pip install "build==1.2.2.post1" "twine==7.0.0"
|
||||
|
||||
- name: Build source and wheel distributions
|
||||
run: python -m build --sdist --wheel
|
||||
@@ -75,6 +75,85 @@ jobs:
|
||||
print(f"verified wheel contents: {wheels[0]}")
|
||||
PY
|
||||
|
||||
- name: Validate distribution metadata
|
||||
run: |
|
||||
mkdir -p package-evidence
|
||||
python -m twine check dist/* | tee package-evidence/twine-check.txt
|
||||
sha256sum dist/* | tee package-evidence/SHA256SUMS
|
||||
|
||||
- name: Verify installed wheel contract
|
||||
run: |
|
||||
mapfile -t wheels < <(find "$GITHUB_WORKSPACE/dist" -maxdepth 1 -type f -name '*.whl' -print)
|
||||
if [ "${#wheels[@]}" -ne 1 ]; then
|
||||
echo "expected exactly one wheel, found ${#wheels[@]}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
wheel_env="$RUNNER_TEMP/obliteratus-wheel-env"
|
||||
wheel_cwd="$RUNNER_TEMP/obliteratus-wheel-cwd"
|
||||
python -m venv "$wheel_env"
|
||||
mkdir -p "$wheel_cwd"
|
||||
"$wheel_env/bin/python" -m pip install --no-cache-dir "rich==15.0.0"
|
||||
"$wheel_env/bin/python" -m pip install --no-cache-dir --no-deps "${wheels[0]}"
|
||||
|
||||
cd "$wheel_cwd"
|
||||
"$wheel_env/bin/python" -I - <<'PY' | tee "$GITHUB_WORKSPACE/package-evidence/wheel-import.txt"
|
||||
import importlib.metadata
|
||||
from pathlib import Path
|
||||
|
||||
import obliteratus
|
||||
|
||||
origin = Path(obliteratus.__file__).resolve()
|
||||
assert "site-packages" in origin.parts, origin
|
||||
assert obliteratus.__version__ == importlib.metadata.version("obliteratus")
|
||||
print(f"installed wheel import: {origin}")
|
||||
print(f"version: {obliteratus.__version__}")
|
||||
PY
|
||||
"$wheel_env/bin/python" -I -m obliteratus --help > "$GITHUB_WORKSPACE/package-evidence/wheel-module-help.txt"
|
||||
"$wheel_env/bin/obliteratus" --help > "$GITHUB_WORKSPACE/package-evidence/wheel-console-help.txt"
|
||||
|
||||
- name: Verify installed sdist contract
|
||||
run: |
|
||||
mapfile -t sdists < <(find "$GITHUB_WORKSPACE/dist" -maxdepth 1 -type f -name '*.tar.gz' -print)
|
||||
if [ "${#sdists[@]}" -ne 1 ]; then
|
||||
echo "expected exactly one sdist, found ${#sdists[@]}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sdist_env="$RUNNER_TEMP/obliteratus-sdist-env"
|
||||
sdist_cwd="$RUNNER_TEMP/obliteratus-sdist-cwd"
|
||||
python -m venv "$sdist_env"
|
||||
mkdir -p "$sdist_cwd"
|
||||
"$sdist_env/bin/python" -m pip install --no-cache-dir "rich==15.0.0"
|
||||
"$sdist_env/bin/python" -m pip install --no-cache-dir --no-deps "${sdists[0]}"
|
||||
|
||||
cd "$sdist_cwd"
|
||||
"$sdist_env/bin/python" -I - <<'PY' | tee "$GITHUB_WORKSPACE/package-evidence/sdist-import.txt"
|
||||
import importlib.metadata
|
||||
from pathlib import Path
|
||||
|
||||
import obliteratus
|
||||
|
||||
origin = Path(obliteratus.__file__).resolve()
|
||||
assert "site-packages" in origin.parts, origin
|
||||
assert obliteratus.__version__ == importlib.metadata.version("obliteratus")
|
||||
print(f"installed sdist import: {origin}")
|
||||
print(f"version: {obliteratus.__version__}")
|
||||
PY
|
||||
"$sdist_env/bin/python" -I -m obliteratus --help > "$GITHUB_WORKSPACE/package-evidence/sdist-module-help.txt"
|
||||
"$sdist_env/bin/obliteratus" --help > "$GITHUB_WORKSPACE/package-evidence/sdist-console-help.txt"
|
||||
|
||||
- name: Upload distributions and package evidence
|
||||
if: always()
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: distributions-py3.12
|
||||
path: |
|
||||
dist/
|
||||
package-evidence/
|
||||
if-no-files-found: error
|
||||
retention-days: 14
|
||||
|
||||
lint:
|
||||
name: Ruff
|
||||
runs-on: ubuntu-latest
|
||||
@@ -94,6 +173,21 @@ jobs:
|
||||
- name: Install Ruff
|
||||
run: python -m pip install "ruff==0.8.6"
|
||||
|
||||
- name: Install actionlint with checksum verification
|
||||
env:
|
||||
ACTIONLINT_VERSION: "1.7.12"
|
||||
ACTIONLINT_SHA256: "8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8"
|
||||
run: |
|
||||
archive="$RUNNER_TEMP/actionlint.tar.gz"
|
||||
curl -fsSLo "$archive" \
|
||||
"https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz"
|
||||
echo "${ACTIONLINT_SHA256} ${archive}" | sha256sum -c -
|
||||
tar -xzf "$archive" -C "$RUNNER_TEMP" actionlint
|
||||
|
||||
- name: Validate GitHub Actions workflows
|
||||
run: |
|
||||
"$RUNNER_TEMP/actionlint" -no-color
|
||||
|
||||
- name: Enforce Ruff F gate
|
||||
run: >-
|
||||
python -m ruff check --select F obliteratus tests
|
||||
|
||||
@@ -50,6 +50,11 @@ Canonical required checks:
|
||||
- `python -c 'import obliteratus; print(obliteratus.__version__)'`
|
||||
- `python -m obliteratus --help`
|
||||
|
||||
CI additionally validates wheel and sdist metadata, installs each distribution
|
||||
in an independent environment outside the checkout, exercises both CLI entry
|
||||
paths, and retains the distributions plus evidence. Immutable CI action/tool
|
||||
pins are recorded in [ci/digests.txt](ci/digests.txt).
|
||||
|
||||
GPU, MPS, model-download, network, and remote-execution checks are conditional
|
||||
release or risk-surface gates, not part of the default CPU job.
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# kind name immutable pin resolved version pinned rationale
|
||||
action actions/checkout 3d3c42e5aac5ba805825da76410c181273ba90b1 v7.0.1 2026-08-14 baseline pin (#59)
|
||||
action actions/setup-python 5fda3b95a4ea91299a34e894583c3862153e4b97 v7.0.0 2026-08-14 baseline pin (#59)
|
||||
action actions/upload-artifact 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a v7.0.1 2026-08-14 test/package evidence (#64, #65)
|
||||
tool rhysd/actionlint sha256:8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8 v1.7.12 2026-08-14 workflow syntax/policy gate (#65)
|
||||
@@ -0,0 +1,47 @@
|
||||
"""Repository contracts for immutable CI execution dependencies."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).parents[1]
|
||||
WORKFLOW = ROOT / ".github" / "workflows" / "ci.yml"
|
||||
MANIFEST = ROOT / "ci" / "digests.txt"
|
||||
ACTION_REF = re.compile(
|
||||
r"^\s*uses:\s*([A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+)@([0-9a-f]{40})\s+#\s+(\S+)\s*$",
|
||||
)
|
||||
|
||||
|
||||
def _manifest_entries() -> dict[tuple[str, str], tuple[str, str]]:
|
||||
entries: dict[tuple[str, str], tuple[str, str]] = {}
|
||||
for line in MANIFEST.read_text(encoding="utf-8").splitlines():
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
kind, name, pin, version, _date, _rationale = line.split(maxsplit=5)
|
||||
entries[(kind, name)] = (pin, version)
|
||||
return entries
|
||||
|
||||
|
||||
def test_every_external_action_is_sha_pinned_and_manifested():
|
||||
entries = _manifest_entries()
|
||||
uses_lines = [
|
||||
line for line in WORKFLOW.read_text(encoding="utf-8").splitlines()
|
||||
if "uses:" in line and "uses: ./" not in line
|
||||
]
|
||||
|
||||
assert uses_lines
|
||||
for line in uses_lines:
|
||||
match = ACTION_REF.match(line)
|
||||
assert match is not None, f"external action is not SHA-pinned with a version comment: {line}"
|
||||
name, pin, version = match.groups()
|
||||
assert entries[("action", name)] == (pin, version)
|
||||
|
||||
|
||||
def test_actionlint_version_and_checksum_match_manifest():
|
||||
workflow = WORKFLOW.read_text(encoding="utf-8")
|
||||
pin, version = _manifest_entries()[("tool", "rhysd/actionlint")]
|
||||
|
||||
assert f'ACTIONLINT_VERSION: "{version.removeprefix("v")}"' in workflow
|
||||
assert f'ACTIONLINT_SHA256: "{pin.removeprefix("sha256:")}"' in workflow
|
||||
Reference in New Issue
Block a user