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>
This commit is contained in:
parent
f37691e983
commit
dd954178b8
6 changed files with 42 additions and 10 deletions
|
|
@ -9,7 +9,7 @@
|
|||
| a box or network check gets one retry (`retry-once.sh`) | Nothing by itself: wraps the box-locks check, the scene parity check and the live public API check so a first failure is printed and retried once; the second is the verdict. The checks keep their own skip line on a runner without the resource. | 7 October 2026 |
|
||||
| the red watcher fires on cancelled and timed-out runs too (`ci-red.yml`, `red-watch.mjs`) | The watcher's `if` missing any of failure, cancelled, timed_out, or the conclusion not handed to the record step (the self-test reads the workflow file); the line names the kind: CI red, CI cancelled, CI timed out. | 7 October 2026 |
|
||||
|
||||
| gh's active account is the stored Igneum entry (`gh-account-check.sh`) | A push or a landing from this Mac while `gh auth status` names any other account as active (or none). RULE: no lane switches gh accounts on this Mac, ever; the second owner's login belongs to other projects and must never touch Igneum; the stored entry's name is in ~/.config/igneum/gh-user, never in the repository. | 7 October 2026, 21:41 UK: a lane switched gh to the other login during the suspension; nobody could say which |
|
||||
| gh's active account is the stored Igneum entry (`gh-account-check.sh`, in Igneum's own gh directory `~/.config/gh-igneum` through `gh-env.sh`, never the founder's) | A push or a landing from this Mac while Igneum's gh directory names any other account as active, or none (the refusal names 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); skipped with a line while `github-suspended` stands. RULE: no lane switches gh accounts on this Mac, ever; the second owner's login belongs to other projects and must never touch Igneum; the stored entry's name is in ~/.config/igneum/gh-user, never in the repository. | 7 October 2026, 21:41 UK: a lane switched gh to the other login during the suspension; nobody could say which |
|
||||
|
||||
| kill by exact command or pid file (owed as a check) | 6 October 2026, 21:09Z: a Mac-side `pkill -f <log file name>` matched nothing (the log name was a redirect, not part of the command line), the roll-everything script lived on and wiped a box it had been told to hold. Rule: a job is stopped by its pid file (`tools/fleet/fleet-bg.sh start|stop <name>`) or by a pattern anchored on its exact command line (`^python3 -u /root/fleet/in/box-prover.py`), never by a word that may or may not appear in it. The check that flags a `pkill -f`/`pgrep -f` whose literal is a path or a name that never starts a command line is owed to the CI lane |
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,9 @@ const FIELDS = 'databaseId,status,conclusion,headSha,url,createdAt,event';
|
|||
const RED = new Set(['failure', 'cancelled', 'timed_out', 'startup_failure', 'action_required']);
|
||||
|
||||
function gh(args) {
|
||||
const env = { ...process.env, PATH: `${process.env.PATH || ''}:/opt/homebrew/bin:/usr/local/bin` };
|
||||
// Igneum's own gh configuration directory, never the founder's (tools/ci/gh-env.sh; 8 October 2026)
|
||||
const ghDir = process.env.IGNEUM_GH_CONFIG_DIR || path.join(os.homedir(), '.config', 'gh-igneum');
|
||||
const env = { ...process.env, GH_CONFIG_DIR: ghDir, PATH: `${process.env.PATH || ''}:/opt/homebrew/bin:/usr/local/bin` };
|
||||
const r = spawnSync('gh', args, { encoding: 'utf8', env, timeout: 60000 });
|
||||
if (r.error) throw new Error(`gh: ${r.error.message}`);
|
||||
if (r.status !== 0) throw new Error(`gh ${args.slice(0, 2).join(' ')}: exit ${r.status}: ${(r.stderr || '').trim().slice(0, 200)}`);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@
|
|||
# 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 '
|
||||
|
|
@ -21,8 +24,11 @@ check() {
|
|||
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. gh has no active account (gh auth status); the Igneum rule: the stored entry, and only it, is active on this Mac: gh auth switch --user $stored" >&2; return 1; fi
|
||||
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"
|
||||
}
|
||||
|
|
@ -33,20 +39,34 @@ if [ "${1:-}" = --self-test ]; then
|
|||
fake="$d/bin/gh"; mkdir -p "$d/bin"
|
||||
cat > "$fake" <<'FAKE'
|
||||
#!/usr/bin/env bash
|
||||
# fake gh: the status text, with the active account named by $FAKE_ACTIVE (empty = none active)
|
||||
# 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
|
||||
out="$(PATH="$d/bin:$PATH" IGNEUM_GH_USER_FILE="$d/gh-user" FAKE_ACTIVE=other-login bash "$0" 2>&1)" && { echo "self-test failed: another active login was not refused"; fails=1; }
|
||||
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
|
||||
PATH="$d/bin:$PATH" IGNEUM_GH_USER_FILE="$d/gh-user" FAKE_ACTIVE=stored-login bash "$0" >/dev/null 2>&1 || { echo "self-test failed: the stored active login was refused"; fails=1; }
|
||||
PATH="$d/bin:$PATH" IGNEUM_GH_USER_FILE="$d/gh-user" FAKE_ACTIVE= bash "$0" >/dev/null 2>&1 && { echo "self-test failed: no active account was let through"; fails=1; }
|
||||
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" IGNEUM_GH_USER_FILE="$d/gh-user" bash "$0" 2>&1)" || { echo "self-test failed: a machine without gh did not skip"; fails=1; }
|
||||
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: another active login is refused and named, the stored one passes, no active account is refused, a machine without gh skips with its line"
|
||||
[ "$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
|
||||
|
|
|
|||
8
tools/ci/gh-env.sh
Executable file
8
tools/ci/gh-env.sh
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
# Igneum's own gh configuration directory (main's rule, 8 October 2026, 10:0x UK): every gh call the gate, the pre-push hook, the merge
|
||||
# tool and the CI-state reader make runs with GH_CONFIG_DIR set to an Igneum-only directory, so the founder's own gh (which he switches
|
||||
# between his accounts as he works) is never read and never switched by a lane. The directory holds the Igneum login's token and nothing
|
||||
# else; storing that token there is the one step no lane takes (the founder or main: GH_CONFIG_DIR=~/.config/gh-igneum gh auth login
|
||||
# --with-token < <the Igneum token>). Source this file; IGNEUM_GH_CONFIG_DIR overrides the path (self-tests).
|
||||
export GH_CONFIG_DIR="${IGNEUM_GH_CONFIG_DIR:-$HOME/.config/gh-igneum}"
|
||||
mkdir -p "$GH_CONFIG_DIR" 2>/dev/null || true
|
||||
|
|
@ -19,6 +19,7 @@
|
|||
# unless --fixes-master, none pushes the branch, pending waits then goes
|
||||
set -euo pipefail
|
||||
ROOT=$(git rev-parse --show-toplevel); cd "$ROOT"
|
||||
. tools/ci/gh-env.sh # every gh call here reads Igneum's own gh directory, never the founder's (8 October 2026)
|
||||
BRANCH="$(git rev-parse --abbrev-ref HEAD)"; TRIES=6; CI_WAIT_MIN="${CI_WAIT_MIN:-25}"; FIXES_MASTER=0; SELF_TEST=0; REMOTE="${MERGE_REMOTE:-origin}"
|
||||
while [ $# -gt 0 ]; do case "$1" in --tries) TRIES="$2"; shift 2 ;; --ci-wait) CI_WAIT_MIN="$2"; shift 2 ;; --fixes-master) FIXES_MASTER=1; shift ;; --remote) REMOTE="$2"; shift 2 ;; --self-test) SELF_TEST=1; shift ;; -*) echo "unknown option $1" >&2; exit 2 ;; *) BRANCH="$1"; shift ;; esac; done
|
||||
# --remote <name>: land on another remote's master (a box mirror, build@<box>:/srv/igneum.git, while GitHub is unreachable; main's
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ cd "$(git rev-parse --show-toplevel)" || exit 1
|
|||
# and fire this hook again inside the fixture: the first master push through the gate died that way (6 October 2026).
|
||||
unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE GIT_PREFIX GIT_COMMON_DIR GIT_OBJECT_DIRECTORY GIT_ALTERNATE_OBJECT_DIRECTORIES GIT_QUARANTINE_PATH GIT_PUSH_OPTION_COUNT
|
||||
MODE="${1:-local}"; MODE="${MODE#--}"
|
||||
GATE_ROOT="$(pwd -P)" # the readers below are called from fixture repositories in the self-test, so by absolute path
|
||||
GATE_ROOT="$(pwd -P)"
|
||||
. "$GATE_ROOT/tools/ci/gh-env.sh" # every gh call under the gate reads Igneum's own gh directory, never the founder's (8 October 2026) # the readers below are called from fixture repositories in the self-test, so by absolute path
|
||||
RED=0; N=0; LOG="$(mktemp)"; trap 'rm -rf "$LOG" "${SITE_TMP:-}"' EXIT
|
||||
T0=$(date +%s)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue