Skip to content

deploy: repin graft-dev to ba889c2 (WI-2577, D39 attribution) #681

deploy: repin graft-dev to ba889c2 (WI-2577, D39 attribution)

deploy: repin graft-dev to ba889c2 (WI-2577, D39 attribution) #681

Workflow file for this run

name: ci
# Required checks for the merge queue: fmt, clippy, test.
# They run on pull requests and in the merge queue. There is no push trigger
# for main: the queue has already tested what lands (docs/plan.md, CONTRIBUTING.md).
on:
pull_request:
merge_group:
workflow_dispatch:
concurrency:
group: ci-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: -D warnings
jobs:
# The runner-policy gate (vogt's pattern, inline). Every job names a
# self-hosted runner, and no job selects one dynamically. graft is public:
# fork pull requests are held for approval by the repository's fork-PR
# setting, and no workflow here uses pull_request_target or gives a PR job
# a secret. If you fork this repo, point `runs-on:` at a GitHub-hosted
# runner locally; do not commit that change upstream.
runner-policy:
runs-on: [self-hosted]
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: every job names a self-hosted runner
run: |
set -euo pipefail
bad=0
# Built from parts: the literal two-brace sequence is an Actions
# expression anywhere in a workflow file, even inside `run:`.
# shellcheck disable=SC2016
expr_open='${'"{"
while IFS=: read -r file number line; do
# The whole runs-on value: key and any trailing comment stripped,
# surrounding whitespace trimmed.
value=${line#*runs-on:}
value=${value%%#*}
value=$(printf '%s' "$value" | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//')
case "$value" in
*"$expr_open"*)
echo "::error file=$file,line=$number::dynamic runner selection is not allowed"
bad=1
continue
;;
esac
case "$value" in
# Self-hosted lists. The graft-dev runner is self-hosted but
# registered with --no-default-labels, so its only label is
# graft-dev.
"[self-hosted]"|"[self-hosted,"*"]"|"[graft-dev]") ;;
# Operator decision 2026-10-26: release builds in release.yml
# only use these hosted labels, matched exactly. Required PR
# checks in ci.yml stay self-hosted.
ubuntu-24.04|ubuntu-24.04-arm|macos-14|"[ubuntu-24.04]"|"[ubuntu-24.04-arm]"|"[macos-14]")
if [ "$file" != .github/workflows/release.yml ]; then
echo "::error file=$file,line=$number::hosted runners are allowed only in release.yml"
bad=1
fi
;;
*)
echo "::error file=$file,line=$number::job does not name an allowed runner"
bad=1
;;
esac
done < <(grep -RnE '^[[:space:]]*runs-on:' .github/workflows \
--include='*.yml' --include='*.yaml')
[ "$bad" -eq 0 ]
# The self-hosted image ships no language runtime: rust-toolchain installs
# rustup and the toolchain on a bare runner.
fmt:
runs-on: [self-hosted]
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
# Docs-only diffs skip the build but still report the check (WI-2401).
- id: scope
env:
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha }}
run: .github/scripts/docs-only.sh "$BASE_SHA"
- if: steps.scope.outputs.docs_only == 'true'
run: echo "docs-only change; skipping the Rust build"
- if: steps.scope.outputs.docs_only == 'true'
run: python3 bench/render.py docs/guide/bench.svg
- uses: dtolnay/rust-toolchain@stable
if: steps.scope.outputs.docs_only != 'true'
with:
components: rustfmt
- run: cargo fmt --all -- --check
if: steps.scope.outputs.docs_only != 'true'
clippy:
runs-on: [self-hosted]
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
# Docs-only diffs skip the build but still report the check (WI-2401).
- id: scope
env:
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha }}
run: .github/scripts/docs-only.sh "$BASE_SHA"
- if: steps.scope.outputs.docs_only == 'true'
run: echo "docs-only change; skipping the Rust build"
- uses: dtolnay/rust-toolchain@stable
if: steps.scope.outputs.docs_only != 'true'
with:
components: clippy
# Only main writes the cache back; every ref restores what main saved.
- uses: Swatinem/rust-cache@v2
if: steps.scope.outputs.docs_only != 'true'
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- run: cargo clippy --workspace --all-targets --all-features -- -D warnings
if: steps.scope.outputs.docs_only != 'true'
test:
runs-on: [self-hosted]
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
# Docs-only diffs skip the build but still report the check (WI-2401).
- id: scope
env:
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha }}
run: .github/scripts/docs-only.sh "$BASE_SHA"
- if: steps.scope.outputs.docs_only == 'true'
run: echo "docs-only change; skipping the Rust build"
- uses: dtolnay/rust-toolchain@stable
if: steps.scope.outputs.docs_only != 'true'
# Only main writes the cache back; every ref restores what main saved.
- uses: Swatinem/rust-cache@v2
if: steps.scope.outputs.docs_only != 'true'
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
# Pinned cargo-nextest, run from the job's temp dir. Some self-hosted
# runners carry an older cargo-nextest in $CARGO_HOME/bin, which cargo
# resolves before PATH, so neither install-action nor a PATH prepend
# is enough.
- if: steps.scope.outputs.docs_only != 'true'
run: |
curl -fsSL https://get.nexte.st/0.9.148/linux | tar -xzf - -C "$RUNNER_TEMP"
"$RUNNER_TEMP/cargo-nextest" nextest --version
# Quarantine (WI-2314, docs/known-flakes.md): rustc, cargo or the
# linker is sometimes OOM-killed on a shared runner. Retry once only
# when the log matches .github/scripts/oom-flake-signature.sh; any
# other failure fails at once. Runs on pull_request and merge_group
# alike. Remove with the host fix.
- name: nextest
if: steps.scope.outputs.docs_only != 'true'
shell: bash
run: |
set +e
nextest() {
"$RUNNER_TEMP/cargo-nextest" nextest run --workspace --all-features --no-tests=pass 2>&1 \
| tee "$RUNNER_TEMP/nextest.log"
return "${PIPESTATUS[0]}"
}
nextest; status=$?
if [ "$status" -ne 0 ] && .github/scripts/oom-flake-signature.sh "$RUNNER_TEMP/nextest.log"; then
echo "::warning::known OOM flake (WI-2314): rustc killed with no diagnostic; retrying once"
nextest; status=$?
fi
exit "$status"
- run: cargo test --workspace --all-features --doc
if: steps.scope.outputs.docs_only != 'true'
# Not a required check (WI-2480). Records restart-to-serving time on the
# merge queue. continue-on-error means a slow host, or a crash in the
# measurement, never fails the workflow. There is no time assert.
restart-timing:
runs-on: [self-hosted]
timeout-minutes: 30
continue-on-error: true
if: github.event_name == 'merge_group'
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- run: |
curl -fsSL https://get.nexte.st/0.9.148/linux | tar -xzf - -C "$RUNNER_TEMP"
- name: measure restart
shell: bash
run: |
"$RUNNER_TEMP/cargo-nextest" nextest run -p graft-server --all-features \
-E 'test(restart_serves_and_keeps_an_acknowledged_write)' --no-capture
# Not a required check. Runs the README quick starts only in the merge
# queue (WI-2478). A failing step or a missed line-merge fails the job.
docs-smoke:
runs-on: [self-hosted]
timeout-minutes: 30
continue-on-error: false
if: github.event_name == 'merge_group'
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: build graft
run: cargo build -p graft-cli
env:
CARGO_BUILD_JOBS: "4"
- name: README quick starts
run: scripts/docs-smoke.sh
env:
GRAFT_BIN: ${{ github.workspace }}/target/debug/graft