clone

a personal forge on plain git: static pages, stacked-diff reviews, signed local ci, repo sites and releases

main
forge / test / scenarios / approval-rules.sh
approval-rules.sh 4.7K
1# .forge/approvals on the default branch says how much approval a change2# to it needs, by path (lib/rules.ts); forge.requireapproval makes the forge3# hold landings to it. The rules are read from main before the change, so4# a review can't loosen its own.5. "$(dirname "$0")/../lib.sh"6user dami7user ana8user fox9repo dami/greet10G=$FORGE_ROOT/dami/greet.git11clone dami dami/greet "$T/w/d"12mkdir -p "$T/w/d/.forge" "$T/w/d/lib" "$T/w/d/docs"13cat >"$T/w/d/.forge/approvals" <<'EOF'14# paths rule15.forge/ 216lib/secret.sh 1 from fox17*.md docs/ none18* 119EOF20echo a >"$T/w/d/lib/secret.sh"21echo a >"$T/w/d/lib/plain.sh"22echo a >"$T/w/d/README.md"23at "$T/w/d" git add -A24at "$T/w/d" git commit -q -m "rules"25ok push_as dami "$T/w/d" origin main26git --git-dir="$G" config forge.requireapproval true2728# lib/rules.ts on its own: what each file takes, and what a change needs.29eq "$(node "$T_HOME/lib/rules.ts" check --git-dir "$G" --base - --tip main --rules-at main --json | sed 's/"have":\[\][^}]*//g' | tr -d '\n' | grep -o '"need":"[^"]*","paths":\[[^]]*\]' | tr '\n' ' ')" \30 '"need":"2 approvals","paths":[".forge/approvals"] "need":"1 approval","paths":["lib/plain.sh"] "need":"1 from fox","paths":["lib/secret.sh"] '3132# Docs only: no approval needed, even with forge.requireapproval.33at "$T/w/d" git switch -q -c dami/docs34echo b >>"$T/w/d/README.md"35mkdir -p "$T/w/d/docs" && echo x >"$T/w/d/docs/guide.txt"36at "$T/w/d" git add -A37at "$T/w/d" git commit -q -m "docs"38ok push_as dami "$T/w/d" origin dami/docs39ok push_as dami "$T/w/d" origin dami/docs:main4041# A plain file needs one approval from someone else.42echo b >>"$T/w/d/lib/plain.sh"43at "$T/w/d" git commit -q -am "plain"44ok push_as dami "$T/w/d" origin HEAD:dami/plain45fails_with 'needs 1 approval \(\.forge/approvals:5\) for lib/plain.sh: no approvals' push_as dami "$T/w/d" origin HEAD:main46ok ssh_as ana approve dami/greet dami/plain47ok push_as dami "$T/w/d" origin HEAD:main4849# lib/secret.sh needs fox: ana's approval isn't enough.50echo b >>"$T/w/d/lib/secret.sh"51at "$T/w/d" git commit -q -am "secret"52ok push_as dami "$T/w/d" origin HEAD:dami/secret53ok ssh_as ana approve dami/greet dami/secret54fails_with 'needs 1 from fox \(\.forge/approvals:3\) for lib/secret.sh: no approvals' push_as dami "$T/w/d" origin HEAD:main55ok ssh_as fox approve dami/greet dami/secret56ok push_as dami "$T/w/d" origin HEAD:main5758# Moving a file out of a guarded path needs that path's rule too.59at "$T/w/d" git mv lib/secret.sh lib/moved.sh60at "$T/w/d" git commit -q -m "move"61ok push_as dami "$T/w/d" origin HEAD:dami/move62ok ssh_as ana approve dami/greet dami/move63fails_with 'needs 1 from fox' push_as dami "$T/w/d" origin HEAD:main64at "$T/w/d" git reset -q --hard origin/main6566# A review that loosens the rules lands under the old ones: .forge/ needs 2.67printf '* none\n' >"$T/w/d/.forge/approvals"68echo c >>"$T/w/d/lib/plain.sh"69at "$T/w/d" git commit -q -am "loosen"70ok push_as dami "$T/w/d" origin HEAD:dami/loosen71fails_with 'needs 2 approvals \(\.forge/approvals:2\) for \.forge/approvals' push_as dami "$T/w/d" origin HEAD:main72ok ssh_as ana approve dami/greet dami/loosen73fails_with 'needs 2 approvals .*: approved by ana *$' push_as dami "$T/w/d" origin HEAD:main74ok ssh_as fox approve dami/greet dami/loosen75ok push_as dami "$T/w/d" origin HEAD:main76# Now they are loose: nothing needs approval.77echo d >>"$T/w/d/lib/plain.sh"78at "$T/w/d" git commit -q -am "free"79ok push_as dami "$T/w/d" origin HEAD:main8081# A file with a mistake in it: the gate says where, and holds.82printf 'lib/ 3 from fox\n* 1 from\n* none\n' >"$T/w/d/.forge/approvals"83at "$T/w/d" git commit -q -am "broken rules"84ok push_as dami "$T/w/d" origin HEAD:main85echo e >>"$T/w/d/README.md"86at "$T/w/d" git commit -q -am "after the broken rules"87fails_with '\.forge/approvals:1: 3 approvals from 1 person can never be met' push_as dami "$T/w/d" origin HEAD:main88git --git-dir="$G" config forge.requireapproval false89ok push_as dami "$T/w/d" origin HEAD:main9091# @mechanical: a file Jev judged mechanical (an answer stored for its blobs).92printf '@mechanical none\n* 1\n' >"$T/w/d/.forge/approvals"93at "$T/w/d" git commit -q -am "mechanical rules"94ok push_as dami "$T/w/d" origin HEAD:main95git --git-dir="$G" config forge.requireapproval true96old=$(git -C "$T/w/d" rev-parse HEAD:lib/plain.sh)97echo f >>"$T/w/d/lib/plain.sh"98at "$T/w/d" git commit -q -am "a version bump"99new=$(git -C "$T/w/d" rev-parse HEAD:lib/plain.sh)100mkdir -p "$G/forge/jev/file.v1"101printf '{"model":"t","at":0,"answers":{"mechanical":{"noul":0.95}},"tokens":null}' >"$G/forge/jev/file.v1/$old.$new"102ok push_as dami "$T/w/d" origin HEAD:dami/bump103ok push_as dami "$T/w/d" origin HEAD:main