Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ gentle-shell upgrade --rollback

<br>

- **Main channel:** On the main channel it rebuilds Gentle AI and Gentle Shell from the latest `main` commits (only what changed) and needs pnpm and Go (a Go older than the minimum is replaced, for the build only, by the pinned Go the installer left in its own folder); switching back to release restores the pinned Gentle AI binary.
- **Main channel:** On the main channel it rebuilds Gentle AI and Gentle Shell from the latest `main` commits (only what changed) and needs pnpm and Go (a Go older than the minimum is replaced, for the build only, by the pinned Go the installer left in its own folder); switching back to release restores the pinned Gentle AI binary. When the Gentle AI build fails but Gentle Shell moved, the new Gentle Shell is still installed and the command exits with an error asking you to run `gentle-shell upgrade --channel main` again. The retry rebuilds Gentle AI with the new Shell's code, and finishes once the cause of the original error is resolved.
- **Your Pi:** With pnpm 11, the updated Gentle Shell may run a newer Pi that pnpm puts next to it (always at least the minimum); your own Pi is never changed.
- **Bundled install:** A bundled install (the web installer's own Node, pnpm and Pi) follows the release channel only: it installs the new version beside the current one, verifies it, then switches, keeping the previous version for `--rollback`. On release, once the latest release publishes the bundled distribution, `upgrade` moves a global pnpm or npm installation to the bundled one and asks once before removing the old global `gentle-pi`.
- **`update` vs `upgrade`:** `gentle-shell update` is a different command: it is Pi's own package update. In a bundled install Pi ships pinned with Gentle Shell, so updating Pi itself runs `gentle-shell upgrade` instead.
Expand Down
24 changes: 22 additions & 2 deletions docs/install-wizard.md
Original file line number Diff line number Diff line change
Expand Up @@ -816,8 +816,28 @@ publishes main builds, so main is built on this computer
a version it cannot check) it builds with the pinned Go under
`<config home>/tools/go`, downloading and verifying it there with the same
`acquireGo` as the wizard when no earlier run published it, and only when
Gentle AI is rebuilt. A failed download stops with `main-requires-tools`
before anything is built; the user's Go is never run for the build.
Gentle AI is rebuilt. A failed download stops the Gentle AI build with
`main-requires-tools` before it starts (a main Shell that moved is still
installed, as below); the user's Go is never run for the build.
- When the Gentle AI step of `gentle-shell upgrade --channel main` fails (the
build, its verification, or getting the pinned Go) and the main Shell moved,
the upgrade still packs and installs that Shell, records no channel, prints
`Updated Gentle Shell to the latest main (<sha12>); Gentle AI was not updated.`
and exits 1 with the original error (code and message) followed by
`; Gentle Shell was updated to main <sha12> but Gentle AI was not: run
gentle-shell upgrade --channel main to finish`. The retry then rebuilds Gentle
AI with the new Shell's code, so a fix in the main-channel upgrade path reaches
an install whose own copy of it is broken; a cause the new Shell does not fix
(a Go download that keeps failing, for example) still has to be resolved first. Until then Gentle AI stays the
previously registered main build or the pinned binary: a rejected build is
never registered. When the Shell install fails as well, its error is reported
with `; Gentle AI was not updated either (<Gentle AI error>)`. An unexpected
error that has no main-channel code (a filesystem `EACCES`, for example) is
reported as `main-upgrade-incomplete` with its own message and the same
suffix, so neither the retry advice nor either error is lost. When the Shell
is already current, a failed Gentle AI step fails as before, with nothing
installed or recorded. The wizard's runner is the one bundled with the
downloaded installer and does not get this behavior from a newer main.
- Trust: Gentle AI source is verified by Go's checksum database; the Gentle Shell
source is fetched over TLS from GitHub by exact commit SHA, with no pinned
digest. Main builds are development builds.
Expand Down
45 changes: 41 additions & 4 deletions scripts/main-channel.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,21 @@ async function mainGo({ userGo, pinnedGo, acquire, run }) {
}
}

/** A thrown value's message when it has one (an Error or not), otherwise the value as text. */
function describe(error) {
return typeof error?.message === "string" ? error.message : String(error);
}

/** `error` with `detail` appended to its message, keeping its code and cause; any
* other error becomes `main-upgrade-incomplete` with its message and itself as cause. */
function withDetail(error, detail) {
if (!(error instanceof MainChannelError)) {
return new MainChannelError("main-upgrade-incomplete", `${describe(error)}; ${detail}`, error);
}
const message = error.message.slice(`${error.code}: `.length);
return new MainChannelError(error.code, `${message}; ${detail}`, error.cause);
}

/**
* Updates Gentle Shell along its recorded channel (or the one `--channel` selects):
* release goes to the latest npm release, main to the latest `main` commits of
Expand Down Expand Up @@ -478,11 +493,33 @@ export async function runUpgrade({ args, ctx, platform, arch = process.arch, pac
out(`gentle-shell is already at the latest main: ${summary}.`);
return 0;
}
if (!aiCurrent) await buildMainGentleAi({ commit: gentleAiCommit, ctx, platform, goPath: await mainGo({ userGo, pinnedGo, acquire, run }), run, fs });
// Fail forward (#2356): a Gentle AI step that fails still installs a main Shell that
// moved, so the retry runs the new Shell's code; the channel is recorded only when
// both steps succeed, so that retry rebuilds Gentle AI. Verification stays fail-closed.
// A flag, not the caught value: a rejection may be any value, even a falsy one.
let aiFailed = false;
let aiError;
if (!aiCurrent) {
try {
await buildMainGentleAi({ commit: gentleAiCommit, ctx, platform, goPath: await mainGo({ userGo, pinnedGo, acquire, run }), run, fs });
} catch (error) {
if (shellCurrent) throw error;
aiFailed = true;
aiError = error;
}
}
if (!shellCurrent) {
const manager = await ownerManager({ ctx, platform, packageRoot, adapters });
const tgz = await packMainShell({ commit: shellCommit, ctx, fetch, run, pnpm, fs, platform });
await installGlobal(manager, tgz, run);
try {
const manager = await ownerManager({ ctx, platform, packageRoot, adapters });
const tgz = await packMainShell({ commit: shellCommit, ctx, fetch, run, pnpm, fs, platform });
await installGlobal(manager, tgz, run);
} catch (error) {
throw aiFailed ? withDetail(error, `Gentle AI was not updated either (${describe(aiError)})`) : error;
}
}
if (aiFailed) {
out(`Updated Gentle Shell to the latest main (${shellCommit.slice(0, 12)}); Gentle AI was not updated.`);
throw withDetail(aiError, `Gentle Shell was updated to main ${shellCommit.slice(0, 12)} but Gentle AI was not: run gentle-shell upgrade --channel main to finish`);
}
await writeChannel(ctx, { channel: "main", shellCommit, gentleAiCommit }, fs);
out(`Updated to the latest main: ${summary}.`);
Expand Down
Loading
Loading