gwm-cli 1.2.0

git worktree manager — TUI + CLI, native libgit2, per-repo bootstrap
Documentation
name: ci

on:
  push:
    branches: [main, dev]
  pull_request:
    branches: [main, dev]
  workflow_dispatch:

env:
  CARGO_TERM_COLOR: always
  RUSTFLAGS: -D warnings
  # Force repo-only config loading so the suite never picks up a
  # runner's user-level ~/.config/gwm/config.toml (issue #190 opt-out).
  GWM_NO_GLOBAL_CONFIG: "1"

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

jobs:
  fmt:
    name: rustfmt
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt
      - name: cargo fmt --check
        run: cargo fmt --all -- --check

  clippy:
    name: clippy
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy
      - uses: Swatinem/rust-cache@v2
      - name: cargo clippy
        run: cargo clippy --all-targets --all-features -- -D warnings

  test:
    name: test (${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
    steps:
      - uses: actions/checkout@v7
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - name: configure git identity (libgit2 commit needs one)
        run: |
          git config --global user.email "ci@gwm.test"
          git config --global user.name "ci"
      - name: cargo build
        run: cargo build --verbose
      - name: cargo test
        run: cargo test --verbose

  hook-smoke:
    name: pre-commit hook smoke
    runs-on: ubuntu-latest
    # Cheap regression net for .githooks/pre-commit. Runs in parallel with
    # the heavy `test` job — no Rust toolchain needed, just shell + git.
    steps:
      - uses: actions/checkout@v7
      - name: shellcheck pre-commit hook
        uses: ludeeus/action-shellcheck@master
        with:
          scandir: ./.githooks
          severity: error
      - name: hook is executable
        run: test -x .githooks/pre-commit
      - name: hook short-circuits on empty index
        run: |
          scratch=$(mktemp -d)
          (
            cd "$scratch"
            git init -q
            git config user.email "ci@gwm.test"
            git config user.name "ci"
            cp "$GITHUB_WORKSPACE/.githooks/pre-commit" pre-commit
            sh pre-commit
          )
      - name: gate 2 detects .gwm.toml and skips when gwm absent
        run: |
          scratch=$(mktemp -d)
          (
            cd "$scratch"
            git init -q
            git config user.email "ci@gwm.test"
            git config user.name "ci"
            cp "$GITHUB_WORKSPACE/.githooks/pre-commit" pre-commit
            echo "[bootstrap]" > .gwm.toml
            git add .gwm.toml
            out=$(PATH="/usr/bin:/bin" sh pre-commit 2>&1)
            echo "$out"
            echo "$out" | grep -q "gwm not in PATH" \
              || (echo "FAIL: gate 2 should print 'gwm not in PATH' skip message" && exit 1)
          )

  audit:
    name: cargo audit
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - name: install cargo-audit
        run: cargo install cargo-audit --locked
      # `--deny warnings` makes warning-class advisories (unmaintained /
      # unsound / yanked) fail the job, not just outright vulnerabilities.
      # Plain `cargo audit` exits 0 on those, which is why RUSTSEC-2025-0068
      # (serde_yml, unsound + unmaintained) slipped past for ~9 months even
      # before accounting for `continue-on-error`. Accepted advisories go in
      # `audit.toml` (`[advisories] ignore = […]`) with a rationale so each
      # is a conscious decision (issue #340).
      - name: cargo audit
        run: cargo audit --deny warnings

  doctor:
    name: gwm doctor (advisory)
    runs-on: ubuntu-latest
    needs: test
    # Restrict to the `dev` integration branch only — `main` is meant to be
    # stable, the doctor exists to catch in-development regressions before
    # they reach a release. Without this guard the job would also run on
    # every push/PR targeting `main` (the workflow header lists both).
    if: |
      (github.event_name == 'push' && github.ref == 'refs/heads/dev') ||
      (github.event_name == 'pull_request' && github.base_ref == 'dev')
    steps:
      - uses: actions/checkout@v7
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - name: configure git identity
        run: |
          git config --global user.email "ci@gwm.test"
          git config --global user.name "ci"
      # Build the binary once with the same release profile gh actions
      # cache benefits from, then ask it to diagnose this very repo.
      # Advisory: a non-zero exit means we want eyes on the report, but
      # not a blocked merge. `lazygit` is intentionally absent on the
      # runner so a Warning here is the floor, not a regression.
      - name: cargo build
        run: cargo build --release --quiet
      - name: gwm doctor
        run: ./target/release/gwm doctor
        continue-on-error: true