mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-14 02:42:24 +03:00
A worktree created with the documented `cp -al` recipe has no `.husky/_`: the directory is gitignored, so it never exists in a fresh worktree, and `core.hooksPath=.husky/_` then resolves to a path that is not there. git finds no hook, commits, and prints nothing — the identity, lint and docs gates simply do not run. That is how the 59 commits carrying a stale identity override slipped past scripts/check/check-git-identity.sh between 2026-08-29 and 09-02: every one of them was made in a codex-* worktree built that way. Two changes: - AGENTS.md documents the missing step in the worktree recipe and states the failure mode plainly, since a silent gate is indistinguishable from a passing one. - scripts/dev/new-worktree.sh performs the whole protocol (canonical .claude/worktrees/ path, base resolved from the active release when not given, hard-linked node_modules, `.husky/_`) and then VERIFIES the hook is executable, exiting non-zero when it is not. The script copies `.husky/_` before node_modules on purpose: node_modules is a ~10 GB hard-link pass that can fail partway (this repo has already hit the ext4 link ceiling), and a failure there must not leave a worktree with no hooks — the exact defect being fixed. A partial node_modules copy is reported with its log and left recoverable by `npm install`, rather than aborting the worktree. It also resolves the main checkout through `--git-common-dir` rather than `--show-toplevel`, so running it from inside a worktree does not nest the new one inside the current one.
93 lines
4.1 KiB
Bash
Executable File
93 lines
4.1 KiB
Bash
Executable File
#!/usr/bin/env sh
|
|
# Cria uma worktree isolada seguindo o protocolo obrigatório do AGENTS.md
|
|
# (Git Workflow → "Worktree isolation" / Hard Rule #19), incluindo os dois
|
|
# passos que são fáceis de esquecer e falham em silêncio:
|
|
#
|
|
# 1. node_modules por HARD LINK (`cp -al`), nunca symlink — um symlink que
|
|
# resolve fora da raiz mata o Turbopack com um FATAL que culpa a
|
|
# "filesystem root" e não a worktree (incidente 2026-07-31, #9043).
|
|
# 2. `.husky/_` copiado — é gitignored, então uma worktree nova NÃO o tem, e
|
|
# `core.hooksPath=.husky/_` aponta para um diretório inexistente: TODOS os
|
|
# hooks de pre-commit ficam mudos, sem aviso nenhum. Foi assim que 59
|
|
# commits com identidade trocada passaram pelo gate entre 29/08 e 02/09
|
|
# (ver .mailmap e scripts/check/check-git-identity.sh).
|
|
#
|
|
# Uso: scripts/dev/new-worktree.sh <branch> [base-branch]
|
|
# Ex.: scripts/dev/new-worktree.sh fix/12345-algo release/v3.8.51
|
|
|
|
set -e
|
|
|
|
BRANCH="$1"
|
|
BASE="$2"
|
|
|
|
if [ -z "$BRANCH" ]; then
|
|
echo "uso: scripts/dev/new-worktree.sh <branch> [base-branch]" >&2
|
|
echo " ex: scripts/dev/new-worktree.sh fix/12345-algo release/v3.8.51" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# O checkout PRINCIPAL, mesmo quando este script roda de dentro de outra worktree:
|
|
# `--show-toplevel` devolveria a worktree atual, e a nova nasceria aninhada nela.
|
|
MAIN=$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")
|
|
cd "$MAIN"
|
|
|
|
# Sem base explícita, usa a release ativa (maior release/* por semver) — nunca
|
|
# `main` e nunca "a branch em que eu estou", conforme a Hard Rule #19.
|
|
if [ -z "$BASE" ]; then
|
|
BASE=$(git ls-remote --heads origin 'refs/heads/release/*' \
|
|
| sed 's#.*refs/heads/##' | sort -V | tail -1)
|
|
[ -z "$BASE" ] && { echo "não consegui resolver a release ativa; passe a base explicitamente" >&2; exit 1; }
|
|
echo "base não informada — usando a release ativa: $BASE"
|
|
fi
|
|
|
|
DIR=".claude/worktrees/${BRANCH##*/}"
|
|
[ -e "$DIR" ] && { echo "já existe: $DIR" >&2; exit 1; }
|
|
|
|
git fetch origin "$BASE" --quiet
|
|
git worktree add "$DIR" -b "$BRANCH" "origin/$BASE"
|
|
|
|
# `.husky/_` PRIMEIRO: é minúsculo e é o que decide se os gates locais rodam.
|
|
# Copiar node_modules antes seria arriscar abortar (set -e) numa árvore de ~10 GB
|
|
# e deixar a worktree sem hook nenhum — exatamente o defeito que este script existe
|
|
# para impedir.
|
|
if [ -d "$MAIN/.husky/_" ]; then
|
|
cp -a "$MAIN/.husky/_" "$DIR/.husky/_"
|
|
else
|
|
echo "AVISO: .husky/_ não existe no checkout principal — rode 'npm install' lá primeiro" >&2
|
|
fi
|
|
|
|
# node_modules: hard links, ~5s e disco quase zero (inodes compartilhados).
|
|
# Um `cp -al SRC DEST` com DEST já existente aninharia SRC DENTRO dele
|
|
# (node_modules/node_modules), então DEST não pode existir aqui.
|
|
if [ -d "$MAIN/node_modules/node_modules" ]; then
|
|
echo "AVISO: $MAIN/node_modules/node_modules existe — resíduo de um cp -al aninhado." >&2
|
|
echo " Ele infla a cópia e esgota o limite de hard links; convém removê-lo." >&2
|
|
fi
|
|
if [ -d "$MAIN/node_modules" ]; then
|
|
# Falha parcial (limite de hard links, disco) não pode derrubar a worktree inteira:
|
|
# os hooks já estão no lugar e o npm install continua sendo uma saída válida.
|
|
if cp -al "$MAIN/node_modules" "$DIR/node_modules" 2>"$DIR/.cp-node-modules.log"; then
|
|
echo "node_modules: $(ls "$DIR/node_modules" | wc -l) entradas (hard links)"
|
|
rm -f "$DIR/.cp-node-modules.log"
|
|
else
|
|
echo "AVISO: a cópia de node_modules falhou parcialmente (veja $DIR/.cp-node-modules.log)." >&2
|
|
echo " Primeiras linhas:" >&2
|
|
head -3 "$DIR/.cp-node-modules.log" >&2
|
|
fi
|
|
else
|
|
echo "AVISO: node_modules não existe no checkout principal — rode 'npm install' lá primeiro" >&2
|
|
fi
|
|
|
|
# Verificação: o hook precisa estar REALMENTE ativo, não apenas presente.
|
|
HOOKS_PATH=$(git -C "$DIR" config --get core.hooksPath || echo ".git/hooks")
|
|
if [ -x "$DIR/$HOOKS_PATH/pre-commit" ]; then
|
|
echo "hooks: ativos ($HOOKS_PATH/pre-commit)"
|
|
else
|
|
echo "AVISO: pre-commit NÃO está ativo em $DIR/$HOOKS_PATH — os gates locais não vão rodar" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo
|
|
echo "pronto: $DIR (branch $BRANCH, base $BASE)"
|
|
echo " cd $DIR"
|