mirror of
https://github.com/elder-plinius/OBLITERATUS.git
synced 2026-08-18 00:47:23 +02:00
632 lines
24 KiB
YAML
632 lines
24 KiB
YAML
name: CI
|
|
|
|
on:
|
|
pull_request:
|
|
push:
|
|
branches:
|
|
- main
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: ci-${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
PIP_DISABLE_PIP_VERSION_CHECK: "1"
|
|
PIP_NO_INPUT: "1"
|
|
UV_VERSION: "0.12.4"
|
|
|
|
jobs:
|
|
package:
|
|
name: Package
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 15
|
|
env:
|
|
BUILD_TOOLS: /tmp/obliteratus-ci-tools
|
|
|
|
steps:
|
|
- name: Check out repository
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
with:
|
|
python-version: "3.12"
|
|
cache: pip
|
|
cache-dependency-path: |
|
|
pyproject.toml
|
|
uv.lock
|
|
|
|
- name: Install build tooling
|
|
run: |
|
|
python -m pip install "uv==${UV_VERSION}"
|
|
UV_PROJECT_ENVIRONMENT="$BUILD_TOOLS" uv sync --locked --only-group ci
|
|
|
|
- name: Build source and wheel distributions
|
|
run: >-
|
|
"$BUILD_TOOLS/bin/python" -m build --sdist --wheel
|
|
|
|
- name: Verify wheel contents and entry point
|
|
run: |
|
|
python - <<'PY'
|
|
from pathlib import Path
|
|
from zipfile import ZipFile
|
|
|
|
wheels = list(Path("dist").glob("*.whl"))
|
|
if len(wheels) != 1:
|
|
raise SystemExit(f"expected one wheel, found: {wheels}")
|
|
|
|
with ZipFile(wheels[0]) as archive:
|
|
names = set(archive.namelist())
|
|
required = {
|
|
"app.py",
|
|
"obliteratus/__init__.py",
|
|
"obliteratus/local_ui.py",
|
|
}
|
|
missing = sorted(required - names)
|
|
if missing:
|
|
raise SystemExit(f"wheel is missing required modules: {missing}")
|
|
|
|
entry_points = [
|
|
name for name in names if name.endswith(".dist-info/entry_points.txt")
|
|
]
|
|
if len(entry_points) != 1:
|
|
raise SystemExit(f"expected one entry_points.txt, found: {entry_points}")
|
|
contents = archive.read(entry_points[0]).decode("utf-8")
|
|
if "obliteratus = obliteratus.cli:main" not in contents:
|
|
raise SystemExit("wheel is missing the obliteratus console entry point")
|
|
|
|
print(f"verified wheel contents: {wheels[0]}")
|
|
PY
|
|
|
|
- name: Validate distribution metadata
|
|
run: |
|
|
mkdir -p package-evidence
|
|
"$BUILD_TOOLS/bin/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
|
|
timeout-minutes: 15
|
|
|
|
steps:
|
|
- name: Check out repository
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
with:
|
|
python-version: "3.12"
|
|
cache: pip
|
|
cache-dependency-path: |
|
|
pyproject.toml
|
|
uv.lock
|
|
|
|
- name: Install Ruff
|
|
run: python -m pip install "ruff==0.16.2"
|
|
|
|
- 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 app.py obliteratus tests
|
|
scripts/check_coverage_thresholds.py
|
|
scripts/check_mutation_score.py
|
|
scripts/check_quality_policy.py
|
|
scripts/check_mutation_targets.py
|
|
scripts/prepare_mutation_coverage.py
|
|
scripts/run_prepared_mutmut.py
|
|
scripts/mutmut_coverage_sitecustomize/sitecustomize.py
|
|
scripts/check_conditional_policy.py
|
|
scripts/check_test_risk_map.py
|
|
scripts/conditional_gate_summary.py
|
|
scripts/run_conditional_gate.py
|
|
scripts/run_repeat_gate.py
|
|
scripts/write_test_evidence.py
|
|
scripts/check_supply_chain_policy.py
|
|
scripts/gemma4_12b_recursive_loop.py
|
|
|
|
- name: Report E501 legacy baseline
|
|
if: always()
|
|
run: >-
|
|
python -m ruff check --select E501 --statistics app.py obliteratus tests
|
|
scripts/check_coverage_thresholds.py
|
|
scripts/check_mutation_score.py
|
|
scripts/check_quality_policy.py
|
|
scripts/check_mutation_targets.py
|
|
scripts/prepare_mutation_coverage.py
|
|
scripts/run_prepared_mutmut.py
|
|
scripts/mutmut_coverage_sitecustomize/sitecustomize.py
|
|
scripts/check_conditional_policy.py
|
|
scripts/check_test_risk_map.py
|
|
scripts/conditional_gate_summary.py
|
|
scripts/run_conditional_gate.py
|
|
scripts/run_repeat_gate.py
|
|
scripts/write_test_evidence.py
|
|
scripts/check_supply_chain_policy.py
|
|
scripts/gemma4_12b_recursive_loop.py || true
|
|
|
|
test:
|
|
name: Tests py${{ matrix.python-version }}
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
python-version:
|
|
- "3.10"
|
|
- "3.11"
|
|
- "3.12"
|
|
env:
|
|
CUDA_VISIBLE_DEVICES: ""
|
|
HF_DATASETS_OFFLINE: "1"
|
|
HF_HUB_DISABLE_TELEMETRY: "1"
|
|
HF_HUB_OFFLINE: "1"
|
|
TOKENIZERS_PARALLELISM: "false"
|
|
TRANSFORMERS_OFFLINE: "1"
|
|
TEST_ENV: /tmp/obliteratus-test-env
|
|
|
|
steps:
|
|
- name: Check out repository
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
with:
|
|
python-version: ${{ matrix.python-version }}
|
|
cache: pip
|
|
cache-dependency-path: |
|
|
pyproject.toml
|
|
uv.lock
|
|
|
|
- name: Install locked package and test tools
|
|
run: |
|
|
python -m pip install "uv==${UV_VERSION}"
|
|
UV_PROJECT_ENVIRONMENT="$TEST_ENV" \
|
|
uv sync --locked --no-default-groups --extra dev --no-editable
|
|
|
|
- name: Smoke import and CLI
|
|
run: |
|
|
"$TEST_ENV/bin/python" - <<'PY'
|
|
import obliteratus
|
|
|
|
version = getattr(obliteratus, "__version__", None)
|
|
if version is not None:
|
|
print(f"obliteratus version: {version}")
|
|
else:
|
|
print("obliteratus import: ok")
|
|
PY
|
|
"$TEST_ENV/bin/python" -m obliteratus --help
|
|
|
|
- name: Run tests with coverage
|
|
run: |
|
|
mkdir -p test-results
|
|
"$TEST_ENV/bin/python" -m pytest \
|
|
-m "not slow and not gpu and not mps and not mlx and not network and not download and not remote and not operator_ui" \
|
|
--cov-branch \
|
|
--cov-fail-under=0 \
|
|
--junitxml="test-results/junit-py${{ matrix.python-version }}.xml" \
|
|
--cov-report="xml:test-results/coverage-py${{ matrix.python-version }}.xml" \
|
|
--cov-report="json:test-results/coverage-py${{ matrix.python-version }}.json"
|
|
|
|
- name: Generate exact-base coverage for module regression comparison
|
|
if: matrix.python-version == '3.12'
|
|
env:
|
|
BASE_TEST_ENV: /tmp/obliteratus-base-test-env
|
|
BASE_WORKTREE: /tmp/obliteratus-base-worktree
|
|
COVERAGE_BASE: ${{ github.event.pull_request.base.sha || github.event.before }}
|
|
run: |
|
|
git rev-parse --verify "${COVERAGE_BASE}^{commit}"
|
|
git worktree add --detach "$BASE_WORKTREE" "$COVERAGE_BASE"
|
|
(
|
|
cd "$BASE_WORKTREE"
|
|
UV_PROJECT_ENVIRONMENT="$BASE_TEST_ENV" \
|
|
uv sync --locked --no-default-groups --extra dev --no-editable
|
|
COVERAGE_FILE="$RUNNER_TEMP/.coverage-base" \
|
|
"$BASE_TEST_ENV/bin/python" -m pytest \
|
|
-m "not slow and not gpu and not mps and not mlx and not network and not download and not remote and not operator_ui" \
|
|
--cov-branch \
|
|
--cov-fail-under=0 \
|
|
--cov-report="json:$GITHUB_WORKSPACE/test-results/base-coverage-py3.12.json"
|
|
) | tee test-results/base-tests-py3.12.log
|
|
|
|
- name: Enforce line and branch coverage floors
|
|
env:
|
|
COVERAGE_BASE: ${{ github.event.pull_request.base.sha || github.event.before }}
|
|
run: |
|
|
module_args=()
|
|
if [ "${{ matrix.python-version }}" = "3.12" ]; then
|
|
module_args=(
|
|
--base-report test-results/base-coverage-py3.12.json
|
|
--touched-module-no-regression
|
|
--new-module-min-line 80
|
|
--new-module-min-branch 75
|
|
)
|
|
fi
|
|
"$TEST_ENV/bin/python" scripts/check_coverage_thresholds.py \
|
|
"test-results/coverage-py${{ matrix.python-version }}.json" \
|
|
--min-line 75 \
|
|
--min-branch 60 \
|
|
--min-file obliteratus/device.py=70 \
|
|
--min-file obliteratus/models/loader.py=70 \
|
|
--min-file obliteratus/architecture_profiles.py=70 \
|
|
--min-file obliteratus/cli.py=70 \
|
|
--min-file obliteratus/mlx_backend.py=70 \
|
|
--min-file obliteratus/evaluation/metrics.py=70 \
|
|
--min-file obliteratus/evaluation/advanced_metrics.py=70 \
|
|
--min-file obliteratus/reporting/report.py=70 \
|
|
--min-file obliteratus/community.py=70 \
|
|
--min-file obliteratus/telemetry.py=70 \
|
|
--min-changed 95 \
|
|
--base-ref "$COVERAGE_BASE" \
|
|
"${module_args[@]}"
|
|
|
|
- name: Enforce mature CPU-scope coverage and immutable quality policy
|
|
run: |
|
|
"$TEST_ENV/bin/python" scripts/check_quality_policy.py \
|
|
--policy ci/test-quality-policy.json \
|
|
--coverage "test-results/coverage-py${{ matrix.python-version }}.json"
|
|
"$TEST_ENV/bin/python" scripts/check_conditional_policy.py
|
|
"$TEST_ENV/bin/python" scripts/check_test_risk_map.py
|
|
|
|
- name: Write normalized test trend evidence
|
|
if: always()
|
|
env:
|
|
COVERAGE_BASE: ${{ github.event.pull_request.base.sha || github.event.before }}
|
|
run: |
|
|
base_args=()
|
|
if [ -f test-results/base-coverage-py3.12.json ]; then
|
|
base_args=(--base-coverage test-results/base-coverage-py3.12.json)
|
|
fi
|
|
"$TEST_ENV/bin/python" scripts/write_test_evidence.py \
|
|
--coverage "test-results/coverage-py${{ matrix.python-version }}.json" \
|
|
--junit "test-results/junit-py${{ matrix.python-version }}.xml" \
|
|
--head-sha "$GITHUB_SHA" \
|
|
--base-sha "$COVERAGE_BASE" \
|
|
--python-version "${{ matrix.python-version }}" \
|
|
--output "test-results/test-trend-py${{ matrix.python-version }}.json" \
|
|
"${base_args[@]}"
|
|
|
|
- name: Enforce normalized test duration budgets
|
|
run: |
|
|
"$TEST_ENV/bin/python" scripts/check_quality_policy.py \
|
|
--policy ci/test-quality-policy.json \
|
|
--evidence "test-results/test-trend-py${{ matrix.python-version }}.json"
|
|
|
|
- name: Upload test and coverage evidence
|
|
if: always()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: test-evidence-py${{ matrix.python-version }}
|
|
path: test-results/
|
|
if-no-files-found: error
|
|
retention-days: 90
|
|
|
|
quality-depth:
|
|
name: Quality depth
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 45
|
|
env:
|
|
CUDA_VISIBLE_DEVICES: ""
|
|
HF_DATASETS_OFFLINE: "1"
|
|
HF_HUB_DISABLE_TELEMETRY: "1"
|
|
HF_HUB_OFFLINE: "1"
|
|
TOKENIZERS_PARALLELISM: "false"
|
|
TRANSFORMERS_OFFLINE: "1"
|
|
QUALITY_ENV: /tmp/obliteratus-quality-env
|
|
|
|
steps:
|
|
- name: Check out repository
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
with:
|
|
python-version: "3.12"
|
|
cache: pip
|
|
cache-dependency-path: |
|
|
pyproject.toml
|
|
uv.lock
|
|
|
|
- name: Install locked quality tooling
|
|
run: |
|
|
python -m pip install "uv==${UV_VERSION}"
|
|
UV_PROJECT_ENVIRONMENT="$QUALITY_ENV" \
|
|
uv sync --locked --extra dev --group quality --no-editable
|
|
mkdir -p quality-evidence
|
|
|
|
- name: Run deterministic order and hash-seed repeats
|
|
run: >-
|
|
"$QUALITY_ENV/bin/python" scripts/run_repeat_gate.py
|
|
--output quality-evidence/repeat-gate.json
|
|
|
|
- name: Run bounded selective mutation gate
|
|
env:
|
|
BLIS_NUM_THREADS: "1"
|
|
MKL_NUM_THREADS: "1"
|
|
NUMEXPR_NUM_THREADS: "1"
|
|
OMP_THREAD_LIMIT: "1"
|
|
OMP_NUM_THREADS: "1"
|
|
OPENBLAS_NUM_THREADS: "1"
|
|
VECLIB_MAXIMUM_THREADS: "1"
|
|
run: |
|
|
"$QUALITY_ENV/bin/python" scripts/check_mutation_targets.py prepare
|
|
# shellcheck disable=SC2016
|
|
/usr/bin/time -f 'elapsed_seconds=%e\nmax_rss_kb=%M' \
|
|
-o quality-evidence/mutation-time.txt \
|
|
bash -euo pipefail -c '
|
|
"$QUALITY_ENV/bin/python" scripts/prepare_mutation_coverage.py prepare-coverage --max-children 8
|
|
"$QUALITY_ENV/bin/python" scripts/prepare_mutation_coverage.py prepare-stats --max-children 8
|
|
PATH="$QUALITY_ENV/bin:$PATH" \
|
|
"$QUALITY_ENV/bin/python" scripts/run_prepared_mutmut.py run --max-children 8
|
|
'
|
|
"$QUALITY_ENV/bin/python" scripts/check_mutation_targets.py check
|
|
"$QUALITY_ENV/bin/mutmut" results > quality-evidence/mutation-survivors.txt
|
|
"$QUALITY_ENV/bin/mutmut" export-cicd-stats
|
|
cp mutants/mutmut-cicd-stats.json quality-evidence/mutation-stats.json
|
|
cp mutants/mutmut-stats.json quality-evidence/mutation-test-selection.json
|
|
"$QUALITY_ENV/bin/python" scripts/check_mutation_score.py \
|
|
quality-evidence/mutation-stats.json --minimum 85.0
|
|
|
|
- name: Write normalized quality trend evidence
|
|
if: always()
|
|
env:
|
|
COVERAGE_BASE: ${{ github.event.pull_request.base.sha || github.event.before }}
|
|
run: |
|
|
evidence_args=()
|
|
if [ -f quality-evidence/repeat-gate.json ]; then
|
|
evidence_args+=(--repeat quality-evidence/repeat-gate.json)
|
|
fi
|
|
if [ -f quality-evidence/mutation-stats.json ]; then
|
|
evidence_args+=(--mutation quality-evidence/mutation-stats.json)
|
|
fi
|
|
"$QUALITY_ENV/bin/python" scripts/write_test_evidence.py \
|
|
--head-sha "$GITHUB_SHA" \
|
|
--base-sha "$COVERAGE_BASE" \
|
|
--python-version "3.12" \
|
|
--output quality-evidence/quality-trend-py3.12.json \
|
|
"${evidence_args[@]}"
|
|
|
|
- name: Enforce repeat duration budgets
|
|
run: |
|
|
"$QUALITY_ENV/bin/python" scripts/check_quality_policy.py \
|
|
--policy ci/test-quality-policy.json \
|
|
--evidence quality-evidence/quality-trend-py3.12.json
|
|
|
|
- name: Upload quality-depth evidence
|
|
if: always()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: quality-depth-py3.12
|
|
path: quality-evidence/
|
|
if-no-files-found: error
|
|
retention-days: 90
|
|
|
|
supply-chain:
|
|
name: Supply chain
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
env:
|
|
EVIDENCE: /tmp/supply-chain-evidence
|
|
GITLEAKS_SHA256: "551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb"
|
|
GITLEAKS_VERSION: "8.30.1"
|
|
RUNTIME_ENV: /tmp/obliteratus-runtime
|
|
SUPPLY_TOOLS: /tmp/obliteratus-supply-tools
|
|
|
|
steps:
|
|
- name: Check out repository
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
with:
|
|
python-version: "3.12"
|
|
cache: pip
|
|
cache-dependency-path: |
|
|
pyproject.toml
|
|
uv.lock
|
|
|
|
- name: Install locked supply-chain tooling
|
|
run: |
|
|
python -m pip install "uv==${UV_VERSION}"
|
|
uv lock --check
|
|
UV_PROJECT_ENVIRONMENT="$RUNTIME_ENV" \
|
|
uv sync --locked --all-extras --no-default-groups --no-editable
|
|
UV_PROJECT_ENVIRONMENT="$SUPPLY_TOOLS" \
|
|
uv sync --locked --only-group ci
|
|
mkdir -p "$EVIDENCE"
|
|
cd "$RUNNER_TEMP"
|
|
"$RUNTIME_ENV/bin/python" -I - <<'PY' | tee "$EVIDENCE/spaces-import.txt"
|
|
import gradio
|
|
import app
|
|
|
|
assert type(app.demo).__name__ == "Blocks"
|
|
print(f"installed all-extras app import: {app.__file__}")
|
|
print(f"gradio: {gradio.__version__}")
|
|
PY
|
|
|
|
- name: Validate exception policy
|
|
run: >-
|
|
python scripts/check_supply_chain_policy.py policy
|
|
--policy ci/supply-chain-policy.json
|
|
|
|
- name: Scan checkout for secrets with redacted evidence
|
|
run: |
|
|
archive="$RUNNER_TEMP/gitleaks.tar.gz"
|
|
curl -fsSLo "$archive" \
|
|
"https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz"
|
|
echo "${GITLEAKS_SHA256} ${archive}" | sha256sum -c -
|
|
tar -xzf "$archive" -C "$RUNNER_TEMP" gitleaks
|
|
set +e
|
|
"$RUNNER_TEMP/gitleaks" dir "$GITHUB_WORKSPACE" \
|
|
--redact=100 \
|
|
--report-format json \
|
|
--report-path "$EVIDENCE/gitleaks.json" \
|
|
--no-banner \
|
|
--no-color
|
|
status=$?
|
|
set -e
|
|
echo "$status" > "$EVIDENCE/gitleaks.status"
|
|
|
|
- name: Collect vulnerability evidence for supported Python versions
|
|
run: |
|
|
for version in 3.10 3.11 3.12; do
|
|
set +e
|
|
uv --preview-features audit-command,json-output audit \
|
|
--locked \
|
|
--python-version "$version" \
|
|
--python-platform linux \
|
|
--output-format json > "$EVIDENCE/uv-audit-py${version}.json"
|
|
status=$?
|
|
set -e
|
|
echo "$status" > "$EVIDENCE/uv-audit-py${version}.status"
|
|
done
|
|
|
|
- name: Build wheel and bind CycloneDX SBOM
|
|
run: |
|
|
mkdir -p "$EVIDENCE/dist"
|
|
"$SUPPLY_TOOLS/bin/python" -m build --wheel --outdir "$EVIDENCE/dist"
|
|
uv --preview-features sbom-export export \
|
|
--locked \
|
|
--format cyclonedx1.5 \
|
|
--all-extras \
|
|
--no-dev \
|
|
--no-editable \
|
|
--output-file "$EVIDENCE/obliteratus.cdx.unbound.json"
|
|
mapfile -t wheels < <(find "$EVIDENCE/dist" -maxdepth 1 -type f -name '*.whl' -print)
|
|
if [ "${#wheels[@]}" -ne 1 ]; then
|
|
echo "expected exactly one wheel, found ${#wheels[@]}"
|
|
exit 1
|
|
fi
|
|
python scripts/check_supply_chain_policy.py sbom \
|
|
--input "$EVIDENCE/obliteratus.cdx.unbound.json" \
|
|
--wheel "${wheels[0]}" \
|
|
--output "$EVIDENCE/obliteratus.cdx.json"
|
|
sha256sum "${wheels[0]}" > "$EVIDENCE/distribution.SHA256SUM"
|
|
|
|
- name: Collect packaged-dependency license inventory
|
|
run: >-
|
|
"$SUPPLY_TOOLS/bin/pip-licenses"
|
|
--python "$RUNTIME_ENV/bin/python"
|
|
--format json
|
|
--output-file "$EVIDENCE/licenses.json"
|
|
|
|
- name: Enforce vulnerability, secret, and license policies
|
|
run: |
|
|
for version in 3.10 3.11 3.12; do
|
|
python scripts/check_supply_chain_policy.py audit \
|
|
--policy ci/supply-chain-policy.json \
|
|
--evidence "$EVIDENCE/uv-audit-py${version}.json" \
|
|
--scanner-status "$EVIDENCE/uv-audit-py${version}.status" \
|
|
--decision "$EVIDENCE/uv-audit-py${version}.decision.json"
|
|
done
|
|
python scripts/check_supply_chain_policy.py secrets \
|
|
--policy ci/supply-chain-policy.json \
|
|
--evidence "$EVIDENCE/gitleaks.json" \
|
|
--scanner-status "$EVIDENCE/gitleaks.status" \
|
|
--decision "$EVIDENCE/gitleaks.decision.json"
|
|
python scripts/check_supply_chain_policy.py licenses \
|
|
--policy ci/supply-chain-policy.json \
|
|
--evidence "$EVIDENCE/licenses.json" \
|
|
--decision "$EVIDENCE/licenses.decision.json"
|
|
|
|
- name: Upload supply-chain evidence
|
|
if: always()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: supply-chain-evidence-py3.12
|
|
path: /tmp/supply-chain-evidence/
|
|
if-no-files-found: error
|
|
retention-days: 14
|