From 29d70822ccd174c8596b676ada5bdae734a2d06b Mon Sep 17 00:00:00 2001 From: Marc-cn <130138935+Marc-cn@users.noreply.github.com> Date: Sat, 10 Oct 2026 11:15:54 -0400 Subject: [PATCH] ci: install the wheels without dev dependencies and run one audit Every CI job installs with uv sync, which uses uv.lock and the dev group. That hid both problems fixed in #573: a release of tree-sitter-language-pack that the lockfile pins away, and a dependency core imported without declaring it. The new job builds the wheels, installs them with pip into a clean environment, and runs one level 1 audit. It does this twice: core and baseline only, then everything darnit-mcp pulls in. The minimal install matters because a sibling package can declare what core forgot. The logic is in scripts/non-dev-install-smoke.sh so it can be run locally. Signed-off-by: Marc-cn <130138935+Marc-cn@users.noreply.github.com> Assisted-by: Claude:claude-fable-5-1 --- .github/workflows/ci.yml | 40 +++++++++++++++++++ scripts/non-dev-install-smoke.sh | 68 ++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100755 scripts/non-dev-install-smoke.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f7b72a83..f2d5f94c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -261,3 +261,43 @@ jobs: "Update the worked example AND docs/packaging-plugins.md together." ) PY + + # Every other job installs with `uv sync`, which uses uv.lock and the dev + # group. This one installs the built wheels with pip, as a user or the + # container image does, and runs one audit. It fails when darnit imports + # something it does not declare, or when a new release of a dependency + # breaks a fresh install. See #573. + non_dev_install_smoke: + name: Non-dev install smoke (wheels + one audit) + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v4 + + - name: Set up Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v5 + with: + # 3.12 to match the container image (python:3.12-slim) + python-version: "3.12" + + - name: Install uv + uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v4 + with: + version: ${{ env.UV_VERSION }} + enable-cache: true + + - name: Build wheels + run: | + for package in darnit-mcp darnit-core darnit-baseline darnit-gittuf darnit-reproducibility; do + uv build --wheel --package "$package" + done + + # The smallest set that can run a baseline audit. A sibling package can + # declare a dependency that core forgot, so the full install alone is + # not enough. + - name: Minimal install (core + baseline) and one audit + run: scripts/non-dev-install-smoke.sh dist darnit-core darnit-baseline + + - name: Full install (everything darnit-mcp pulls in) and one audit + run: scripts/non-dev-install-smoke.sh dist diff --git a/scripts/non-dev-install-smoke.sh b/scripts/non-dev-install-smoke.sh new file mode 100755 index 00000000..e84fe6cd --- /dev/null +++ b/scripts/non-dev-install-smoke.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# +# non-dev-install-smoke.sh - Install built wheels the way a user does, then run one audit +# +# Usage: scripts/non-dev-install-smoke.sh [dist-dir [package ...]] +# +# Installs the darnit wheels from dist-dir (default: dist) into a new virtual +# environment with pip: a fresh dependency resolve, no lockfile, no development +# dependencies. Then audits a throwaway repository and fails if the audit +# crashes, reports a dependency as "not installed", or passes no control. +# +# `uv sync` hides both kinds of breakage this catches: a dependency darnit +# imports without declaring it (the dev group supplies it), and a newly +# published release of a dependency that uv.lock pins away. See #573. +# +# With no package names it installs everything darnit-mcp pulls in. Pass a +# smaller set (darnit-core darnit-baseline) as well: a sibling package can +# declare a dependency that core forgot, which hides the gap in a full install. +# +# Requirements: python3 (with venv and pip), git, network access to PyPI. + +set -euo pipefail + +dist="${1:-dist}" +if [ $# -gt 1 ]; then + shift + packages=("$@") +else + packages=(darnit-mcp darnit-core darnit-baseline darnit-gittuf darnit-reproducibility) +fi + +fail() { echo "FAIL: $*" >&2; exit 1; } + +wheels=() +for name in "${packages[@]}"; do + matches=("$dist"/"${name//-/_}"-*.whl) + if [ ! -e "${matches[0]}" ]; then + fail "no $name wheel in $dist" + fi + wheels+=("${matches[0]}") +done + +work="$(mktemp -d)" +trap 'rm -rf "$work"' EXIT + +python3 -m venv "$work/venv" +"$work/venv/bin/pip" install --quiet --disable-pip-version-check "${wheels[@]}" + +mkdir "$work/repo" +git -C "$work/repo" init -q +echo "# fixture" > "$work/repo/README.md" + +out="$work/audit.txt" +status=0 +"$work/venv/bin/darnit" -q audit --no-fail --framework openssf-baseline --tags level=1 "$work/repo" >"$out" 2>&1 || status=$? +cat "$out" + +if [ "$status" -ne 0 ]; then + fail "darnit audit exited with status $status" +fi +if grep -q -E 'Traceback|not installed' "$out"; then + fail "the audit reported a crash or a dependency as not installed" +fi +if ! grep -q -E '^Total: .*\| Pass: [1-9]' "$out"; then + fail "no control passed; the expression-based controls should pass on a near-empty repository" +fi + +echo "OK: the wheels install without dev dependencies and the audit runs"