Install repository-declared hooks before worker tasks; read Vault token from VAULT_TOKEN_FILE #1

Open
riley wants to merge 7 commits from fix/worker-install-repo-hooks into main
Owner

Two changes so workers stop bypassing repository hooks and stop failing on a stale Vault token.

1. Install repository-declared hooks before a task (977fe8e7)

prepare_git only installed CodeTether's provenance commit-msg hook, so local workers cloned riley/spotlessbinco without its pre-commit/post-commit/pre-push cache-gate hooks and pushed 49 branches that bypassed the cache.

  • New codetether_worktree::repository::install_declared_hooks, next to the declared-hook policy, mirroring scripts/harvester/job_bootstrap.py::install_hooks:
    • .pre-commit-config.yaml → pre-commit install --install-hooks (requires pre-commit on PATH; honours default_install_hook_types)
    • .githooks/pre-push → bash scripts/dev/install-pre-push-cache-gate.sh (script must exist)
    • stdin closed, timeout CODETETHER_HOOK_INSTALL_TIMEOUT_SECONDS (default 900); missing tool/script, nonzero exit or timeout → repository hooks could not be installed (...)
  • prepare_git: repository hooks first, then the provenance commit-msg hook (which no longer replaces a repository-owned commit-msg), then require_installed. The task is refused on failure instead of logging a warning.

2. VAULT_TOKEN_FILE (934485e2)

  • SecretsManager::from_env: after the login profile and Kubernetes auth, VAULT_TOKEN_FILE takes precedence over VAULT_TOKEN. Its trimmed contents are the token; an unreadable or empty file is an error; the token is never logged.
  • On 403 / invalid token, the file is re-read and the client rebuilt once (a second refresh source beside Kubernetes). Behaviour is unchanged when the variable is unset.
  • The contract fixture now records X-Vault-Token, so tests prove which token was sent.

Validation (local)

  • cargo fmt -p codetether-worktree -p codetether-a2a-worker-core -p codetether-secrets -- --check → clean
  • cargo test -p codetether-secrets -p codetether-worktree -p codetether-a2a-worker-core → all pass (unit: 57, 5, 31, 65, 1; doc: 95, 25, 38; 0 failed)
    • new: 7 hook-installer tests, 3 prepare_git tests, 3 token-file unit tests, 4 token-file contract tests
  • cargo clippy -p <these 3> --all-targets --no-deps -- -D warnings: 0 findings in touched files. The 31 remaining findings are pre-existing in untouched files (base origin/main has 38; none new). Without --no-deps, clippy stops on pre-existing errors in codetether-slash-command.
