Skip to content

Let install.py resume a failed first install - #716

Merged
SaladDay merged 1 commit into
mainfrom
let-install-py-resume-a-failed-first-install
Oct 15, 2026
Merged

SaladDay merged 1 commit into
mainfrom
let-install-py-resume-a-failed-first-install

Conversation

@SaladDay

@SaladDay SaladDay commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

What

install.py (the offline bundle's installer, deploy/install-source.py):

  • A failed first install can be rerun. A new installation is assembled in a .staging-* directory inside the installation directory (node payload, .env, oac, both Compose files). Once docker compose config accepts it, the files move into place, with compose.yaml last. If the run fails before that, the stage is removed, and so is the directory when this run created it. A stage left by a killed run is removed by the next run, the same way upgrade stages already are. A non-empty directory without compose.yaml is still refused (Incomplete installation: preserve … and choose another directory).
  • No tracebacks. DistributionError and PayloadError exit with their message. A failed command prints <step> failed; see the output above plus what happened to the installation, not the CalledProcessError argument tuple. Ctrl-C prints Interrupted; rerun the same command to continue. Other OSErrors print <reason>: <path> without [Errno N]. An invalid --host is now an argparse error.
  • Permission errors name one path and the command to fix it: <path> is not writable by this account; if an earlier run used sudo, return it with: sudo chown -R "$(id -un)" <path>. A path inside the installation is widened to the installation directory. A path that could not be created names its parent, e.g. a root-owned ~/.oac.
  • Same-release resume failures say Data volume check failed; no service was changed. See the output above. (or Configuration check failed; …), matching oac apply's wording in the operations troubleshooting table. Only real upgrades say Upgrade stopped; the running services are unchanged. <step> failed; see the output above., and that keeps the phrase operations.md quotes.
  • --bundle-dir is removed. It had no caller. The bundle modules are now imported from the script's own directory, so their error classes can be caught by name, and a separate bundle directory would mix code and data from two bundles.
  • Docs: docs/development.md (EN + zh, source_hash refreshed) drops the "remove that incomplete directory and retry" advice. deploy/README.md describes how the bundle installer stages a new installation.

Why

Installer polish audit (audit-core.md):

  • Finding 8: a fresh failure left .env and node-payload/, and the rerun refused the directory as incomplete. Bundle errors printed tracebacks, and failed commands printed the raw CalledProcessError.
  • Finding 4 (Python part) and probes-mx: a root-owned ~/.oac/<dir>.lock from an earlier sudo run gave [Errno 13] Permission denied.
  • P2-n: a same-release resume refusal said "Upgrade stopped".

All of these were reproduced on main before the change with a real bundle layout (install.py beside distribution.py/node_payload.py) against Docker.

Verification

  • python3 deploy/test_install_source.py: 7 tests pass. New or extended tests cover: a config failure leaves no directory and the rerun succeeds, also after a killed run's stage; a same-release resume failure message; the upgrade failure message; a bundle checksum error, Ctrl-C (directory removed) and a denied lock, each exiting with one line.
  • Manual run of the real bundle layout against Docker 29 / Compose 2.40, with a docker wrapper that refuses up/run, so no container or volume was created. Checked: broken template → one-line message and nothing left; the rerun with a valid template passes config in the stage and commits (modes 0600/0700/0755, the bind points at the final node-payload, docker compose config OK in the installation); resume → Data volume check failed; no service was changed…; SIGINT → message and nothing left; checksum error → one line; denied lock, unwritable parent and unreadable file in the installation → chown messages naming the lock, the parent and the installation directory.
  • node --test website/tests/translations.test.mjs (website deps installed): 3/3 pass.
  • python3 scripts/check-names.py: passed.

Skipped

  • Full make check and make check-distribution (focused checks only). The rest of the website suite needs pnpm build and was not run; this change adds no links.
  • No real up with the bundle images. The start path and its arguments are unchanged.

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@SaladDay
SaladDay merged commit a52679d into main Oct 15, 2026
14 checks passed
@SaladDay
SaladDay deleted the let-install-py-resume-a-failed-first-install branch October 15, 2026 10:32
A new installation is now assembled in a .staging-* directory inside the
installation directory and moved into place after docker compose config
accepts it, compose.yaml last. A failure before then removes the stage,
and the directory when the run created it, so the same command can be
rerun; the next run removes a stage a killed run left. A non-empty
directory without compose.yaml is still refused.

Failures exit with one line instead of a traceback: distribution and
payload errors print their message, a failed command names its step and
points at the output above, Ctrl-C asks to rerun, and permission errors
name the path with the sudo chown command that returns it. A same-release
resume check no longer says "Upgrade stopped".

The bundle modules are imported from the script's own directory, so the
unused --bundle-dir flag is removed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant