Skip to content

v1.6.0: harden audit comment ingestion #2384

v1.6.0: harden audit comment ingestion

v1.6.0: harden audit comment ingestion #2384

Workflow file for this run

name: Code Mower CI
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
jobs:
package_matrix:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.12", "3.13", "3.14"]
steps:
- name: Check out
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97
with:
python-version: ${{ matrix.python-version }}
- name: Install package
run: |
python -m pip install --upgrade pip
python -m pip install ruff
python -m pip install -e '.[coworker]'
python -m pip check
python -c 'from code_mower import coworker_mcp; import keyring'
- name: Ruff lint
run: python -m ruff check .
- name: Privacy scan
run: python scripts/privacy_scan.py
- name: Documentation lifecycle
if: matrix.python-version == '3.12'
run: python -m code_mower.docs_lifecycle --json
- name: Current release documentation
if: matrix.python-version == '3.12'
run: python scripts/render_release.py --check
- name: Unit tests
run: python -m unittest discover -s tests
- name: Compile sources
run: python -m compileall src scripts
- name: Guard package workflow templates
run: python scripts/guard_package_workflows.py
- name: Install actionlint
if: matrix.python-version == '3.12'
run: |
go install github.com/rhysd/actionlint/cmd/actionlint@v1.7.12
echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH"
- name: Actionlint generated workflows
if: matrix.python-version == '3.12'
run: python scripts/actionlint_generated_workflows.py --code-mower-bin code-mower
- name: Release readiness
if: matrix.python-version == '3.12'
run: python -m code_mower.migration release-readiness --json
- name: Verify the base install without optional dependencies
if: matrix.python-version == '3.12'
run: |
python -m venv "$RUNNER_TEMP/code-mower-base"
"$RUNNER_TEMP/code-mower-base/bin/python" -m pip install "$GITHUB_WORKSPACE"
"$RUNNER_TEMP/code-mower-base/bin/python" -c 'import importlib.util; assert all(importlib.util.find_spec(name) is None for name in ("mcp", "keyring", "httpx2", "jwt"))'
- name: Easy-mode smoke with the base install
if: matrix.python-version == '3.12'
run: python scripts/smoke_easy_mode.py --code-mower-bin "$RUNNER_TEMP/code-mower-base/bin/code-mower" --work-dir "$RUNNER_TEMP/code-mower-smoke" --json
- name: Fresh-clone rehearsal
if: matrix.python-version == '3.12'
run: >-
python scripts/fresh_clone_rehearsal.py
--repo-url "$GITHUB_WORKSPACE"
--ref "$GITHUB_SHA"
--work-dir "$RUNNER_TEMP/code-mower-fresh-clone"
--json
- name: Package-install first-user rehearsal
if: matrix.python-version == '3.12'
run: >-
python -m code_mower.migration package-install-rehearsal
--package-spec "$GITHUB_WORKSPACE"
--work-dir "$RUNNER_TEMP/code-mower-package-install"
--python "$(command -v python)"
--timeout 240
--json
release_rehearsal:
name: release wheel rehearsal
runs-on: ubuntu-latest
env:
SOURCE_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
steps:
- name: Check out the exact rehearsal source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
ref: ${{ env.SOURCE_SHA }}
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97
with:
python-version: "3.12"
- name: Build a pre-merge rehearsal pair
# No --release-pr: these bytes are explicitly ineligible for publication.
run: |
python -m pip install build twine PyYAML packaging
python scripts/release_candidate.py build --source "$GITHUB_WORKSPACE" \
--source-sha "$SOURCE_SHA" --dist "$RUNNER_TEMP/rehearsal-dist"
- name: Rehearse the exact installed wheel offline
run: |
python scripts/rehearse_release.py --dist "$RUNNER_TEMP/rehearsal-dist" \
--source-sha "$SOURCE_SHA" --work-dir "$RUNNER_TEMP/rehearsal"
- name: Upload sanitized pre-merge evidence
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: release-rehearsal-evidence
path: |
${{ runner.temp }}/rehearsal-dist/candidate.json
${{ runner.temp }}/rehearsal/rehearsal.json
if-no-files-found: error
graph_containment:
# The local-graph boundary, against the real kernel mechanism rather than a
# stand-in for one. The unit suite skips these when a host offers no
# mechanism, which is right for a laptop and useless as coverage: this job
# installs bubblewrap and sets CODE_MOWER_REQUIRE_CONTAINMENT, which turns
# that skip into a failure.
name: graph containment
runs-on: ubuntu-latest
steps:
- name: Check out
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97
with:
python-version: "3.12"
- name: Install bubblewrap
run: |
sudo apt-get update
sudo apt-get install -y bubblewrap
test -x /usr/bin/bwrap
# Ubuntu 24.04 restricts unprivileged user namespaces by default, which
# is what bubblewrap needs to build a mount namespace without being
# setuid. Enabling it is a property of this runner, not of the product:
# a host that refuses keeps refusing builds, which is the fail-closed
# posture. Reported rather than asserted, so the suite below is what
# decides the job.
- name: Allow unprivileged user namespaces
run: |
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 || true
sudo sysctl -w kernel.unprivileged_userns_clone=1 || true
bwrap --unshare-net --dev-bind / / /bin/true \
&& echo "bwrap: namespaces available" \
|| echo "bwrap: refused a namespace on this runner"
- name: Install package
run: python -m pip install -e .
- name: Real containment tests
env:
CODE_MOWER_REQUIRE_CONTAINMENT: "1"
run: python -m unittest discover -s tests -p test_context_graph_lifecycle.py -v
graph_containment_macos:
# The Seatbelt half of the same claim. The bubblewrap job above proves the
# Linux boundary and nothing whatever about this one, and macOS is the
# platform this tool is developed on: leaving the profile to be exercised
# only by whoever happens to run the suite on a laptop is how it stayed
# unexecuted. ``sandbox-exec`` ships with the OS, so there is nothing to
# install -- the job is the evidence that the profile runs at all.
#
# Required, like the bubblewrap job. macOS containment is a behaviour claim
# in ``docs/context-graph-lifecycle.md``, and a claim whose only check is
# allowed to be red is not being checked. A red result still carries the
# probe's own diagnosis of which candidate failed and what the launcher
# said, which is what turned the equivalent bubblewrap failure into a
# one-round fix.
name: graph containment (macOS)
runs-on: macos-latest
steps:
- name: Check out
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97
with:
python-version: "3.12"
- name: Confirm the system sandbox is present
run: test -x /usr/bin/sandbox-exec
- name: Install package
run: python -m pip install -e .
- name: Real containment tests
env:
CODE_MOWER_REQUIRE_CONTAINMENT: "1"
run: python -m unittest discover -s tests -p test_context_graph_lifecycle.py -v
board_qualification:
name: Board qualification
runs-on: ubuntu-latest
env:
QUALIFICATION_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
steps:
- name: Check out
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97
with:
python-version: "3.12"
- name: Install package and browser test tool
run: |
python -m pip install -e .
npm install --prefix "$RUNNER_TEMP/board-browser" --no-audit --no-fund playwright@1.63.0
node "$RUNNER_TEMP/board-browser/node_modules/playwright/cli.js" install --with-deps chromium
- name: Local contract scorecard
run: >-
python scripts/qualify_board.py --head-sha "$QUALIFICATION_HEAD_SHA"
--output "$RUNNER_TEMP/board-qualification/scorecard.json"
- name: Desktop and phone Board fixtures
if: always()
run: |
python tests/board_qualification_fixtures.py "$RUNNER_TEMP/board-qualification"
NODE_PATH="$RUNNER_TEMP/board-browser/node_modules" node tests/board_qualification_browser.cjs "$RUNNER_TEMP/board-qualification"
- name: Upload allowlisted qualification metadata
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: board-qualification
path: |
${{ runner.temp }}/board-qualification/scorecard.json
${{ runner.temp }}/board-qualification/browser-scorecard.json
${{ runner.temp }}/board-qualification/board-1440.png
${{ runner.temp }}/board-qualification/board-390.png
package:
name: package
runs-on: ubuntu-latest
needs: [package_matrix, board_qualification, release_rehearsal]
if: always()
steps:
- name: Check matrix result
run: |
test "${{ needs.package_matrix.result }}" = "success"
test "${{ needs.board_qualification.result }}" = "success"
test "${{ needs.release_rehearsal.result }}" = "success"