diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml index ee038c41..60535005 100644 --- a/.forgejo/workflows/ci.yml +++ b/.forgejo/workflows/ci.yml @@ -3,78 +3,106 @@ name: CI on: pull_request: branches: ["**"] - # Lets `hive-forge ci-rerun` re-trigger CI via workflow-dispatch API + # Lets `hive-forge ci-rerun` re-trigger CI via the workflow-dispatch API + # without an empty commit. No effect on the PR-triggered runs above. workflow_dispatch: jobs: check: name: nix flake check runs-on: [hive-ci] - # 30 min is well above a cold-cache rebuild (~15 min observed) and well - # under the runner's 3h cap + # Bound the job so a wedged build fails in minutes instead of + # hanging until the runner's 3h cap (or, when the runner itself + # deadlocks, never). 30 min is well above a cold-cache rebuild + # (~15 min observed) and well under the 3h hard cap — tune if a + # legit cold build ever trips it. timeout-minutes: 30 steps: - uses: actions/checkout@v3 - name: check + # Runs all flake checks: formatting (treefmt+rustfmt), cargo test, + # cargo clippy, and module evaluation. No --no-build: the checks + # derivations are the canonical source of truth. run: nix flake check tracker-tags: name: tracker-tag lint runs-on: [hive-ci] + # Pure git+grep — seconds normally; a few minutes is already a hang. timeout-minutes: 5 steps: - uses: actions/checkout@v3 - name: lint + # Flags hash-number tracker tags in source (hive convention is + # prose, not tags — /knowledge/hive-rules.md). Its own job, and + # IS a required check on the forge (branch protection) — a hit + # blocks merge. See scripts/check-issue-refs.sh. run: sh scripts/check-issue-refs.sh comment-blocks: name: comment-block lint runs-on: [hive-ci] + # Pure git+awk — seconds. timeout-minutes: 5 steps: - uses: actions/checkout@v3 - name: lint + # Flags contiguous comment blocks over 30 lines (a giant prose + # block belongs in docs/ as implementation notes, not in source). + # Own job, and IS a required check on the forge (branch + # protection) — a hit blocks merge. See + # scripts/check-comment-blocks.sh. run: sh scripts/check-comment-blocks.sh doc-refs: name: doc-pointer lint runs-on: [hive-ci] + # Pure git+grep — seconds. timeout-minutes: 5 steps: - uses: actions/checkout@v3 - name: lint + # Flags `docs/…md` paths and relative markdown links that no + # longer resolve. Pointing at a doc is what the comment-block + # lint above pushes people toward, so the pointers need a gate of + # their own. See scripts/check-doc-refs.sh. run: sh scripts/check-doc-refs.sh - attribution-trailers: - name: attribution-trailer lint - runs-on: [hive-ci] - timeout-minutes: 5 - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: lint - run: sh scripts/check-attribution-trailers.sh - prose-lint: name: prose lint (vale) runs-on: [hive-ci] + # A `vale sync` + full docs/ run took under a minute in testing. timeout-minutes: 5 steps: - uses: actions/checkout@v3 - name: lint - # Styles are fetched fresh from Vale Package Hub each run (not vendored). - # Not wired into branch protection — see prose-lint-errors below for the - # error-only slice that is. + # Styles (Microsoft + alex + write-good, named in .vale.ini's + # `Packages` line) are fetched fresh from the Vale Package Hub each + # run rather than vendored, so this job always lints against + # upstream's current rules. Failing red on a hit is intentional -- + # the existing docs/ tree has a standing backlog of warning- and + # suggestion-level rule hits, so this job stays red on unrelated + # PRs until that backlog is worked down (tracked separately, as a + # follow-up to the error-only work below). Not wired into branch + # protection -- see the sibling job below for the error-only slice + # that is. run: XDG_DATA_HOME="$PWD/.vale-data" nix shell nixpkgs#vale --command sh -c 'vale sync && vale docs' prose-lint-errors: name: prose lint (vale, errors) runs-on: [hive-ci] + # Own `vale sync` rather than reusing the sibling job's: each job gets + # a fresh checkout/environment, so there is nothing to share between + # them without an artifact-upload step this doesn't need. timeout-minutes: 5 steps: - uses: actions/checkout@v3 - name: lint - # Error-level-only slice. Split out so this job alone can be a required - # check without blocking merges on the warning/suggestion backlog. + # The error-level-only slice of the sibling job above. + # `--minAlertLevel=error` changes what vale both reports AND exits + # non-zero on, so this job's pass/fail tracks only error-severity + # rule hits, not the warning/suggestion backlog the sibling job + # carries. Split out so this one alone can be a required check + # (forge branch protection) without blocking merges on the + # pre-existing backlog the sibling job still surfaces. run: XDG_DATA_HOME="$PWD/.vale-data" nix shell nixpkgs#vale --command sh -c 'vale sync && vale --minAlertLevel=error docs' diff --git a/docs/scheduler/ci.md b/docs/scheduler/ci.md index 74196cd5..da1b9b57 100644 --- a/docs/scheduler/ci.md +++ b/docs/scheduler/ci.md @@ -41,15 +41,14 @@ gating on the full job's standing warning/suggestion backlog, but flipping that switch on the forge is a separate operator action, not something this table can assert ahead of it happening. -| Job | What it runs | Required? | -| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | -| **nix flake check** | treefmt formatting, `cargo clippy -D warnings`, `cargo test`, module evaluation | yes | -| **tracker-tag lint** | flags `#NNN` issue tags in source and comments (`scripts/check-issue-refs.sh`) | yes | -| **comment-block lint** | flags contiguous comment blocks over 30 lines (`scripts/check-comment-blocks.sh`) | yes | -| **doc-pointer lint** | flags `docs/…md` paths and relative markdown links that no longer resolve (`scripts/check-doc-refs.sh`) | yes | -| **attribution-trailer lint** | flags `Co-Authored-By` trailers with Claude/Anthropic attribution in PR commits (`scripts/check-attribution-trailers.sh`) | not yet — intended to be | -| **prose lint (vale, errors)** | vale, `docs/` tree, error-severity rule hits only | not yet — intended to be | -| **prose lint (vale)** | vale, `docs/` tree, full severity range (warnings + suggestions too) | no — advisory, standing backlog | +| Job | What it runs | Required? | +| ----------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------- | +| **nix flake check** | treefmt formatting, `cargo clippy -D warnings`, `cargo test`, module evaluation | yes | +| **tracker-tag lint** | flags `#NNN` issue tags in source and comments (`scripts/check-issue-refs.sh`) | yes | +| **comment-block lint** | flags contiguous comment blocks over 30 lines (`scripts/check-comment-blocks.sh`) | yes | +| **doc-pointer lint** | flags `docs/…md` paths and relative markdown links that no longer resolve (`scripts/check-doc-refs.sh`) | yes | +| **prose lint (vale, errors)** | vale, `docs/` tree, error-severity rule hits only | not yet — intended to be | +| **prose lint (vale)** | vale, `docs/` tree, full severity range (warnings + suggestions too) | no — advisory, standing backlog | `hive-forge ci-rerun --pr N` dispatches a `workflow_dispatch` retrigger without an empty commit. diff --git a/scripts/check-attribution-trailers.sh b/scripts/check-attribution-trailers.sh deleted file mode 100755 index c2fc2b73..00000000 --- a/scripts/check-attribution-trailers.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/sh -# Flags Co-Authored-By trailers with Claude/Anthropic attribution in PR commits. -# The hive convention in /knowledge/hive-rules.md forbids these trailers. -# -# Pattern is anchored to an actual git trailer line — `^[[:space:]]*co-authored-by:` -# — not any mention of the phrase, then requires the value name `claude`, -# `anthropic`, or `noreply@anthropic.com`. Matching is case-insensitive and -# per-line (git trailers are one `Key: value` per line) to cover variants like -# `Co-Authored-By: Claude Sonnet 4.5 ` while letting a -# commit subject/body that merely *discusses* the trailer (e.g. this script's -# own "add CI lint for Co-Authored-By/Claude/Anthropic trailers") pass. -# -# Scope is the PR's commits (BASE..HEAD), not entire history — old commits -# must not fail the build. -set -eu - -trailer_re='^[[:space:]]*co-authored-by:.*(claude|anthropic|noreply@anthropic\.com)' - -if [ -n "${GITHUB_BASE_REF:-}" ]; then - base="origin/${GITHUB_BASE_REF}" -else - base="origin/main" -fi - -# actions/checkout@v3 fetches the PR head but may not fetch the base branch -if ! git rev-parse --verify "$base" >/dev/null 2>&1; then - branch="${base#origin/}" - git fetch origin "$branch" 2>/dev/null || true - if ! git rev-parse --verify "$base" >/dev/null 2>&1; then - base="origin/main" - git fetch origin main 2>/dev/null || base="HEAD~10" - fi -fi - -commits="$(git log --reverse --format='%H' "${base}..HEAD" 2>/dev/null || true)" - -if [ -z "$commits" ]; then - exit 0 -fi - -hits="" -for sha in $commits; do - msg="$(git log -1 --format='%B' "$sha")" - if printf '%s\n' "$msg" | grep -qiE "$trailer_re"; then - subject="$(git log -1 --format='%s' "$sha")" - hits="${hits}${sha}|${subject} -" - fi -done - -if [ -n "$hits" ]; then - printf '%s' "$hits" | while IFS='|' read -r sha subject; do - [ -z "$sha" ] && continue - short_sha="$(printf '%s' "$sha" | cut -c1-8)" - printf '::error title=Attribution trailer found in %s::%s — commit carries a Co-Authored-By trailer with Claude/Anthropic attribution, forbidden by /knowledge/hive-rules.md\n' \ - "$short_sha" "$subject" - done - count="$(printf '%s' "$hits" | grep -c '|' || true)" - printf 'check-attribution-trailers: %s commit(s) with prohibited trailers found\n' "$count" >&2 - exit 1 -fi - -exit 0 diff --git a/scripts/pre-push b/scripts/pre-push index 0f6bf3c2..62634252 100755 --- a/scripts/pre-push +++ b/scripts/pre-push @@ -1,8 +1,17 @@ #!/bin/sh -# Install: ln -sf ../../scripts/pre-push .git/hooks/pre-push +# Git pre-push hook: runs the tracker-tag, comment-block and doc-pointer +# lints against the working tree before any push lands on the remote. +# Catches issues that would fail CI and require a follow-up commit +# (common failure mode: a nix comment containing a hash-issue-number +# tag added mid-session). # -# Runs against the full working tree (not just staged or pushed files) to match -# what CI sees: `nix flake check` builds from the committed tree. +# Install (once per clone): +# ln -sf ../../scripts/pre-push .git/hooks/pre-push +# +# The hook runs against the full working tree (not just staged or +# pushed files) to match what CI sees: `nix flake check` builds from +# the committed tree, so an untracked hit on a staged file would still +# trip CI. set -eu repo_root="$(git rev-parse --show-toplevel)" @@ -25,11 +34,5 @@ if ! sh "$repo_root/scripts/check-doc-refs.sh"; then exit 1 fi -echo "pre-push: running attribution-trailer lint..." >&2 -if ! sh "$repo_root/scripts/check-attribution-trailers.sh"; then - echo "pre-push: attribution-trailer lint FAILED — fix before pushing" >&2 - exit 1 -fi - echo "pre-push: lints passed ✓" >&2 exit 0