diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..a5f393c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,54 @@ +name: CI + +# Runs on pull requests into main. Branch protection requires the `checks` +# job to pass before merge. Not run on push to main: pre-commit's +# no-commit-to-branch hook would fail there by design, and merges are gated +# at the PR instead. +on: + pull_request: + branches: [main] + +permissions: + contents: read + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + checks: + name: checks + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Cache pre-commit environments + uses: actions/cache@v4 + with: + path: ~/.cache/pre-commit + key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }} + + - name: Install pre-commit + run: python -m pip install --upgrade pip pre-commit + + - name: Run pre-commit (all files) + run: pre-commit run --all-files --show-diff-on-failure --color always + + - name: Compile Python + run: python -m compileall -q hooks plugin + + - name: Installer dry-run (hermetic) + run: | + # The turn-hook helper ships with ferrosa-memory, not this repo; stub + # it so the installer's existence check passes (the dry-run only checks + # it exists, never executes it). Write wrappers to a temp dir so the + # runner's real ~/.config is never touched. + mkdir -p scripts/hooks + printf '#!/usr/bin/env python3\n' > scripts/hooks/ferrosa-memory-turn-hook.py + python3 hooks/install-agent-hooks.py \ + --harness all --dry-run --no-apply-config --skip-auth-check \ + --install-dir "$RUNNER_TEMP/fmem-hooks" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2cbee45..72cbc78 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -71,7 +71,11 @@ repos: rev: 1.8.0 hooks: - id: bandit - args: [-r, --skip=B101] + # Skipped checks reflect intentional, documented design (see CLAUDE.md): + # B101 assert; B110 try/except/pass (fail-open config load); + # B310 urllib.urlopen (the raw-urllib MCP client, loopback endpoint); + # B404/B603 subprocess (installer's fixed verification commands). + args: [-r, "--skip", "B101,B110,B310,B404,B603"] files: ^(plugin|hooks)/.*\.py$ # ---------------------------------------------------------------------------