Repository navigation
fix(terminal): auto-provision Linux sandbox before running commands #2962
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,40 @@ const Terminal = { | |
| * @returns {Promise<boolean>} - Returns true if installation completes with exit code 0, void if not installing | ||
| */ | ||
| async startAxs(installing = false, logger = console.log, err_logger = console.error,failsafe = false) { | ||
| // Guard against callers (core terminal UI, or third-party plugins such as | ||
| // AI copilots that shell out to run `npm`/`node`) invoking startAxs before | ||
| // the Alpine sandbox has ever been provisioned on this device. Without this | ||
| // check, init-sandbox.sh does not exist yet and the caller sees a confusing | ||
| // "No such file or directory" + unknown node/npm versions instead of the | ||
| // sandbox being set up automatically. | ||
| if (!this._provisioning && !(await this.isInstalled())) { | ||
| if (!installing) { | ||
| const message = "Linux sandbox is not installed yet. Open Terminal once (or let the plugin install it) before running commands."; | ||
| err_logger(message); | ||
| throw new Error(message); | ||
| } | ||
|
|
||
| // install() finishes by calling startAxs(true, ...) itself to apply the | ||
| // final sandbox configuration, at a point where isInstalled() is still | ||
| // false (the .configured marker is written by init-sandbox.sh during | ||
| // that very call). The _provisioning flag prevents that internal call | ||
| // from re-entering install() and recursing forever. | ||
| logger("Linux sandbox not found, installing it first..."); | ||
| this._provisioning = true; | ||
| let installed; | ||
| try { | ||
| installed = await this.install(logger, err_logger); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The normal first-run UI calls Set the guard for the whole |
||
| } finally { | ||
| this._provisioning = false; | ||
| } | ||
| if (!installed) { | ||
| const message = this.lastInstallError || "Failed to install the Linux sandbox required to run this command."; | ||
| err_logger(message); | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| const filesDir = await new Promise((resolve, reject) => { | ||
| system.getFilesDir(resolve, reject); | ||
| }); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two concurrent
startAxs(true)calls can both read_provisioningas false before awaitingisInstalled(). Both then callinstall(). Each install deletes the same sandbox before extracting it, so one can remove files the other needs and make setup fail.Store and share a pending installation promise before the first await, and keep the internal configuration call separate from outside callers.