igneum/tools/ci/gh-account-check.sh
igneum-labs 3d805febb4 Every gate gh call reads Igneum's own gh directory, never the founder's (main's rule, 8 October 2026, 10:0x UK): tools/ci/gh-env.sh sets GH_CONFIG_DIR=~/.config/gh-igneum for the gate, the hook, the merge tool and the CI-state reader
At 09:46 UK the founder's gh had his other account active (his own work) and tools/ci/gh-account-check.sh refused every Igneum push from the Mac (the v5 lane's 56a50160 held local). The check now reads Igneum's directory: empty, it refuses naming the one step (the founder or main stores the Igneum token there with `GH_CONFIG_DIR=~/.config/gh-igneum gh auth login --with-token`; no lane does); another login, refused and named; the stored entry, passes; while tools/ci/github-suspended stands it skips with a line, because no gh call can succeed and the hook already refuses GitHub pushes, so the lanes land on the mirror meanwhile. Known-failed first: an empty directory, another login, the founder's directory never read (the fake gh records the directory it was given), the marker's skip, no gh. IGNEUM_GH_CONFIG_DIR overrides the path for the self-tests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-08 08:51:55 +00:00

72 lines
7 KiB
Bash
Executable file

#!/usr/bin/env bash
# gh's ACTIVE account on this Mac is the stored Igneum entry and nothing else (main's rule, 7 October 2026, 21:5x UK: at 21:41 a lane
# switched gh to the second owner's login, which belongs to other projects and must never touch Igneum; nobody could say which lane).
# The stored entry's name is in ~/.config/igneum/gh-user (the login's pre-rename spelling until a re-login; never in the repository).
# Runs before a push (tools/ci/pre-push.sh --hook) and before a landing (tools/ci/merge-to-master.sh); refuses with the line otherwise.
# A machine without gh, or without the stored-name file, is not this Mac: skip with a line (CI runners, the boxes).
#
# tools/ci/gh-account-check.sh # exit 0 when gh's active account is the stored entry (or gh / the file is absent, with a skip line); exit 1 with the line otherwise
# tools/ci/gh-account-check.sh --self-test # a fake gh whose active account is another login is refused and named; the stored one passes;
# # a status with no active account is refused; no gh on PATH skips
set -uo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
. "$HERE/gh-env.sh" # every gh call below reads Igneum's own configuration directory, never the founder's (8 October 2026, 09:46 UK: the founder's gh had his other account active and every Igneum push met this check)
STORED_FILE="${IGNEUM_GH_USER_FILE:-$HOME/.config/igneum/gh-user}"
SUSPENDED_FILE="${IGNEUM_GITHUB_SUSPENDED_FILE:-$HERE/github-suspended}"
active_account() { # from `gh auth status`: the account whose block carries "Active account: true"
gh auth status 2>&1 | awk '
/account [^ ]+ \(/ { for (i = 1; i <= NF; i++) if ($i == "account") { acct = $(i + 1) } }
/Active account: true/ { print acct; exit }'
}
check() {
local stored active
command -v gh >/dev/null 2>&1 || { echo "gh-account: skipped, no gh on this machine (the rule binds the Mac that pushes)"; return 0; }
[ -s "$STORED_FILE" ] || { echo "gh-account: skipped, no stored-name file at $STORED_FILE (not the pushing Mac)"; return 0; }
stored="$(tr -d '[:space:]' < "$STORED_FILE")"
# while GitHub is unreachable (tools/ci/github-suspended) no gh call can succeed and no push reaches GitHub (the hook refuses them), so the
# account question is moot: skip with the line. `gh auth login --with-token` cannot fill the Igneum directory until the suspension lifts.
if [ -f "$SUSPENDED_FILE" ]; then echo "gh-account: skipped, GitHub is unreachable ($(grep -E '^suspended-since' "$SUSPENDED_FILE" | head -1)); pushes go to the box mirror; the Igneum gh directory $GH_CONFIG_DIR is filled when GitHub answers again (the one step: GH_CONFIG_DIR=$GH_CONFIG_DIR gh auth login --with-token, by the founder or main)"; return 0; fi
active="$(active_account)"
if [ -z "$active" ]; then echo "gh-account: REFUSED. Igneum's gh directory $GH_CONFIG_DIR holds no active account. The one step, for the founder or main, never a lane: GH_CONFIG_DIR=$GH_CONFIG_DIR gh auth login --with-token < <the Igneum login's token>. The founder's own gh directory is never read or switched by a lane." >&2; return 1; fi
if [ "$active" != "$stored" ]; then echo "gh-account: REFUSED. gh's active account is $active, not the stored Igneum entry; that login must never touch Igneum (rule of 7 October 2026). Run: gh auth switch --user $stored" >&2; return 1; fi
echo "gh-account: gh's active account is the stored Igneum entry"
}
if [ "${1:-}" = --self-test ]; then
d="$(mktemp -d)"; trap 'rm -rf "$d"' EXIT
printf 'stored-login\n' > "$d/gh-user"
fake="$d/bin/gh"; mkdir -p "$d/bin"
cat > "$fake" <<'FAKE'
#!/usr/bin/env bash
# fake gh: the status text from $GH_CONFIG_DIR (a file "active" there names the active account; no file = none); the dir it read is recorded
echo "$GH_CONFIG_DIR" > "${FAKE_SEEN:-/dev/null}"
FAKE_ACTIVE="$(cat "$GH_CONFIG_DIR/active" 2>/dev/null || true)"
printf 'github.com\n'
printf ' X Failed to log in to github.com account stored-login (keyring)\n - Active account: %s\n - Token: gho_x\n\n' "$([ "${FAKE_ACTIVE:-}" = stored-login ] && echo true || echo false)"
printf ' %s Logged in to github.com account other-login (keyring)\n - Active account: %s\n - Token: gho_y\n' "$([ "${FAKE_ACTIVE:-}" = other-login ] && echo '✓' || echo '✓')" "$([ "${FAKE_ACTIVE:-}" = other-login ] && echo true || echo false)"
FAKE
chmod +x "$fake"; fails=0
ig="$d/gh-igneum"; founder="$d/gh-founder"; mkdir -p "$ig" "$founder"; echo other-login > "$founder/active" # the founder's dir has his other account active
common=(IGNEUM_GH_USER_FILE="$d/gh-user" IGNEUM_GH_CONFIG_DIR="$ig" IGNEUM_GITHUB_SUSPENDED_FILE="$d/no-marker" FAKE_SEEN="$d/seen" HOME="$d")
# an empty Igneum directory: refused, the line names the one step and the directory; the founder's directory is never read
out="$(PATH="$d/bin:$PATH" env "${common[@]}" bash "$0" 2>&1)" && { echo "self-test failed: an empty Igneum gh directory was not refused"; fails=1; }
case "$out" in *"REFUSED. Igneum's gh directory $ig holds no active account"*"gh auth login --with-token"*) ;; *) echo "self-test failed: the refusal did not name the step and the directory: $out"; fails=1 ;; esac
[ "$(cat "$d/seen")" = "$ig" ] || { echo "self-test failed: gh read $(cat "$d/seen"), not the Igneum directory"; fails=1; }
# the Igneum directory holding another login: refused and named; holding the stored entry: passes
echo other-login > "$ig/active"
out="$(PATH="$d/bin:$PATH" env "${common[@]}" bash "$0" 2>&1)" && { echo "self-test failed: another active login was not refused"; fails=1; }
case "$out" in *"REFUSED. gh's active account is other-login"*) ;; *) echo "self-test failed: the refusal did not name the active login: $out"; fails=1 ;; esac
echo stored-login > "$ig/active"
PATH="$d/bin:$PATH" env "${common[@]}" bash "$0" >/dev/null 2>&1 || { echo "self-test failed: the stored active login was refused"; fails=1; }
# the suspension marker: skipped with the line, whatever the directory holds
rm -f "$ig/active"; printf 'suspended-since: 2026-10-07T17:02:00Z\n' > "$d/marker"
out="$(PATH="$d/bin:$PATH" env "${common[@]}" IGNEUM_GITHUB_SUSPENDED_FILE="$d/marker" bash "$0" 2>&1)" || { echo "self-test failed: the check did not skip under the suspension marker"; fails=1; }
case "$out" in *"skipped, GitHub is unreachable"*) ;; *) echo "self-test failed: no skip line under the marker: $out"; fails=1 ;; esac
# a machine without gh: the system binaries on PATH, no gh
out="$(PATH="/usr/bin:/bin" env "${common[@]}" bash "$0" 2>&1)" || { echo "self-test failed: a machine without gh did not skip"; fails=1; }
case "$out" in *"skipped, no gh"*) ;; *) echo "self-test failed: no skip line without gh: $out"; fails=1 ;; esac
[ "$fails" = 0 ] && echo "self-test passed: gh reads Igneum's own directory and never the founder's; an empty Igneum directory is refused naming the one step; another active login is refused and named; the stored one passes; the suspension marker skips with its line; a machine without gh skips with its line"
exit $fails
fi
check