Skip to content

Record leases from the pre-tool hook, three CLI paper cuts, and the fork/exclude finding #119

Record leases from the pre-tool hook, three CLI paper cuts, and the fork/exclude finding

Record leases from the pre-tool hook, three CLI paper cuts, and the fork/exclude finding #119

Workflow file for this run

name: ci
on:
push:
branches: [main]
pull_request:
# A new push to the same PR (or to main) supersedes the run in flight;
# stop paying for the old one. release.yml deliberately does not cancel.
concurrency:
group: ci-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
FUSE_T_VERSION: 1.2.7
CARGO_TERM_COLOR: always
# acyclic-fs is fetched from its own git repo (see Cargo.toml); cargo's
# built-in libgit2 fetcher can't resolve a pinned commit SHA that isn't a
# branch tip on that host, so delegate to the system git CLI instead.
CARGO_NET_GIT_FETCH_WITH_CLI: true
jobs:
# Three cheap jobs (changes, deny, lint) run in parallel; the expensive
# test matrix (FUSE-T install, release build, acceptance suite on two
# OSes) only starts once deny and lint pass, so a formatting slip or a
# banned crate is reported in about a minute instead of after a full
# matrix run — and it is skipped outright when nothing that could change
# a test result was touched.
# Which parts of the tree a change touches. A job-level filter rather
# than a workflow-level `paths-ignore` because branch protection on main
# requires the `test (...)` checks: a workflow that never runs leaves
# them "expected" forever and blocks the merge, whereas a job skipped by
# `if:` reports as skipped, which counts as passing.
changes:
runs-on: ubuntu-24.04
# paths-filter lists a PR's files through the API; the default token
# here is read-only on contents and nothing else.
permissions:
contents: read
pull-requests: read
outputs:
code: ${{ steps.filter.outputs.code }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
# On a push to main the filter diffs against the pre-push commit,
# which a depth-1 checkout does not have.
fetch-depth: 0
- id: filter
uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3
with:
# "some file changed that is not docs/prose": a negation-only list
# matches nothing, so the positive `**` is required, and
# some-with-excludes makes the negations apply to it.
predicate-quantifier: some-with-excludes
filters: |
code:
- '**'
- '!**/*.md'
- '!docs/**'
- '!LICENSE'
- '!.github/CODEOWNERS'
# Licenses, advisories, and sources per deny.toml. Keeps the published
# SBOM inside the permissive allowlist and fails on known-vulnerable or
# yanked crates. Always runs: the secrets scan applies to docs too.
deny:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: Product name is single-sourced (product.toml)
run: bash scripts/check-product-name.sh
- name: No forbidden files or credential patterns
run: bash scripts/check-no-secrets.sh
- name: Code quality (line width, TODO format, comment blocks, duplication)
run: bash scripts/check-code-quality.sh
- uses: EmbarkStudios/cargo-deny-action@3c6349835b2b7b196a839186cb8b78e02f7b5f25 # v2.1.1
with:
command: check
arguments: --locked
log-level: warn
# Formatting and lint. Fast and toolchain-only (no FUSE-T needed).
lint:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: Install toolchain
run: |
rustup toolchain install stable --profile minimal --component rustfmt --component clippy
rustup default stable
- name: Cache cargo
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: lint-cargo-${{ hashFiles('Cargo.lock') }}
- name: cargo fmt --check
run: cargo fmt --all --check
- name: cargo clippy
run: cargo clippy --workspace --all-targets --all-features -- -D warnings
# Line coverage of the unit and integration tests, as a job summary and
# an lcov artifact, with a floor so a change can't quietly delete tests.
# The daemon, client, and MCP server are exercised by the acceptance
# scripts rather than `cargo test`, so they read as 0% here; raise the
# floor as unit coverage of those grows, not by counting the scripts.
coverage:
needs: [changes, deny, lint]
if: needs.changes.outputs.code == 'true'
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: Install toolchain
run: |
rustup toolchain install stable --profile minimal --component llvm-tools-preview
rustup default stable
- uses: taiki-e/install-action@3f74d7c16a4242f1c95561e98edc25d36adb4375 # v2.87.12
with:
tool: cargo-llvm-cov
- name: Cache cargo
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: coverage-cargo-${{ hashFiles('Cargo.lock') }}
- name: cargo llvm-cov
run: |
cargo llvm-cov --workspace --all-features --lcov --output-path lcov.info --fail-under-lines 48
{
echo '## Test coverage (lines)'
echo '```'
cargo llvm-cov report --summary-only
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: lcov
path: lcov.info
# Windows, which nothing else in this file covers. The lint job runs on
# Linux, so every `#[cfg(windows)]` block in the tree is invisible to it —
# a Windows-only arm can stop compiling and no other check notices. This
# job builds and lints those arms, runs the workspace tests, and drives
# the parts of the product whose behaviour genuinely differs there
# (named-pipe transport, UTF-16LE names, renaming the repo root, copy
# forks). The POSIX acceptance suite is not run: it assumes mount tooling,
# symlinks and modes that Windows does not have.
windows:
needs: [changes, deny, lint]
if: >-
always()
&& needs.deny.result == 'success'
&& needs.lint.result == 'success'
&& (needs.changes.result != 'success' || needs.changes.outputs.code == 'true')
runs-on: windows-2022
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: Install toolchain
run: |
rustup toolchain install stable --profile minimal --component rustfmt --component clippy
rustup default stable
- name: Cache cargo
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: windows-cargo-${{ hashFiles('Cargo.lock') }}
- name: cargo fmt --check
run: cargo fmt --all --check
# The point of the job: lint the cfg(windows) arms the Linux lint job
# never compiles.
- name: cargo clippy
run: cargo clippy --workspace --all-targets --all-features -- -D warnings
- name: Unit and integration tests
run: cargo test --workspace
- name: Release build
run: cargo build --release --locked -p acyclic
- name: Windows end-to-end smoke
shell: bash
env:
ACYCLIC_BIN: ${{ github.workspace }}/target/release/acyclic.exe
run: bash tests/acceptance/windows-smoke.sh
test:
needs: [changes, deny, lint]
# Fail closed: run when the change filter says code moved, and also
# when the filter job itself failed (an empty output must not read as
# "nothing to test", since a skipped required check counts as passing).
# deny and lint failing still skip this job; those are required checks
# in their own right, so the PR stays red.
if: >-
always()
&& needs.deny.result == 'success'
&& needs.lint.result == 'success'
&& (needs.changes.result != 'success' || needs.changes.outputs.code == 'true')
strategy:
fail-fast: false
matrix:
os: [macos-14, ubuntu-24.04]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout plugin
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
path: graphcoder-plugin
- name: Install FUSE-T (macOS)
if: runner.os == 'macOS'
run: |
set -euo pipefail
curl --fail --location --retry 5 \
"https://github.com/macos-fuse-t/fuse-t/releases/download/${FUSE_T_VERSION}/fuse-t-macos-installer-${FUSE_T_VERSION}.pkg" \
--output /tmp/fuse-t.pkg
sudo installer -pkg /tmp/fuse-t.pkg -target /
test -d /usr/local/include/fuse3
- name: Install toolchain
run: rustup toolchain install stable --profile minimal && rustup default stable
- name: Cache cargo
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: |
~/.cargo/registry
~/.cargo/git
graphcoder-plugin/target
key: ${{ matrix.os }}-cargo-${{ hashFiles('graphcoder-plugin/Cargo.lock') }}
- name: Unit and integration tests
working-directory: graphcoder-plugin
run: cargo test --workspace
- name: Release build (acceptance + latency gate run against it)
working-directory: graphcoder-plugin
run: cargo build --release
- name: Acceptance suite
working-directory: graphcoder-plugin
env:
ACYCLIC_BIN: ${{ github.workspace }}/graphcoder-plugin/target/release/acyclic
ACYCLIC_QUAL: ${{ github.workspace }}/graphcoder-plugin/target/release/acyclic-qual
# Smaller corpus keeps CI wall-clock sane; the budget is unchanged.
ACYCLIC_LAT_FILES: "5000"
ACYCLIC_LAT_MB: "64"
ACYCLIC_SOAK_ROUNDS: "30"
run: bash tests/acceptance/run-all.sh