qctl 0.5.1

Control in-repo YAML work queues
# Hooks call mise tasks, so a hook runs the same command as CI. mise.dev.toml
# installs them through its postinstall hook; CI never loads that file, so no
# runner has them and no hook needs a CI skip.
#
# Every command runs with MISE_ENV=dev,hooks, from the hookenv anchor on the
# first command. MISE_ENV replaces the .miserc.toml default, so dev is named
# again; a mise.hooks.toml, where a repository has one, disables auto deps
# providers inside hooks.
pre-commit:
  commands:
    format:
      # *.rs matches every depth; **/*.rs misses files at the root.
      glob: "*.rs"
      env: &hookenv { MISE_ENV: "dev,hooks" }
      run: mise run format
    # TODO(TPL-002): remove once a mise release includes jdx/mise#14316.
    warm-remote-tasks:
      priority: 1
      env: *hookenv
      run: env -u GIT_INDEX_FILE mise tasks ls >/dev/null
    staged-secrets:
      env: *hookenv
      run: mise run hooks:secrets:staged

commit-msg:
  commands:
    # TODO(TPL-002): remove once a mise release includes jdx/mise#14316.
    warm-remote-tasks:
      priority: 1
      env: *hookenv
      run: env -u GIT_INDEX_FILE mise tasks ls >/dev/null
    committed:
      env: *hookenv
      run: mise run commit-msg {1}

pre-push:
  commands:
    lockfile:
      env: *hookenv
      run: mise install --locked
    # Local edit: lint each pushed commit that neither origin/main nor the
    # remote branch already has. Git passes one
    # `<local ref> <local sha> <remote ref> <remote sha>` line per ref on
    # stdin. A branch that merged main carries main's commits, and squash
    # merges land on main without this hook, so either base alone can let
    # those through. With neither base, the pushed commit is linted alone.
    commits:
      env: *hookenv
      use_stdin: true
      run: |
        zero=0000000000000000000000000000000000000000
        while read -r _ lsha _ rsha; do
          [ "$lsha" = "$zero" ] && continue
          bases=""
          if git rev-parse -q --verify origin/main >/dev/null; then
            bases="origin/main"
          fi
          if [ "$rsha" != "$zero" ] && git cat-file -e "$rsha^{commit}" 2>/dev/null; then
            bases="$bases $rsha"
          fi
          if [ -n "$bases" ]; then
            commits=$(git rev-list "$lsha" --not $bases)
          else
            commits=$lsha
          fi
          [ -n "$commits" ] || continue
          mise run commits $commits </dev/null || exit 1
        done
    verify:
      env: *hookenv
      # Local edit: git exports GIT_DIR and friends to hooks, and tests that
      # probe a scratch directory with `git -C` would find this repository.
      run: unset $(git rev-parse --local-env-vars) && mise run verify
    full-history-secrets:
      env: *hookenv
      run: mise run hooks:secrets:history