Repository navigation
fix(impact): name the changed files it could not analyse (#454) #1339
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # .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 |