name: Feature Matrix
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
workflow_dispatch:
concurrency:
group: feature-matrix-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0
jobs:
bundles:
name: bundle / ${{ matrix.feature }}
runs-on: ubuntu-latest
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
feature:
- default
- full
- all-languages
- most-languages
- core-languages
- extended-languages
- rust-only
- advanced-analysis
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
key: featmatrix-${{ matrix.feature }}
- name: cargo check --lib --features ${{ matrix.feature }}
run: cargo check --lib --locked --features '${{ matrix.feature }}'
- name: cargo check --bin pmat --features ${{ matrix.feature }}
run: cargo check --bin pmat --locked --features '${{ matrix.feature }}'
individual:
name: individual / shard ${{ matrix.shard }}
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
shard: [0, 1, 2, 3, 4, 5]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
key: featmatrix-shard-${{ matrix.shard }}
- name: cargo check every feature in this shard
shell: bash
run: |
set -uo pipefail
# `broken-tests` is a deliberate non-compiling quarantine — the only
# feature exempt, and named here so the exemption is visible.
mapfile -t FEATURES < <(
python3 - <<'PY'
import re
body = re.search(r'^\[features\]\n(.*?)(?=^\[)', open('Cargo.toml').read(), re.S | re.M).group(1)
skip = {'broken-tests'}
for m in re.finditer(r'^([a-zA-Z0-9_-]+)\s*=', body, re.M):
if m.group(1) not in skip:
print(m.group(1))
PY
)
total=${#FEATURES[@]}
echo "::notice::${total} features discovered; this is shard ${{ matrix.shard }} of 6"
if [ "$total" -lt 40 ]; then
echo "::error::only ${total} features parsed from Cargo.toml — the parser is broken, not the crate"
exit 1
fi
failed=()
for i in "${!FEATURES[@]}"; do
if [ $(( i % 6 )) -ne ${{ matrix.shard }} ]; then continue; fi
f="${FEATURES[$i]}"
echo "::group::cargo check --lib --features $f"
if cargo check --lib --locked --features "$f"; then
echo "ok $f"
else
echo "::error::feature '$f' does not compile"
failed+=("$f")
fi
echo "::endgroup::"
done
if [ ${#failed[@]} -ne 0 ]; then
echo "features that do not compile: ${failed[*]}"
exit 1
fi
echo "all features in shard ${{ matrix.shard }} compile"
all-targets:
name: clippy --all-targets
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: Swatinem/rust-cache@v2
- name: clippy --all-targets (compiles benches and examples)
run: cargo clippy --all-targets --locked -- -D warnings
package-size:
name: package size
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: the packaged tarball builds, and fits
shell: bash
run: |
set -uo pipefail
# NOT --no-verify. `cargo package --no-verify` skips building the
# packaged tarball, so it cannot see that an exclude pattern removed a
# source file. That happened here: an unanchored "coverage/" pattern
# matched src/services/agent_context/query/coverage/ as well as the
# top-level artifacts dir, and --no-verify reported a healthy 7.4 MiB
# for a tarball that did not compile. Measuring a package without
# building it is the same defect this repo keeps finding.
out=$(cargo package --locked --allow-dirty 2>&1) || {
echo "$out"; echo "::error::cargo package failed to build the packaged tarball"; exit 1; }
echo "$out" | tail -3
line=$(echo "$out" | grep -oE '\([0-9.]+MiB compressed\)' | tail -1)
mib=$(echo "$line" | grep -oE '[0-9.]+')
if [ -z "$mib" ]; then
echo "::error::could not read the packaged size from cargo output — the check cannot pass without a measurement"
exit 1
fi
echo "packaged: ${mib} MiB compressed (crates.io hard limit: 10 MiB)"
# 9.0 leaves room for one ordinary addition; the hard limit leaves none.
if awk "BEGIN{exit !($mib >= 9.0)}"; then
echo "::error::package is ${mib} MiB compressed, at or past the 9.0 MiB budget (crates.io rejects >10 MiB with an opaque 503). Add large paths to Cargo.toml's exclude list, or drop the asset."
exit 1
fi
if awk "BEGIN{exit !($mib >= 8.0)}"; then
echo "::warning::package is ${mib} MiB compressed; the budget is 9.0 and the hard limit is 10."
fi
feature-gate:
name: feature-gate
runs-on: ubuntu-latest
needs: [bundles, individual, all-targets, package-size]
if: always()
steps:
- name: Require every leg
run: |
for r in "${{ needs.bundles.result }}" "${{ needs.individual.result }}" "${{ needs.all-targets.result }}" "${{ needs.package-size.result }}"; do
if [ "$r" != "success" ]; then
echo "a leg failed: bundles=${{ needs.bundles.result }} individual=${{ needs.individual.result }} all-targets=${{ needs.all-targets.result }} package-size=${{ needs.package-size.result }}"
exit 1
fi
done
echo "every advertised feature compiles and the package fits"