Two changes so workers stop bypassing repository hooks and stop failing on a stale Vault token. ## 1. Install repository-declared hooks before a task (`977fe8e7`) `prepare_git` only installed CodeTether's provenance `commit-msg` hook, so local workers cloned riley/spotlessbinco without its pre-commit/post-commit/pre-push cache-gate hooks and pushed 49 branches that bypassed the cache. - New `codetether_worktree::repository::install_declared_hooks`, next to the declared-hook policy, mirroring `scripts/harvester/job_bootstrap.py::install_hooks`: - `.pre-commit-config.yaml` → `pre-commit install --install-hooks` (requires `pre-commit` on PATH; honours `default_install_hook_types`) - `.githooks/pre-push` → `bash scripts/dev/install-pre-push-cache-gate.sh` (script must exist) - stdin closed, timeout `CODETETHER_HOOK_INSTALL_TIMEOUT_SECONDS` (default 900); missing tool/script, nonzero exit or timeout → `repository hooks could not be installed (...)` - `prepare_git`: repository hooks first, then the provenance `commit-msg` hook (which no longer replaces a repository-owned `commit-msg`), then `require_installed`. **The task is refused** on failure instead of logging a warning. ## 2. `VAULT_TOKEN_FILE` (`934485e2`) - `SecretsManager::from_env`: after the login profile and Kubernetes auth, `VAULT_TOKEN_FILE` takes precedence over `VAULT_TOKEN`. Its trimmed contents are the token; an unreadable or empty file is an error; the token is never logged. - On 403 / invalid token, the file is re-read and the client rebuilt once (a second refresh source beside Kubernetes). Behaviour is unchanged when the variable is unset. - The contract fixture now records `X-Vault-Token`, so tests prove which token was sent. ## Validation (local) - `cargo fmt -p codetether-worktree -p codetether-a2a-worker-core -p codetether-secrets -- --check` → clean - `cargo test -p codetether-secrets -p codetether-worktree -p codetether-a2a-worker-core` → all pass (unit: 57, 5, 31, 65, 1; doc: 95, 25, 38; 0 failed) - new: 7 hook-installer tests, 3 `prepare_git` tests, 3 token-file unit tests, 4 token-file contract tests - `cargo clippy -p <these 3> --all-targets --no-deps -- -D warnings`: 0 findings in touched files. The 31 remaining findings are pre-existing in untouched files (base `origin/main` has 38; none new). Without `--no-deps`, clippy stops on pre-existing errors in `codetether-slash-command`.
prepare_git only installed CodeTether's provenance commit-msg hook, so local
workers cloned repositories such as riley/spotlessbinco without their
pre-commit, post-commit and pre-push cache-gate hooks and pushed branches that
bypassed the cache.

codetether-worktree gains repository::install_declared_hooks, next to the
declared-hook policy, mirroring scripts/harvester/job_bootstrap.py:
- .pre-commit-config.yaml: require pre-commit on PATH and run
  'pre-commit install --install-hooks' (honours default_install_hook_types);
- .githooks/pre-push: require scripts/dev/install-pre-push-cache-gate.sh and
  run it with bash.
Each installer runs with stdin closed and a timeout from
CODETETHER_HOOK_INSTALL_TIMEOUT_SECONDS (default 900). A missing tool or
script, nonzero exit or timeout fails with 'repository hooks could not be
installed (...)'.

prepare_git now installs the repository's hooks first, then the provenance
commit-msg hook, which no longer replaces a repository-owned commit-msg hook
(pre-commit only writes the hook types it is configured for). It then
verifies the result with require_installed and refuses the task on failure
instead of warning.
A token pasted into a systemd EnvironmentFile goes stale; the workstation
vault agent instead keeps a valid periodic token in a 0600 sink file and
rotates it.

SecretsManager::from_env now honours VAULT_TOKEN_FILE after the saved login
profile and Kubernetes workload auth: its trimmed contents are the token, it
takes precedence over VAULT_TOKEN, and an unreadable or empty file is an
error. The token is never logged.

When Vault rejects the token (403 / invalid token / permission denied), the
manager re-reads the file and retries once with a rebuilt client, so a
rotated sink is picked up without a restart. This is a second refresh source
beside Kubernetes reauthentication; behaviour is unchanged when neither is
configured. The contract fixture now records X-Vault-Token so tests prove
which token was sent.
This pull request can be merged automatically.
The changes on this branch are already on the target branch. This will be an empty commit.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin fix/worker-install-repo-hooks:fix/worker-install-repo-hooks
git switch fix/worker-install-repo-hooks

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch main
git merge --no-ff fix/worker-install-repo-hooks
git switch fix/worker-install-repo-hooks
git rebase main
git switch main
git merge --ff-only fix/worker-install-repo-hooks
git switch fix/worker-install-repo-hooks
git rebase main
git switch main
git merge --no-ff fix/worker-install-repo-hooks
git switch main
git merge --squash fix/worker-install-repo-hooks
git switch main
git merge --ff-only fix/worker-install-repo-hooks
git switch main
git merge fix/worker-install-repo-hooks
git push origin main
Sign in to join this conversation.
No description provided.