mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-03 15:27:56 +01:00
fix(capy): the install trailer says behind=<n> and its fix, so lanes that relay only the trailer report a stale install (field list 2 item 13, #2378)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
03968f0a7c
commit
ee24844773
6 files changed
+115
-11
No files matched your search
+11
-4
@@ -465,11 +465,15 @@ EOF_SHIM
|
||||
|
||||
# 4. Certify. The trailer is the contract, in one grammar:
|
||||
# gstack: ok <rev> for=<skill>[ (requested: …; pins: skipped (planning-only|no project); latest: <ver> available)]
|
||||
# gstack: ok <rev> for=<skill> ([requested: …; ][pins: …; ]behind=<n> (latest: <ver>); fix: <command>)
|
||||
# Notes, in that fixed order, ride only on an ok trailer: `requested` when the
|
||||
# existing checkout is not at --revision (it is never moved here), `pins` when
|
||||
# the doctor skipped the project pins, `latest` when the bootstrapper read a
|
||||
# newer VERSION from origin in this run, exported as GSTACK_CAPY_REMOTE_VERSION. The
|
||||
# failure head (`gstack: fail <ids>`) is unchanged.
|
||||
# the doctor skipped the project pins, then either `behind=<n> (latest: <ver>)`
|
||||
# and `fix: <command>` when the session ends behind origin's default branch
|
||||
# (bin/gstack-capy-session exports GSTACK_CAPY_BEHIND, _BEHIND_TO, _BEHIND_FIX), or
|
||||
# `latest` when the bootstrapper read a newer VERSION from origin in this run,
|
||||
# exported as GSTACK_CAPY_REMOTE_VERSION. The failure head (`gstack: fail <ids>`)
|
||||
# is unchanged.
|
||||
INSTALLED=$(head -1 "$ROOT/VERSION")
|
||||
TRAILER="$(printf '%s\n' "$OUT" | grep '^gstack: ' | tail -1)"
|
||||
NOTES=""
|
||||
@@ -482,7 +486,10 @@ if printf '%s\n' "$OUT" | grep -q '^SKIP pins (planning-only'; then note "pins:
|
||||
elif printf '%s\n' "$OUT" | grep -Eq '^SKIP pins \((no project|project directory )'; then note "pins: skipped (no project)"
|
||||
fi
|
||||
REMOTE_VERSION="${GSTACK_CAPY_REMOTE_VERSION:-}"
|
||||
if [ -n "$REMOTE_VERSION" ] && [ "$REMOTE_VERSION" != "$INSTALLED" ] \
|
||||
if [ -n "${GSTACK_CAPY_BEHIND:-}" ]; then
|
||||
note "behind=$GSTACK_CAPY_BEHIND (latest: ${GSTACK_CAPY_BEHIND_TO:-${REMOTE_VERSION:-unknown}})"
|
||||
note "fix: ${GSTACK_CAPY_BEHIND_FIX:-bash <skill>/scripts/install.sh}"
|
||||
elif [ -n "$REMOTE_VERSION" ] && [ "$REMOTE_VERSION" != "$INSTALLED" ] \
|
||||
&& [ "$(printf '%s\n%s\n' "$INSTALLED" "$REMOTE_VERSION" | sort -V | head -1)" = "$INSTALLED" ]; then
|
||||
note "latest: $REMOTE_VERSION available"
|
||||
fi
|
||||
|
||||
+27
-3
@@ -28,6 +28,12 @@
|
||||
# GSTACK_CAPY_AUTO_UPGRADE=0>)`.
|
||||
# --check never moves the checkout; behind origin it prints
|
||||
# `gstack-capy-session: behind origin/<branch>: <installed> -> <remote> (<n> commits); fix: ...`.
|
||||
# Whenever the session ends behind origin (--check, or a self-upgrade that was
|
||||
# skipped or disabled) it exports GSTACK_CAPY_BEHIND=<n>, GSTACK_CAPY_BEHIND_TO=<remote
|
||||
# VERSION> and GSTACK_CAPY_BEHIND_FIX=<command> for the installer's trailer notes
|
||||
# `behind=<n> (latest: <remote>); fix: <command>`. The command never contains
|
||||
# "; " (the note separator): after a refused self-upgrade it is bin/gstack-upgrade,
|
||||
# which prints its own refusal and fix.
|
||||
#
|
||||
# Usage: gstack-capy-session [--check] --root <checkout> [gstack-capy-install arguments]
|
||||
# --check certify only (bin/gstack-capy-install --check: the doctor with the
|
||||
@@ -65,18 +71,35 @@ behind() {
|
||||
git -C "$ROOT" rev-parse -q --verify FETCH_HEAD >/dev/null 2>&1 || return 1
|
||||
! git -C "$ROOT" merge-base --is-ancestor FETCH_HEAD HEAD 2>/dev/null
|
||||
}
|
||||
disabled() { echo "SELF_UPGRADE: disabled ($1)"; } # VAR — the switch that turned the fast-forward off
|
||||
disabled() { echo "SELF_UPGRADE: disabled ($1)"; behind && report_behind "unset $1, then $RERUN"; } # VAR — the switch that turned the fast-forward off
|
||||
# report_behind FIX — hand the installer the gap and the command that closes
|
||||
# it, for the trailer's `behind=<n>` note (lanes relay only the trailer).
|
||||
report_behind() {
|
||||
export GSTACK_CAPY_BEHIND="$(git -C "$ROOT" rev-list --count HEAD..FETCH_HEAD 2>/dev/null || echo '?')" \
|
||||
GSTACK_CAPY_BEHIND_TO="$(git -C "$ROOT" show FETCH_HEAD:VERSION 2>/dev/null | head -1)" GSTACK_CAPY_BEHIND_FIX="$1"
|
||||
}
|
||||
unset GSTACK_CAPY_BEHIND GSTACK_CAPY_BEHIND_TO GSTACK_CAPY_BEHIND_FIX
|
||||
RERUN="bash ${GSTACK_CAPY_WRAPPER_PATH:-<skill>/scripts/install.sh}${SKILL:+ --for $SKILL}"
|
||||
if [ "$CHECK" -eq 1 ]; then
|
||||
if behind; then
|
||||
# The rerun fast-forwards only what gstack-upgrade would move; otherwise
|
||||
# gstack-upgrade itself names the refusal and its fix.
|
||||
if [ "${GSTACK_CAPY_SELF_UPGRADE:-}" = off ]; then report_behind "unset GSTACK_CAPY_SELF_UPGRADE, then $RERUN"
|
||||
elif [ "${GSTACK_CAPY_AUTO_UPGRADE:-1}" = 0 ]; then report_behind "unset GSTACK_CAPY_AUTO_UPGRADE, then $RERUN"
|
||||
elif [ "$(git -C "$ROOT" symbolic-ref -q --short HEAD 2>/dev/null)" != "$(sed -n "1s/.*branch '\([^']*\)' of .*/\1/p" "$(git -C "$ROOT" rev-parse --absolute-git-dir 2>/dev/null)/FETCH_HEAD" 2>/dev/null)" ] \
|
||||
|| [ -n "$(git -C "$ROOT" status --porcelain --untracked-files=no 2>/dev/null)" ] \
|
||||
|| ! git -C "$ROOT" merge-base --is-ancestor HEAD FETCH_HEAD 2>/dev/null; then report_behind "$ROOT/bin/gstack-upgrade"
|
||||
else report_behind "$RERUN"
|
||||
fi
|
||||
BRANCH=$(git -C "$ROOT" symbolic-ref -q --short HEAD 2>/dev/null || echo HEAD)
|
||||
echo "gstack-capy-session: behind origin/$BRANCH: $(head -1 "$ROOT/VERSION" 2>/dev/null) -> $(git -C "$ROOT" show FETCH_HEAD:VERSION 2>/dev/null | head -1) ($(git -C "$ROOT" rev-list --count HEAD..FETCH_HEAD 2>/dev/null || echo '?') commits); fix: bash ${GSTACK_CAPY_WRAPPER_PATH:-<skill>/scripts/install.sh}${SKILL:+ --for $SKILL} (fast-forwards)"
|
||||
echo "gstack-capy-session: behind origin/$BRANCH: $(head -1 "$ROOT/VERSION" 2>/dev/null) -> $GSTACK_CAPY_BEHIND_TO ($GSTACK_CAPY_BEHIND commits); fix: $GSTACK_CAPY_BEHIND_FIX$([ "$GSTACK_CAPY_BEHIND_FIX" = "$RERUN" ] && echo ' (fast-forwards)')"
|
||||
fi
|
||||
elif [ "${GSTACK_CAPY_SELF_UPGRADED:-}" != 1 ]; then
|
||||
if [ "${GSTACK_CAPY_SELF_UPGRADE:-}" = off ]; then disabled GSTACK_CAPY_SELF_UPGRADE
|
||||
elif [ "${GSTACK_CAPY_AUTO_UPGRADE:-1}" = 0 ]; then disabled GSTACK_CAPY_AUTO_UPGRADE
|
||||
elif [ -z "${GSTACK_CAPY_REMOTE_MIN:-}" ] || [ "$GSTACK_CAPY_REMOTE_MIN" = unknown ]; then echo "SELF_UPGRADE: none (remote minimum unknown)"
|
||||
elif ! behind; then echo "SELF_UPGRADE: none (current)"
|
||||
elif [ ! -x "$ROOT/bin/gstack-upgrade" ]; then echo "SELF_UPGRADE: skipped ($ROOT/bin/gstack-upgrade is missing)"
|
||||
elif [ ! -x "$ROOT/bin/gstack-upgrade" ]; then echo "SELF_UPGRADE: skipped ($ROOT/bin/gstack-upgrade is missing)"; report_behind "$RERUN"
|
||||
else
|
||||
OLD=$(head -1 "$ROOT/VERSION" 2>/dev/null)
|
||||
UP_OUT=$("$ROOT/bin/gstack-upgrade" --ff-only-no-reinstall 2>&1); UP_RC=$?
|
||||
@@ -89,6 +112,7 @@ elif [ "${GSTACK_CAPY_SELF_UPGRADED:-}" != 1 ]; then
|
||||
REASON=$(printf '%s\n' "$UP_OUT" | sed -n 's/^gstack-upgrade: \(.*\); nothing was changed; fix: /\1; fix: /p' | tail -1)
|
||||
[ -n "$REASON" ] || REASON=$(printf '%s\n' "$UP_OUT" | sed -n 's/^gstack-upgrade: //p' | tail -1)
|
||||
echo "SELF_UPGRADE: skipped (${REASON:-gstack-upgrade exit $UP_RC})"
|
||||
report_behind "$ROOT/bin/gstack-upgrade"
|
||||
fi
|
||||
fi
|
||||
exec "$INSTALLER" "$@"
|
||||
@@ -10,7 +10,7 @@ description: Install and use gstack in Capy and Claude Code. Load at session sta
|
||||
Resolve `scripts/install.sh` relative to this SKILL.md, not the working repository. It is a frozen bootstrapper: it prints `gstack-capy-skill: wrapper <n> (<path>)` first, learns the supported minimum from gstack's remote, and hands over to the checkout's own wrapper (`bin/gstack-capy-session`).
|
||||
|
||||
1. At session start run `bash scripts/install.sh --check`, adding `--for <skill>` when you know the workflow (`--for autoplan`, `--for review`, `--for qa`). It prints one `PASS|FAIL|SKIP|WARN <component>` row per component (`WARN` is an owner action), a `GSTACK_LAUNCH:` line, and exactly one trailer as the last line: `gstack: ok <revision> for=<skill>[ (<notes>)]` (exit 0) or `gstack: fail <ids> …` (exit 1). Read the exit status and grep `^gstack: ` directly; never pipe the check through `tail`, `head` or `grep` alone. `--check` never moves the checkout.
|
||||
2. If the trailer names `revision`, `runtime` or `pins` (`gstack: fail revision …`, `gstack: fail runtime,pins`, `gstack: fail pins`) and a checkout exists, or the output carries a `gstack-capy-session: behind origin/<branch>: …` line, run the same command without `--check` (900-second timeout). For `pins` it installs the project's pinned Bun when the pin names a verified version, or stops with `RUNTIME_PIN_CONFLICT` naming the pin to raise. Any clean, unpinned checkout behind origin is fast-forwarded in install mode (below the `GSTACK_MIN_REVISION` floor by the bootstrapper, `gstack-capy-skill: upgraded <old> -> <new>`; above it by the checkout, `SELF_UPGRADE: done <old> -> <new>`), reinstalled and rechecked in one locked step. A pinned (`--revision`, detached), dirty or diverged checkout, or one another live session holds a lease on (`UPGRADE_BUSY`), is never moved: below the floor the trailer names the cause and the exact commands; above it the run prints `SELF_UPGRADE: skipped (<reason>)` and continues on the installed revision. `GSTACK_CAPY_AUTO_UPGRADE=0` disables every fast-forward, `GSTACK_CAPY_SELF_UPGRADE=off` only the above-floor one.
|
||||
2. If the trailer names `revision`, `runtime` or `pins` (`gstack: fail revision …`, `gstack: fail runtime,pins`, `gstack: fail pins`) and a checkout exists, or an ok trailer carries `behind=<n> (latest: <v>); fix: <command>` (the checkout is <n> commits behind origin; the output also has a `gstack-capy-session: behind origin/<branch>: …` line), run the same command without `--check` (900-second timeout), or the `fix:` command when it names another. Report the trailer verbatim, `behind=` included: a lane that is behind says so. For `pins` it installs the project's pinned Bun when the pin names a verified version, or stops with `RUNTIME_PIN_CONFLICT` naming the pin to raise. Any clean, unpinned checkout behind origin is fast-forwarded in install mode (below the `GSTACK_MIN_REVISION` floor by the bootstrapper, `gstack-capy-skill: upgraded <old> -> <new>`; above it by the checkout, `SELF_UPGRADE: done <old> -> <new>`), reinstalled and rechecked in one locked step. A pinned (`--revision`, detached), dirty or diverged checkout, or one another live session holds a lease on (`UPGRADE_BUSY`), is never moved: below the floor the trailer names the cause and the exact commands; above it the run prints `SELF_UPGRADE: skipped (<reason>)` and continues on the installed revision. `GSTACK_CAPY_AUTO_UPGRADE=0` disables every fast-forward, `GSTACK_CAPY_SELF_UPGRADE=off` only the above-floor one.
|
||||
3. If the trailer is `gstack: fail not-installed …` and this session runs no gstack workflow, record that line and continue; nothing is installed. Install right before the first workflow with `bash scripts/install.sh --for <skill>` (about 30 s cold: clone, a SHA-256-verified Bun when none installed fits, both host profiles without Chromium, and a `~/.local/bin/bun` shim so fresh shells get the recorded Bun).
|
||||
4. Any other `FAIL` row carries a `fix:` command and a reason code with a troubleshooting anchor; run the fix, then `--check` again. `SKIP codex-cli (optional; …)`, `SKIP browser (lazy; …)` and `SKIP pins (planning-only; …)` are not failures.
|
||||
|
||||
|
||||
+12
-3
@@ -49,7 +49,11 @@ git clone --single-branch --depth 1 https://github.com/garrytan/gstack.git ~/.lo
|
||||
`SELF_UPGRADE: disabled (<variable>)`. `--check` never moves the checkout;
|
||||
behind origin it prints `gstack-capy-session: behind origin/<branch>:
|
||||
<installed> -> <remote> (<n> commits); fix: bash <skill>/scripts/install.sh
|
||||
[--for <skill>] (fast-forwards)` and the agent runs that fix.
|
||||
[--for <skill>] (fast-forwards)` and the agent runs that fix. Whenever the
|
||||
session ends behind origin (`--check`, or a self-upgrade that was skipped or
|
||||
disabled), the ok trailer also says so, because lanes relay only the
|
||||
trailer: `behind=<n> (latest: <ver>); fix: <command>` (see the trailer
|
||||
grammar below).
|
||||
4. When the fetch fails (offline), the bootstrapper says `remote minimum
|
||||
unknown (fetch failed)` and checks the local install only; it never claims
|
||||
the minimum, and nothing is fast-forwarded.
|
||||
@@ -192,13 +196,18 @@ The last line is the trailer:
|
||||
gstack: ok 1.91.96.0 for=autoplan
|
||||
gstack: ok 1.91.96.0 for=review (requested: v1.91.90.0 (run <checkout>/bin/gstack-upgrade to move); latest: 1.91.101.0 available)
|
||||
gstack: ok 1.91.96.0 for=autoplan (pins: skipped (planning-only))
|
||||
gstack: ok 1.91.101.0 for=autoplan (behind=12 (latest: 1.91.107.0); fix: bash <skill>/scripts/install.sh --for autoplan)
|
||||
gstack: fail runtime,pins
|
||||
```
|
||||
|
||||
An ok trailer carries at most one parenthesised note group, `; `-separated,
|
||||
in this order: `requested: …` (an existing checkout at another revision than
|
||||
`--revision`), `pins: skipped (planning-only|no project)`, and `latest: <ver>
|
||||
available` (origin's default branch has a newer VERSION; the bootstrapper
|
||||
`--revision`), `pins: skipped (planning-only|no project)`, then either
|
||||
`behind=<n> (latest: <ver>)` followed by `fix: <command>` (the session ended
|
||||
<n> commits behind origin's default branch after a fetch that succeeded in this
|
||||
run; `bin/gstack-capy-session` exports `GSTACK_CAPY_BEHIND`, `_BEHIND_TO` and
|
||||
`_BEHIND_FIX`, so this works with every bootstrapper revision), or `latest:
|
||||
<ver> available` (origin's default branch has a newer VERSION; the bootstrapper
|
||||
exports it as `GSTACK_CAPY_REMOTE_VERSION` only after a fetch that succeeded
|
||||
in this run). A fail trailer never carries notes. Match `^gstack: ok ` or
|
||||
`^gstack: fail `; never parse past the first space after the version.
|
||||
|
||||
@@ -308,6 +308,60 @@ describe('capy skill bootstrapper: GSTACK_CAPY_REMOTE_VERSION', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// Field list 2 item 13: lanes relay only the trailer, so a session that ends
|
||||
// behind origin hands the installer the gap and the fix for its trailer note.
|
||||
describe('capy skill: the session hands the installer GSTACK_CAPY_BEHIND', () => {
|
||||
const ENV_INSTALLER = '#!/usr/bin/env bash\necho "gstack: ok $(cat "$(dirname "$0")/../VERSION") for=autoplan behind=${GSTACK_CAPY_BEHIND:-none} to=${GSTACK_CAPY_BEHIND_TO:-none} fix=${GSTACK_CAPY_BEHIND_FIX:-none}"\n';
|
||||
const rerun = `bash ${BOOT} --for autoplan`;
|
||||
|
||||
test('--check behind origin: the commit count, origin\'s VERSION and the fast-forwarding command; a checkout the rerun would not move points at gstack-upgrade', () => {
|
||||
const f = fixture({ cloneAt: 'B' });
|
||||
write(path.join(f.root, 'bin/gstack-capy-install'), ENV_INSTALLER, 0o755);
|
||||
f.git(['update-index', '--assume-unchanged', 'bin/gstack-capy-install']);
|
||||
const r = f.run(['--check', '--for', 'autoplan']);
|
||||
expect(r.status, r.stdout + r.stderr).toBe(0);
|
||||
expect(r.last).toBe(`gstack: ok 1.0.1 for=autoplan behind=1 to=1.0.2 fix=${rerun}`);
|
||||
expect(f.git(['rev-parse', 'HEAD'])).toBe(f.b);
|
||||
fs.writeFileSync(path.join(f.root, 'README.md'), 'local edit\n');
|
||||
expect(f.run(['--check', '--for', 'autoplan']).last).toBe(`gstack: ok 1.0.1 for=autoplan behind=1 to=1.0.2 fix=${f.root}/bin/gstack-upgrade`);
|
||||
f.git(['checkout', '-q', '--', 'README.md']);
|
||||
f.git(['checkout', '-q', '--detach']);
|
||||
expect(f.run(['--check', '--for', 'autoplan']).last).toBe(`gstack: ok 1.0.1 for=autoplan behind=1 to=1.0.2 fix=${f.root}/bin/gstack-upgrade`);
|
||||
f.git(['checkout', '-q', '-b', 'topic']);
|
||||
expect(f.run(['--check', '--for', 'autoplan']).last).toBe(`gstack: ok 1.0.1 for=autoplan behind=1 to=1.0.2 fix=${f.root}/bin/gstack-upgrade`);
|
||||
f.git(['checkout', '-q', 'main']);
|
||||
expect(f.run(['--check', '--for', 'autoplan']).stdout).toMatch(/^gstack-capy-session: behind origin\/main: 1\.0\.1 -> 1\.0\.2 \(1 commits\); fix: bash .+ --for autoplan \(fast-forwards\)$/m);
|
||||
expect(f.run(['--check', '--for', 'autoplan'], { GSTACK_CAPY_AUTO_UPGRADE: '0' }).last).toContain('fix=unset GSTACK_CAPY_AUTO_UPGRADE, then ');
|
||||
});
|
||||
|
||||
test('install mode: a skipped self-upgrade points at gstack-upgrade (which names its refusal); a disabled one names the variable to unset; no fix carries the "; " note separator', () => {
|
||||
const f = fixture({ cloneAt: 'B' });
|
||||
write(path.join(f.root, 'bin/gstack-capy-install'), ENV_INSTALLER, 0o755);
|
||||
const skipped = f.run(['--for', 'autoplan']);
|
||||
expect(skipped.stdout).toMatch(/^SELF_UPGRADE: skipped \(tracked files changed: bin\/gstack-capy-install; fix: .+\)$/m);
|
||||
expect(skipped.last).toBe(`gstack: ok 1.0.1 for=autoplan behind=1 to=1.0.2 fix=${f.root}/bin/gstack-upgrade`);
|
||||
const disabled = f.run(['--for', 'autoplan'], { GSTACK_CAPY_SELF_UPGRADE: 'off' });
|
||||
expect(disabled.last).toBe(`gstack: ok 1.0.1 for=autoplan behind=1 to=1.0.2 fix=unset GSTACK_CAPY_SELF_UPGRADE, then ${rerun}`);
|
||||
for (const r of [skipped, disabled]) expect(r.last.split(' fix=')[1]).not.toContain('; ');
|
||||
expect(f.git(['rev-parse', 'HEAD'])).toBe(f.b);
|
||||
});
|
||||
|
||||
test('current, or the fetch failed: nothing is exported, and values inherited from a parent session are dropped', () => {
|
||||
const f = fixture({ cloneAt: 'B' });
|
||||
f.git(['fetch', '-q', 'origin', 'main']);
|
||||
f.git(['merge', '--ff-only', '-q', 'FETCH_HEAD']);
|
||||
write(path.join(f.root, 'bin/gstack-capy-install'), ENV_INSTALLER, 0o755);
|
||||
const inherited = { GSTACK_CAPY_BEHIND: '9', GSTACK_CAPY_BEHIND_TO: '9.9.9', GSTACK_CAPY_BEHIND_FIX: 'stale' };
|
||||
expect(f.run(['--check', '--for', 'autoplan'], inherited).last).toBe('gstack: ok 1.0.2 for=autoplan behind=none to=none fix=none');
|
||||
f.git(['reset', '-q', '--hard', f.b]);
|
||||
write(path.join(f.root, 'bin/gstack-capy-install'), ENV_INSTALLER, 0o755);
|
||||
f.git(['remote', 'set-url', 'origin', `file://${f.host.base}/missing.git`]);
|
||||
const offline = f.run(['--check', '--for', 'autoplan'], inherited);
|
||||
expect(offline.stdout).toContain('remote minimum unknown (fetch failed)');
|
||||
expect(offline.last).toBe('gstack: ok 1.0.1 for=autoplan behind=none to=none fix=none');
|
||||
});
|
||||
});
|
||||
|
||||
describe('capy skill: vendored text and support file', () => {
|
||||
const support = fs.existsSync(path.join(REPO, 'capy/support.env')) ? fs.readFileSync(path.join(REPO, 'capy/support.env'), 'utf8') : '';
|
||||
test('the wrapper revision meets the checkout minimum; GSTACK_MIN_REVISION <= VERSION', () => {
|
||||
|
||||
@@ -269,6 +269,16 @@ describe('gstack-capy-install', () => {
|
||||
expect(lastLine(failed.stdout)).toBe('gstack: fail codex');
|
||||
});
|
||||
|
||||
test('trailer grammar: behind=<n> and its fix replace latest, after requested and pins; never on a fail trailer', () => {
|
||||
const f = fixture();
|
||||
const behind = { GSTACK_CAPY_BEHIND: '5', GSTACK_CAPY_BEHIND_TO: '1.2.8', GSTACK_CAPY_BEHIND_FIX: 'bash /s/install.sh --for autoplan', GSTACK_CAPY_REMOTE_VERSION: '1.2.8' };
|
||||
expect(lastLine(run(f, [], behind).stdout)).toBe('gstack: ok 1.2.3 for=autoplan (behind=5 (latest: 1.2.8); fix: bash /s/install.sh --for autoplan)');
|
||||
const all = run(f, ['--revision', 'v9.9.9.9'], { ...behind, STUB_PINS_SKIP: 'planning-only; repo pins differ: x' });
|
||||
expect(all.status, all.stdout + all.stderr).toBe(0);
|
||||
expect(lastLine(all.stdout)).toBe(`gstack: ok 1.2.3 for=autoplan (requested: v9.9.9.9 (run ${f.root}/bin/gstack-upgrade to move); pins: skipped (planning-only); behind=5 (latest: 1.2.8); fix: bash /s/install.sh --for autoplan)`);
|
||||
expect(lastLine(run(f, [], { ...behind, STUB_DOCTOR_FAIL: '1' }).stdout)).toBe('gstack: fail codex');
|
||||
});
|
||||
|
||||
test('--root clones when the directory is absent and refuses a non-gstack directory', () => {
|
||||
const f = fixture();
|
||||
const clone = path.join(f.base, 'clone');
|
||||
|
||||
Reference in new issue
Block a user