mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-09 14:38:59 +02:00
fix(gbrain-source-wireup): first sync targets the registered source, not --repo
The wireup registered a federated source by id, then ran 'gbrain sync --repo $WORKTREE' — which resolves against the brain's DEFAULT source and (on gbrain 0.46.x) rewrites that source's local_path anchor to our worktree. Net effect: the user's primary knowledge source silently repointed at the gstack brain worktree while the just-registered source got zero pages, and pages_synced still reported success. The sync now targets the registered id ('gbrain sync --source $id', the same form the repo's own troubleshooting documents). Because the script's stated floor is gbrain >= 0.18.0 and nothing proves --source exists there, support is probed via 'gbrain sync --help' first: an older gbrain keeps the wrong-but-working --repo call with an upgrade warning instead of converting it into a hard failure. The probe sits after the GSTACK_BRAIN_NO_SYNC early-exit and is unreachable in --probe mode.
Regression tests (fail on v1.68.3.0): a no-skip sync case asserting the call log shows 'sync --source gstack-brain-<id>' and never 'sync --repo', and an old-gbrain fallback case (fake sync --help without --source) asserting --repo plus the upgrade warning.
Fixes #2662
This commit is contained in:
@@ -332,8 +332,21 @@ do_wireup() {
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# #2662: `sync --repo <path>` resolves against the brain's DEFAULT source and
|
||||
# can silently repoint that source's local_path anchor at our worktree while
|
||||
# the source registered above gets nothing. Target the registered source by
|
||||
# id. `--source` support is probed first (the documented floor is gbrain >=
|
||||
# 0.18.0 and nothing proves the flag exists there): an older gbrain keeps the
|
||||
# wrong-but-working --repo call with an upgrade warning, never a hard failure.
|
||||
local sync_out sync_redacted
|
||||
sync_out=$(gbrain sync --repo "$WORKTREE" 2>&1) || {
|
||||
local -a sync_cmd
|
||||
if gbrain sync --help 2>/dev/null | grep -q -- '--source'; then
|
||||
sync_cmd=(gbrain sync --source "$id")
|
||||
else
|
||||
warn "this gbrain's sync lacks --source; falling back to 'sync --repo' (upgrade gbrain so the sync targets source $id directly — #2662)"
|
||||
sync_cmd=(gbrain sync --repo "$WORKTREE")
|
||||
fi
|
||||
sync_out=$("${sync_cmd[@]}" 2>&1) || {
|
||||
# Redact any postgres:// URLs from the error message in case gbrain logged
|
||||
# a connection error containing the full DSN with password. The user sees
|
||||
# "***REDACTED***" instead of credentials in their stderr or any log.
|
||||
|
||||
@@ -42,10 +42,12 @@ let gbrainStateFile: string;
|
||||
function makeFakeGbrain(opts: {
|
||||
version?: string | null; // null = "binary missing" (don't write the file)
|
||||
syncFails?: boolean;
|
||||
syncHelpNoSource?: boolean; // simulate an older gbrain whose sync lacks --source
|
||||
}) {
|
||||
const version = opts.version ?? '0.18.2';
|
||||
if (version === null) return; // simulate missing binary by NOT writing one
|
||||
const syncFails = opts.syncFails ?? false;
|
||||
const syncHelpNoSource = opts.syncHelpNoSource ?? false;
|
||||
|
||||
// Stub gbrain reads/writes state from a JSON file. Fields:
|
||||
// sources: [{id, local_path, federated}]
|
||||
@@ -109,6 +111,13 @@ json.dump(state, open('$STATE','w'), indent=2)
|
||||
fi
|
||||
|
||||
# sync --repo <p> → records, optionally fails
|
||||
# sync --help → advertise flags (the wireup probes this before choosing the
|
||||
# sync form; the default fake mirrors a current gbrain, which HAS --source)
|
||||
if [ "$1" = "sync" ] && [ "$2" = "--help" ]; then
|
||||
echo "Usage: gbrain sync [--repo <path>]${syncHelpNoSource ? '' : ' [--source <id>]'}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$1" = "sync" ]; then
|
||||
${syncFails ? 'echo "sync failed: connection error" >&2; exit 1' : 'echo "1 page imported"; exit 0'}
|
||||
fi
|
||||
@@ -193,6 +202,31 @@ describe('gstack-gbrain-source-wireup — wireup mode', () => {
|
||||
expect(state.sources[0].federated).toBe(true);
|
||||
});
|
||||
|
||||
test('the real sync targets the REGISTERED source, never --repo (#2662)', () => {
|
||||
// `sync --repo <path>` resolves against the brain's DEFAULT source and can
|
||||
// silently repoint its local_path anchor at our worktree. This case runs
|
||||
// WITHOUT GSTACK_BRAIN_NO_SYNC — the skip-mode cases never reach the sync,
|
||||
// so asserting the sync argv there would be vacuous.
|
||||
setupGstackRepo('git@github.com:user/gstack-brain-user.git');
|
||||
makeFakeGbrain({});
|
||||
const r = run([]);
|
||||
expect(r.status).toBe(0);
|
||||
const calls = gbrainCalls();
|
||||
expect(calls.some((c) => c.startsWith('gbrain sync --source gstack-brain-user'))).toBe(true);
|
||||
expect(calls.some((c) => c.includes('sync --repo'))).toBe(false);
|
||||
});
|
||||
|
||||
test('older gbrain without sync --source: falls back to --repo with an upgrade warning', () => {
|
||||
setupGstackRepo('git@github.com:user/gstack-brain-user.git');
|
||||
makeFakeGbrain({ syncHelpNoSource: true });
|
||||
const r = run([]);
|
||||
expect(r.status).toBe(0);
|
||||
const calls = gbrainCalls();
|
||||
expect(calls.some((c) => c.startsWith('gbrain sync --repo'))).toBe(true);
|
||||
expect(calls.some((c) => c.includes('sync --source '))).toBe(false);
|
||||
expect(r.stderr).toContain('#2662');
|
||||
});
|
||||
|
||||
test('idempotent re-run after success: no new sources add call', () => {
|
||||
setupGstackRepo('git@github.com:user/gstack-brain-user.git');
|
||||
makeFakeGbrain({});
|
||||
|
||||
Reference in New Issue
Block a user