Skip to content

fix(impact): name the changed files it could not analyse (#454) #1339

fix(impact): name the changed files it could not analyse (#454)

fix(impact): name the changed files it could not analyse (#454) #1339

Workflow file for this run

# .github/workflows/ci.yml
name: diffctx CI
'on':
pull_request:
branches: ['**']
push:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# ============================================================================
# Pre-commit checks (fast feedback)
# ============================================================================
pre-commit:
name: Pre-commit hooks
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up uv
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
python-version: '3.12'
enable-cache: true
# This job runs the cargo fmt and clippy hooks, so it is the one place
# that needs those components; rust-toolchain.toml deliberately does not
# pin them (see the comment there). It also compiles the extension, so
# cargo has to be on PATH at the pinned version rather than whatever the
# runner image happens to ship.
- name: Install Rust
uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable
with:
toolchain: "1.92.0"
components: rustfmt, clippy
- name: Cache pre-commit
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/pre-commit
key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}
- name: Install dependencies
run: |
uv sync --locked --no-build --extra dev
echo "$PWD/.venv/bin" >> "$GITHUB_PATH"
- name: Run pre-commit
run: pre-commit run --all-files
# ============================================================================
# Rust dependency advisories. Separate job because the pre-commit hook can
# only skip when cargo-audit is absent, and it is absent on GitHub runners —
# so this is the only place the check actually runs.
# ============================================================================
cargo-audit:
name: Rust dependency advisories
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust
uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable
with:
toolchain: "1.92.0"
- name: Install cargo-audit
run: cargo install cargo-audit --locked
- name: Audit
run: cargo audit
# ============================================================================
# Cargo.toml splits the tree-sitter grammars into lang-core/lang-extra, but
# only the default (both) feature set was ever compiled. Under lang-core
# alone every lang-extra LANG_CONFIGS entry resolves to no grammar and
# degrades to generic chunking, and nothing would have caught the build
# breaking outright.
# ============================================================================
cargo-feature-sets:
name: Rust builds without default features
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust
uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable
with:
toolchain: "1.92.0"
- name: Cache Cargo
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: cargo-features-${{ runner.os }}-${{ hashFiles('Cargo.lock') }}
restore-keys: cargo-features-${{ runner.os }}-
- name: Check lang-core only
working-directory: crates/diffctx-native
run: cargo check --no-default-features --features lang-core
- name: Check with no language grammars at all
working-directory: crates/diffctx-native
run: cargo check --no-default-features
# ============================================================================
# `cargo publish --locked` in publish-crate.yml was the first packaging step
# the crate ever went through, after PyPI and the GitHub Release had already
# shipped. The manifest excludes tests/** yet declares a yaml_cases test
# target; a stray include!() of an excluded file, or a readme path that does
# not survive packaging, fails only at publish time without this.
# ============================================================================
cargo-publish-dry-run:
name: Crate packages for crates.io
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust
uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable
with:
toolchain: "1.92.0"
- name: Cache Cargo
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: cargo-publish-${{ runner.os }}-${{ hashFiles('Cargo.lock') }}
restore-keys: cargo-publish-${{ runner.os }}-
- name: Package and verify
working-directory: crates/diffctx-native
run: cargo publish --dry-run --locked
# ============================================================================
# Docker image build. Gated here rather than only at release time: cd.yml's
# build-image needs finalize-release, so the first build of this Dockerfile
# happened after PyPI and the GitHub Release had already shipped — a broken
# COPY whitelist left ghcr.io with no tag while every other channel moved.
# ============================================================================
docker-build:
name: Docker image builds
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Build image
run: >-
docker build -f Dockerfile -t diffctx:ci
--build-arg "DIFFCTX_BUILD_SHA=${GITHUB_SHA}"
--build-arg "APT_REFRESH=$(date -u +%F)" .
# A synthetic two-commit repo, not this repo's own HEAD~1..HEAD: whether
# the latest commit's diff fits a fixed budget depends on what that
# commit happens to touch, which made the smoke fail on a docs-only or
# test-only push. The fixture mirrors cd.yml's publish-image smoke.
- name: Smoke the image on a real two-commit diff
run: |
docker run --rm diffctx:ci --version | grep -qx "diffctx $(cut -d'"' -f2 src/diffctx/version.py)"
SMOKE=$(mktemp -d)
cd "$SMOKE"
git init -q
git config user.email "smoke@test.local"
git config user.name "Smoke"
printf 'def add(a, b):\n return a + b\n' > util.py
printf 'from util import add\n\ndef main():\n print(add(1, 2))\n' > app.py
git add -A && git commit -q -m "initial"
printf 'def add(a, b):\n return a + b\n\ndef sub(a, b):\n return a - b\n' > util.py
git add -A && git commit -q -m "add sub"
# mktemp -d creates the dir 0700 for the runner user, while the image
# runs as the unprivileged uid 10001 — without this the container
# sees "'/repo' is not a git repository".
chmod -R a+rX "$SMOKE"
docker run --rm -v "$SMOKE:/repo" diffctx:ci . \
--diff HEAD~1..HEAD --budget 2000 > /tmp/out.md
test -s /tmp/out.md
grep -q "def sub" /tmp/out.md
docker run --rm -v "$SMOKE:/repo" diffctx:ci . \
--diff HEAD~1..HEAD -f json --no-content > /tmp/out.json
jq -e --arg sha "$GITHUB_SHA" '.provenance.engine.build == $sha' /tmp/out.json
# ============================================================================
# Linting and Type Checking
# ============================================================================
lint-type-check:
name: Lint & Type Check
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout Code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up uv
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
python-version: '3.10'
enable-cache: true
# Same reason as the pre-commit job: installing this package compiles the
# native extension, so cargo has to be on PATH at the pinned version.
- name: Install Rust
uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable
with:
toolchain: "1.92.0"
- name: Install Linters and Type Checker
run: |
uv sync --locked --no-build --extra dev
echo "$PWD/.venv/bin" >> "$GITHUB_PATH"
- name: Run Linters and Formatters Check
run: |
ruff check src eval tests
black --check src eval tests
- name: Run Type Checker (Mypy)
run: |
mypy src
# ============================================================================
# Rust diffctx tests (the full YAML corpus under tests/cases, real git repos)
# ============================================================================
rust-diffctx-test:
name: Rust diffctx tests
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust
uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable
with:
toolchain: "1.92.0"
components: rustfmt, clippy
- name: Cache Cargo
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: cargo-${{ runner.os }}-${{ hashFiles('Cargo.lock') }}
restore-keys: cargo-${{ runner.os }}-
# No DIFFCTX_YAML_CASES_LIMIT: the whole corpus is gated here (~2 min).
# Cases below threshold at the shipped operating point are enumerated in
# crates/diffctx-native/tests/known_below_threshold.txt (the file is the
# count — numbers written here rotted twice), enforced bidirectionally, so
# a regression in ANY case fails the build. A limit still works locally
# for a fast stratified subset.
- name: Build and test
working-directory: crates/diffctx-native
run: |
# --features python or analytics/, graph_export/ and project_graph/
# are not compiled at all: they are cfg-gated on that feature, ship
# inside the wheel, and their 19 inline tests ran in no job until
# 2026-09-06 — `cargo test --lib` reported a confident green over a
# library that did not contain them.
cargo test --lib --features python
cargo test --test native_cli
cargo test --test fragment_snapshot
# The binary-level suites: impact, the hook, bindings, staged
# snapshots, git failures. They ran in no job until 2026-10-11, so
# their regression tests (#387 among them) guarded nothing.
# --no-fail-fast: one failing suite must not hide the others.
cargo test --no-fail-fast --test impact --test hook --test bindings --test staged \
--test git_faults --test in_memory --test context_schema --test kotlin
cargo build --release --examples
# --release, the profile the wheel ships: it unwinds, so the corpus
# exercises the same panic strategy production runs under.
cargo test --release --test yaml_cases
# The Linux job above keeps its name because branch protection requires it;
# this one gates the same suites where paths, line endings and process
# spawning differ.
rust-diffctx-test-cross:
name: Rust diffctx tests (${{ matrix.os }})
strategy:
fail-fast: false
matrix:
os: [macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust
uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable
with:
toolchain: "1.92.0"
- name: Cache Cargo
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: cargo-${{ runner.os }}-${{ hashFiles('Cargo.lock') }}
restore-keys: cargo-${{ runner.os }}-
- name: Build and test
working-directory: crates/diffctx-native
shell: bash
run: |
cargo test --lib --features python
cargo test --test native_cli
cargo test --test fragment_snapshot
cargo test --no-fail-fast --test impact --test hook --test bindings --test staged \
--test git_faults --test in_memory --test context_schema --test kotlin
cargo test --release --test yaml_cases
# Release ships an Intel-Mac binary cross-built on this arm64 image; the
# first build of it must not be the release itself.
- name: Cross-build the Intel-Mac binary and run it under Rosetta
if: runner.os == 'macOS'
working-directory: crates/diffctx-native
run: |
rustup target add x86_64-apple-darwin
cargo build --release --locked --bin diffctx --target x86_64-apple-darwin
bin=../../target/x86_64-apple-darwin/release/diffctx
file "$bin" | grep -q x86_64
arch -x86_64 "$bin" --version
# ============================================================================
# Cross-platform Python Testing (diffctx core)
# ============================================================================
test:
needs: [lint-type-check]
# The repository is public, so hosted macOS/Windows minutes cost nothing;
# release-time smoke in cd.yml alone let platform bugs land on main first.
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
# The ARM legs of the release matrix. Windows on ARM has native
# CPython only from 3.11, so one current interpreter each.
include:
- os: ubuntu-22.04-arm
python-version: '3.13'
- os: windows-11-arm
python-version: '3.13'
runs-on: ${{ matrix.os }}
permissions:
contents: read
steps:
- name: Checkout Code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# tests/test_concurrent_deadlines.py runs the engine over this
# repository's own full history (root..HEAD) as a workload no build profile
# finishes inside a one-second ceiling; a shallow clone has no such range.
fetch-depth: 0
- name: Set up uv with Python ${{ matrix.python-version }}
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
python-version: ${{ matrix.python-version }}
enable-cache: true
- name: Install Rust
uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable
with:
toolchain: "1.92.0"
components: rustfmt, clippy
- name: Cache Cargo
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: cargo-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python-version }}-${{ hashFiles('Cargo.lock') }}
restore-keys: cargo-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python-version }}-
- name: Build and install diffctx (with Rust _diffctx) and dev deps
shell: bash
env:
PYO3_USE_ABI3_FORWARD_COMPATIBILITY: "1"
run: |
# Editable install of the project plus every extra the suite needs.
# uv provisions maturin in its own build environment, so the build
# backend is not a separate step and cannot drift from pyproject.
# The eval group carries pebble/numpy/scipy/datasets. Without it
# tests/conftest.py drops tests/eval/* at COLLECTION time, so those
# tests reported as neither run nor skipped and executed in no job at
# all. Installed on one interpreter: the cost is one matrix cell, the
# alternative was a suite nobody ran.
SYNC=(uv sync --locked --no-build --extra dev --extra full --extra mcp)
# Windows on ARM: cryptography (pulled in by mcp) publishes no wheel
# and needs OpenSSL to build, so the MCP suites skip there; tiktoken,
# the test oracle, has no wheel either and builds with the Rust above.
if [[ "$RUNNER_OS" == "Windows" && "$RUNNER_ARCH" == "ARM64" ]]; then
SYNC=(uv sync --locked --extra dev --extra full)
fi
if [[ "${{ matrix.python-version }}" == "3.12" ]]; then
SYNC+=(--group eval)
fi
"${SYNC[@]}"
if [[ "$RUNNER_OS" == "Windows" ]]; then
cygpath -w "$PWD/.venv/Scripts" >> "$GITHUB_PATH"
else
echo "$PWD/.venv/bin" >> "$GITHUB_PATH"
fi
- name: Run Tests with Coverage
shell: bash
run: |
pytest -v --cov=src/diffctx --cov-report=xml \
--cov-report=term-missing --cov-branch --junitxml=test-results.xml
- name: Coverage report with threshold
run: |
coverage report --fail-under=40
- name: Upload coverage reports to Codecov
# Every OS uploads: platform-only branches (Windows paths, POSIX modes)
# are covered nowhere else, and Codecov merges the reports.
if: matrix.python-version == '3.12'
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage.xml
flags: integration
fail_ci_if_error: false
verbose: true
# ============================================================================
# The landing page is a PWA; tests/test_publishing_manifests.py checks the
# manifest and precache list statically, this job opens the site in a real
# browser, cuts the network and expects the installed shortcuts to open.
# Playwright is not a project dependency (only this job drives a browser),
# so it is pinned here rather than in uv.lock.
# ============================================================================
docs-pwa:
name: Landing page installs and opens offline
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up uv
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
python-version: '3.12'
enable-cache: true
- name: Install Rust
uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable
with:
toolchain: "1.92.0"
- name: Install dependencies and a browser
run: |
uv sync --locked --no-build --extra dev
uv pip install --no-build "playwright==1.63.0"
echo "$PWD/.venv/bin" >> "$GITHUB_PATH"
.venv/bin/playwright install --with-deps chromium
- name: Open the site offline
run: pytest tests/test_docs_pwa.py -v -p no:cacheprovider
# ============================================================================
# Complexity & Maintainability Metrics
# ============================================================================
complexity-checks:
name: Complexity & Maintainability Analysis
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up uv
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
python-version: '3.12'
enable-cache: true
# Installing this package compiles the native extension, so cargo must
# be on PATH at the pinned version.
- name: Install Rust
uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable
with:
toolchain: "1.92.0"
- name: Install dependencies
run: |
uv sync --locked --no-build --extra dev
echo "$PWD/.venv/bin" >> "$GITHUB_PATH"
- name: Check cyclomatic complexity
run: |
echo "=== Cyclomatic Complexity Report ==="
radon cc src/diffctx/ --min B --show-complexity --total-average
- name: Check maintainability index
run: |
echo "=== Maintainability Index Report ==="
radon mi src/diffctx/ --min B --show
# ============================================================================
# Architecture & Import Contracts
# ============================================================================
architecture-checks:
name: Architecture & Import Contracts
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up uv
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
python-version: '3.12'
enable-cache: true
# Installing this package compiles the native extension, so cargo must
# be on PATH at the pinned version.
- name: Install Rust
uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable
with:
toolchain: "1.92.0"
- name: Install dependencies
run: |
uv sync --locked --no-build --extra dev
echo "$PWD/.venv/bin" >> "$GITHUB_PATH"
- name: Check import contracts
run: lint-imports