mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-01 14:34:42 +01:00
fix(install): fresh-install log lines name the clone and every host installed (A10)
gstack-capy-install prints `cloned <repo> at <sha> (<version>)` for a checkout it cloned, or one the bootstrapper (revision 2, GSTACK_CAPY_CLONED) cloned in this run, instead of `reusing`. setup's final line is `gstack ready (<hosts>)` from the same rows as its summary table, including the --refresh-registered upgrade summary. Files: bin/gstack-capy-install, bin/gstack-capy-session, capy/skill/gstack/scripts/install.sh, setup, test/gstack-capy-install.test.ts, test/host-conformance.test.ts Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
9c4635b221
commit
d008757e87
6 files changed
+27
-1
No files matched your search
@@ -91,6 +91,9 @@ elif [ ! -f "$ROOT/setup" ] || [ ! -f "$ROOT/VERSION" ]; then
|
||||
else
|
||||
git clone --quiet --single-branch --depth 1 "$REPO" "$ROOT" || stop INSTALL_PREFLIGHT_FAILED "could not clone $REPO" "check network access to github.com, then re-run"
|
||||
fi
|
||||
say "cloned $REPO at $(git -C "$ROOT" rev-parse --short=12 HEAD 2>/dev/null) ($(head -1 "$ROOT/VERSION"))"
|
||||
elif [ -n "${GSTACK_CAPY_CLONED:-}" ]; then
|
||||
say "cloned $GSTACK_CAPY_CLONED at $(git -C "$ROOT" rev-parse --short=12 HEAD 2>/dev/null) ($(head -1 "$ROOT/VERSION"))"
|
||||
else
|
||||
say "reusing $ROOT ($(head -1 "$ROOT/VERSION")); never reset or pulled"
|
||||
fi
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
# GSTACK_CAPY_REMOTE_VERSION origin's VERSION read in this run (wrapper 2+; the
|
||||
# installer's `latest: <ver> available` trailer note)
|
||||
# GSTACK_CAPY_UPGRADED_FROM the version it fast-forwarded from (setup is never skipped then)
|
||||
# GSTACK_CAPY_CLONED the repo it just cloned (wrapper 2+; the installer's `cloned` line)
|
||||
# GSTACK_CAPY_LOCK_HELD=1 install mode: .gstack-capy-install.lock is held on an
|
||||
# inherited descriptor; nothing here takes it again
|
||||
# Because this file moves with the checkout, session-start behavior can change
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
# 4. exec the checkout's full wrapper, bin/gstack-capy-session, with the same
|
||||
# arguments, handing the held lock over (GSTACK_CAPY_LOCK_HELD=1) and,
|
||||
# from revision 2, origin's VERSION read in this run
|
||||
# (GSTACK_CAPY_REMOTE_VERSION, the trailer's `latest:` note).
|
||||
# (GSTACK_CAPY_REMOTE_VERSION, the trailer's `latest:` note) and the repo
|
||||
# it just cloned (GSTACK_CAPY_CLONED, the installer's `cloned` line).
|
||||
# Everything else (Bun, setup, the doctor, the verdict, the launch line) is
|
||||
# the checkout's. The last line is the `gstack:` trailer; exit 0 only on
|
||||
# `gstack: ok`. Contract: docs/capy-install.md; tests:
|
||||
@@ -30,6 +31,7 @@ repo="${GSTACK_CAPY_REPO:-https://github.com/garrytan/gstack.git}"
|
||||
echo "gstack-capy-skill: wrapper $GSTACK_CAPY_WRAPPER ($self)"
|
||||
|
||||
check=0 skill="" revision="" min_note="" remote_min="" remote_version=""
|
||||
unset GSTACK_CAPY_CLONED
|
||||
args=()
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
@@ -83,6 +85,7 @@ if [ ! -f "$root/setup" ]; then
|
||||
fi
|
||||
remote_min=$(support_get "$(cat "$root/capy/support.env" 2>/dev/null)" GSTACK_MIN_REVISION)
|
||||
remote_version=$(head -1 "$root/VERSION" 2>/dev/null)
|
||||
export GSTACK_CAPY_CLONED="$repo"
|
||||
else
|
||||
# The remote's rules: fetch the default branch into FETCH_HEAD (never --depth
|
||||
# into an existing checkout), read capy/support.env from it.
|
||||
|
||||
@@ -440,6 +440,15 @@ _repair_crlf_checkout() {
|
||||
}
|
||||
[ -n "${GSTACK_SETUP_REFRESH_CHILD:-}" ] || _repair_crlf_checkout
|
||||
|
||||
# _setup_ready_line — the final `gstack ready (<host>, ...)` line from summary
|
||||
# rows on stdin (host<TAB>...<TAB>result in field 7): every host installed,
|
||||
# updated or unchanged, in table order; nothing when no host row exists.
|
||||
_setup_ready_line() {
|
||||
local hosts
|
||||
hosts=$(awk -F'\t' '$1 != "state" && $7 ~ /^(installed|updated|unchanged)$/ && !seen[$1]++ { printf "%s%s", (n++ ? ", " : ""), $1 }')
|
||||
[ -z "$hosts" ] || log "gstack ready ($hosts)"
|
||||
}
|
||||
|
||||
# ─── --refresh-registered: refresh exactly the registered installs (#1925) ───
|
||||
# What /gstack-upgrade and the team-mode auto-update run. Each install this
|
||||
# checkout registered is refreshed by running setup for that one host, in its
|
||||
@@ -553,6 +562,7 @@ EOF
|
||||
if [ "$_rf_disabled" != " " ]; then
|
||||
echo " disabled skills (not registered on any host):$_rf_disabled"
|
||||
fi
|
||||
_setup_ready_line < "$_RF_SUMMARY"
|
||||
rm -f "$_RF_SUMMARY"
|
||||
exit "$_RF_FAILED"
|
||||
fi
|
||||
@@ -4766,3 +4776,6 @@ if [ -n "${_HOOK_REFUSED:-}" ]; then
|
||||
echo "gstack setup: everything else is installed, but these hooks were not registered because they do not parse: $_HOOK_REFUSED(first: $_HOOK_REFUSED_AT). This is a gstack bug; report $_HOOK_REFUSED_AT. https://github.com/garrytan/gstack/blob/main/docs/troubleshooting.md#setup-hook-does-not-parse" >&2
|
||||
exit 1
|
||||
fi
|
||||
# The final line names every host this run installed, from the same rows as
|
||||
# the summary table; instruction-only hosts write no row and print none.
|
||||
printf '%s' "$_SETUP_ROWS" | _setup_ready_line
|
||||
@@ -264,6 +264,10 @@ describe('gstack-capy-install', () => {
|
||||
const r = run(f, ['--root', clone], { GSTACK_CAPY_REPO: f.root });
|
||||
expect(r.status, r.stdout + r.stderr).toBe(0);
|
||||
expect(r.stdout).toContain(`cloning ${f.root} into ${clone}`);
|
||||
expect(r.stdout).toMatch(new RegExp(`^gstack-capy-install: cloned ${f.root} at [0-9a-f]{12} \\(1\\.2\\.3\\)$`, 'm'));
|
||||
const handed = run(f, [], { GSTACK_CAPY_CLONED: 'https://github.com/garrytan/gstack.git' });
|
||||
expect(handed.stdout).toMatch(/^gstack-capy-install: cloned https:\/\/github\.com\/garrytan\/gstack\.git at [0-9a-f]{12} \(1\.2\.3\)$/m);
|
||||
expect(handed.stdout).not.toContain('reusing');
|
||||
expect(fs.existsSync(path.join(clone, 'setup'))).toBe(true);
|
||||
const occupied = path.join(f.base, 'occupied');
|
||||
write(path.join(occupied, 'README.md'), 'x');
|
||||
|
||||
@@ -129,6 +129,7 @@ describe.skipIf(process.platform === 'win32')('host conformance kit', () => {
|
||||
const rows = registryRows(f);
|
||||
expect(rows.map(row => [row[0], row[1], row[3], row[5], row[6]])).toEqual([[host, 'global', dest, realpathSync(src), readFileSync(join(src, 'VERSION'), 'utf8').trim()]]);
|
||||
expect(r.stdout + r.stderr).toMatch(new RegExp(`Install summary:[\\s\\S]*\\b${host}\\s+\\S+\\s+global\\s+installed`));
|
||||
expect(r.stdout.trim().split('\n').at(-1)).toBe(`gstack ready (${host})`);
|
||||
|
||||
const status = runSetup(f, join(src, 'setup'), ['--status']);
|
||||
expect(status.status).toBe(0);
|
||||
@@ -231,6 +232,7 @@ describe.skipIf(process.platform === 'win32')('host conformance kit', () => {
|
||||
expect(upgrade.status, upgrade.stdout + upgrade.stderr).toBe(0);
|
||||
expect(upgrade.stdout).toContain(`Refreshing registered gstack installs from source: ${realpathSync(src)}`);
|
||||
expect(upgrade.stdout).toMatch(/Upgrade summary:\n[\s\S]*claude\s+full\s+global\s+unchanged\s+1\.91\.14\.0[\s\S]*codex\s+experimental\s+global\s+installed\s+1\.91\.14\.0/);
|
||||
expect(upgrade.stdout.trim().split('\n').at(-1)).toBe('gstack ready (claude, codex)');
|
||||
expect(registryRows(f).map(r => [r[0], r[6]]).sort()).toEqual([['claude', '1.91.14.0'], ['codex', '1.91.14.0']]);
|
||||
// The refreshed Codex install runs the new checkout's render.
|
||||
expect(realpathSync(join(f.home, '.codex/skills/gstack-review/SKILL.md'))).toBe(join(realpathSync(src), '.agents/skills/gstack-review/SKILL.md'));
|
||||
|
||||
Reference in new issue
Block a user