mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-27 22:15:33 +00:00
* feat(self-upgrade): decision/cache/snooze foundation + atomic binary self-update Pure decideSelfUpgrade (invocation + autopilot channels), atomic untrusted cache + escalating snooze + shared marker grammar (forged-marker rejection), semver helpers, and real darwin-arm64/linux-x64 binary self-update (download -> fsync -> smoke -> atomic rename; failure leaves old binary intact). Tests incl. real-HTTP-server swap E2E. * feat(self-upgrade): check-update cache/markers, self-upgrade command, CLI heartbeat hook check-update gains gstack-style cache/snooze/markers + refreshUpdateCache + exported fetchLatestRelease. New 'gbrain self-upgrade' command. cli.ts emits the update marker on every invocation (cache-read-only hot path, detached single-flight refresh, skip-set + recursion guard + NODE_ENV=test gate). * feat(self-upgrade): autopilot silent channel, doctor check, runPostUpgrade setup, config + identity marker autopilot opt-in silent channel (auto+quiet+idle, swap-only+breadcrumb+exit-relaunch) + installSystemd Restart=always + migrateSystemdUnitToRestartAlways. doctor self_upgrade_health. runPostUpgrade applySelfUpgradeSetup (one-time consent + systemd rewrite). init defaults mode=notify. config self_upgrade plane + KNOWN_CONFIG_KEYS. get_brain_identity carries update marker. * docs(self-upgrade): gbrain-upgrade agent skill, RESOLVER/manifest, auto-update doc reversal, HEARTBEAT New skills/gbrain-upgrade agent flow (mirror gstack-upgrade) wired into RESOLVER + manifest. upgrades-auto-update.md reversed to document opt-in auto + conservative gates. HEARTBEAT self-upgrade --check-only line. llms-full regenerated. * chore: bump version and changelog (v0.42.12.0) Self-upgrading gbrain: invocation-riding update marker + opt-in autopilot silent channel + real atomic binary self-update. Mirrors gstack's mechanism. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(self-upgrade): write just-upgraded-from breadcrumb + clear stale cache after upgrade Codex ship-review P3: the CLI startup hook reads just-upgraded-from to print the one-time JUST_UPGRADED confirmation, but nothing wrote it — dead path. runUpgrade now writes the breadcrumb (covers full + --swap-only) and clears the update-check cache + snooze so a now-applied 'upgrade available' marker stops nudging. * feat(self-upgrade): surface what's-new in notify + wire agent integration (AGENTS.md, HEARTBEAT) + e2e - self-upgrade --check-only --json now includes changelog_diff + release_url (export fetchChangelog); the gbrain-upgrade skill shows 3-5 what's-new bullets before the 4-option prompt instead of just version numbers. - setup injects a self-upgrade marker protocol into AGENTS.md so interactive agents (Claude Code, Codex) act on the UPGRADE_AVAILABLE stderr marker — the piece that makes notify actually fire for them. - HEARTBEAT daily beat routes through the gbrain-upgrade skill (OpenClaw/Hermes cron cadence); auto-mode daemons ride the autopilot tick. - e2e: real subprocess invocation proves the marker fires (notify emits; off/snooze/up-to-date silent; JUST_UPGRADED fires+clears; --quiet suppresses). Serial test: --check-only surfaces the changelog. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
70 lines
2.7 KiB
TypeScript
70 lines
2.7 KiB
TypeScript
import { describe, expect, test } from 'bun:test';
|
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { withEnv } from './helpers/with-env.ts';
|
|
import { checkSelfUpgradeHealth } from '../src/commands/doctor.ts';
|
|
import { writeUpdateCache } from '../src/core/self-upgrade.ts';
|
|
import { logSelfUpgrade } from '../src/core/audit/self-upgrade-audit.ts';
|
|
|
|
async function withHome<T>(fn: (home: string) => T | Promise<T>): Promise<T> {
|
|
const dir = mkdtempSync(join(tmpdir(), 'gbrain-doctor-su-'));
|
|
try {
|
|
return await withEnv({ GBRAIN_HOME: dir, GBRAIN_AUDIT_DIR: join(dir, 'audit'), GBRAIN_SELF_UPGRADE_MODE: undefined }, () => fn(dir));
|
|
} finally {
|
|
rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
describe('checkSelfUpgradeHealth', () => {
|
|
test('mode=off → ok, names disabled', async () => {
|
|
await withEnv({ GBRAIN_SELF_UPGRADE_MODE: 'off' }, () => {
|
|
const c = checkSelfUpgradeHealth();
|
|
expect(c.name).toBe('self_upgrade_health');
|
|
expect(c.status).toBe('ok');
|
|
expect(c.message).toContain('disabled');
|
|
});
|
|
});
|
|
|
|
test('fresh install (no cache) → ok, mode=notify', async () => {
|
|
await withHome(() => {
|
|
const c = checkSelfUpgradeHealth();
|
|
expect(c.status).toBe('ok');
|
|
expect(c.message).toContain('mode=notify');
|
|
});
|
|
});
|
|
|
|
test('pending upgrade in cache → ok, surfaces it', async () => {
|
|
await withHome(() => {
|
|
writeUpdateCache({ kind: 'upgrade_available', current: '0.42.0', latest: '0.99.0' });
|
|
const c = checkSelfUpgradeHealth();
|
|
expect(c.status).toBe('ok');
|
|
expect(c.message).toContain('update available');
|
|
expect(c.message).toContain('0.99.0');
|
|
});
|
|
});
|
|
|
|
test('recent failed auto-upgrade → warn with hint', async () => {
|
|
await withHome(() => {
|
|
logSelfUpgrade({ channel: 'autopilot', action: 'apply', current: '0.42.0', latest: '0.99.0', outcome: 'failed', error: 'boom' });
|
|
const c = checkSelfUpgradeHealth();
|
|
expect(c.status).toBe('warn');
|
|
expect(c.message).toContain('self-upgrade failure');
|
|
expect(c.message).toContain('gbrain self-upgrade');
|
|
});
|
|
});
|
|
|
|
test('known-bad versions in config are surfaced', async () => {
|
|
await withHome((home) => {
|
|
mkdirSync(join(home, '.gbrain'), { recursive: true });
|
|
writeFileSync(
|
|
join(home, '.gbrain', 'config.json'),
|
|
JSON.stringify({ engine: 'pglite', self_upgrade: { mode: 'notify', failed_versions: ['0.50.0'] } }),
|
|
);
|
|
const c = checkSelfUpgradeHealth();
|
|
expect(c.message).toContain('known-bad');
|
|
expect(c.message).toContain('0.50.0');
|
|
});
|
|
});
|
|
});
|