Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 6 additions & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,21 @@
# Windows dep check, and changelog verification (on PRs to main).
# No secrets required — only needs contents: read and pull-requests: read.
#
# Push trigger is scoped to main/dev only — feature branches get CI via
# pull_request. This prevents duplicate runs when a branch push and PR
# creation fire seconds apart. tags-ignore prevents CI on release tag pushes
# (the release workflow handles those). paths-ignore skips doc-only pushes.
# paths-ignore is NOT applied to pull_request to avoid blocking required
# status checks when a PR only touches ignored paths.
# pull_request only: merges to main/dev are squash merges, so a push run
# would re-verify the identical tree already checked at the PR head.
name: CI

on:
push:
branches: [main, dev]
tags-ignore: ['**']
paths-ignore:
- '**.md'
- 'LICENSE-*'
- 'docs/**'
pull_request:

permissions:
contents: read
pull-requests: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
ci:
uses: brettdavies/.github/.github/workflows/rust-ci.yml@main
9 changes: 7 additions & 2 deletions .github/workflows/skill-fixture-drift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,22 @@

name: skill-fixture-drift

# pull_request only: merges to main/dev are squash merges, so a push run
# would re-check the identical tree already checked at the PR head.
on:
pull_request:
push:
branches: [main, dev]

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
drift-check:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ Local setup:
```bash
git clone https://github.com/brettdavies/agentnative-cli
cd agentnative-cli
git config core.hooksPath scripts/hooks # mirror CI locally on every push
git config core.hooksPath scripts/hooks # fast checks on commit, full CI mirror on push
cargo test
cargo run -- audit .
```
Expand Down
75 changes: 75 additions & 0 deletions scripts/hooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/usr/bin/env bash
# Pre-commit hook: fast checks scoped to staged files only.
#
# Defensive: each tool no-ops with a notice when not installed, so it never
# blocks a commit on a machine that hasn't set up the full toolchain yet.
# Kept staged-file-scoped (not whole-repo) so it stays fast enough to run on
# every commit; the whole-repo equivalents live in scripts/hooks/pre-push.
#
# Activated per clone via: git config core.hooksPath scripts/hooks
set -euo pipefail

RED='\033[0;31m'
GREEN='\033[0;32m'
BOLD='\033[1m'
RESET='\033[0m'

pass() { echo -e " ${GREEN}✓${RESET} $1"; }
fail() { echo -e " ${RED}✗${RESET} $1"; exit 1; }

echo -e "${BOLD}Running pre-commit checks...${RESET}"

mapfile -t staged_files < <(git diff --cached --name-only --diff-filter=ACM)

staged_rs=()
staged_md=()
staged_sh=()
for f in "${staged_files[@]}"; do
case "$f" in
*.rs) staged_rs+=("$f") ;;
*.md) staged_md+=("$f") ;;
*.sh) staged_sh+=("$f") ;;
esac
done
if printf '%s\n' "${staged_files[@]}" | grep -qx 'scripts/hooks/pre-commit\|scripts/hooks/pre-push'; then
staged_sh+=("scripts/hooks/pre-commit" "scripts/hooks/pre-push")
fi

# 1. Rust formatting — staged .rs files only
if [ ${#staged_rs[@]} -gt 0 ]; then
if command -v cargo >/dev/null 2>&1; then
cargo fmt -- --check 2>/dev/null || fail "cargo fmt -- --check (run \`cargo fmt\` and re-stage)"
pass "fmt (${#staged_rs[@]} file(s))"
else
echo " - fmt (skipped: cargo not installed)"
fi
else
echo " - fmt (skipped: no staged .rs files)"
fi

# 2. Markdown lint — staged .md files only
if [ ${#staged_md[@]} -gt 0 ]; then
if command -v markdownlint-cli2 >/dev/null 2>&1; then
markdownlint-cli2 "${staged_md[@]}" || fail "markdownlint-cli2"
pass "markdownlint (${#staged_md[@]} file(s))"
else
echo " - markdownlint (skipped: install via \`brew install markdownlint-cli2\`)"
fi
else
echo " - markdownlint (skipped: no staged .md files)"
fi

# 3. Shellcheck — staged .sh files (and the hooks themselves if touched)
if [ ${#staged_sh[@]} -gt 0 ]; then
if command -v shellcheck >/dev/null 2>&1; then
mapfile -t staged_sh_uniq < <(printf '%s\n' "${staged_sh[@]}" | sort -u)
shellcheck --severity=warning "${staged_sh_uniq[@]}" || fail "shellcheck --severity=warning"
pass "shellcheck (${#staged_sh_uniq[@]} file(s))"
else
echo " - shellcheck (skipped: install via \`brew install shellcheck\`)"
fi
else
echo " - shellcheck (skipped: no staged .sh files)"
fi

echo -e "${BOLD}${GREEN}Pre-commit checks passed.${RESET}"