fix: stop --tag main from failing during npm pack (#125949)

* fix: refuse unsupported OpenClaw source package updates

* refactor: narrow main update refusal
This commit is contained in:
Peter Steinberger
2026-08-18 12:22:45 -07:00
committed by GitHub
parent 3116923ff7
commit bb92dc61c8
7 changed files with 55 additions and 31 deletions
+10 -11
View File
@@ -26,7 +26,6 @@ openclaw update --channel extended-stable
openclaw update --channel beta
openclaw update --channel dev
openclaw update --tag beta
openclaw update --tag main
openclaw update --dry-run
openclaw update --no-restart
openclaw update --yes
@@ -40,16 +39,16 @@ launcher scripts).
## Options
| Flag | Description |
| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--no-restart` | Skip restarting the Gateway service after a successful update. Package-manager updates that do restart verify the restarted service reports the expected version before the command succeeds. |
| `--channel <stable\|extended-stable\|beta\|dev>` | Set the update channel and persist it after core update success. Extended-stable is package-only. |
| `--tag <dist-tag\|version\|spec>` | Override the package target for this update only. It cannot be combined with an effective `extended-stable` channel, whose verified exact target is mandatory. For other package installs, `main` maps to `github:openclaw/openclaw#main`; GitHub/git source specs are packed into a temporary tarball before the staged global npm install. |
| `--dry-run` | Preview planned actions (channel/tag/target/restart flow) without writing config, installing, syncing plugins, or restarting. |
| `--json` | Print machine-readable `UpdateRunResult` JSON. Includes `postUpdate.plugins.warnings` when a managed plugin needs repair, beta-channel plugin fallback details, and `postUpdate.plugins.integrityDrifts` when npm plugin artifact drift is detected during post-update sync. |
| `--timeout <seconds>` | Per-step timeout. Default `1800`. |
| `--yes` | Skip confirmation prompts (for example downgrade confirmation). |
| `--acknowledge-clawhub-risk` | Allow post-update plugin sync to continue past community ClawHub trust warnings without an interactive prompt. Without it, risky community releases are skipped and left unchanged when OpenClaw cannot prompt. Official ClawHub packages and bundled plugin sources bypass this prompt. |
| Flag | Description |
| ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--no-restart` | Skip restarting the Gateway service after a successful update. Package-manager updates that do restart verify the restarted service reports the expected version before the command succeeds. |
| `--channel <stable\|extended-stable\|beta\|dev>` | Set the update channel and persist it after core update success. Extended-stable is package-only. |
| `--tag <dist-tag\|version\|spec>` | Override the package target for this update only. It cannot be combined with an effective `extended-stable` channel, whose verified exact target is mandatory. Package installs reject the `main` shorthand; use `--channel dev` for the supported checkout and build flow. Other explicit package specs keep their package-manager behavior. |
| `--dry-run` | Preview planned actions (channel/tag/target/restart flow) without writing config, installing, syncing plugins, or restarting. |
| `--json` | Print machine-readable `UpdateRunResult` JSON. Includes `postUpdate.plugins.warnings` when a managed plugin needs repair, beta-channel plugin fallback details, and `postUpdate.plugins.integrityDrifts` when npm plugin artifact drift is detected during post-update sync. |
| `--timeout <seconds>` | Per-step timeout. Default `1800`. |
| `--yes` | Skip confirmation prompts (for example downgrade confirmation). |
| `--acknowledge-clawhub-risk` | Allow post-update plugin sync to continue past community ClawHub trust warnings without an interactive prompt. Without it, risky community releases are skipped and left unchanged when OpenClaw cannot prompt. Official ClawHub packages and bundled plugin sources bypass this prompt. |
There is no `--verbose` flag. Use `--dry-run` to preview planned actions,
`--json` for machine-readable results, and `openclaw update status --json`
+2 -6
View File
@@ -75,8 +75,6 @@ openclaw update --channel dev
# Install a specific npm package spec
openclaw update --tag openclaw@2026.4.1-beta.1
# Install from GitHub main once without persisting the channel
openclaw update --tag main
```
Notes:
@@ -84,13 +82,11 @@ Notes:
- `--tag` applies to **package (npm) installs only**; git installs ignore it.
- The tag is not persisted; the next `openclaw update` uses the configured
channel.
- `--tag main` maps to the npm-compatible spec `github:openclaw/openclaw#main`
for that one run. For a persistent moving `main` install, use
- The `--tag main` shorthand is rejected for package installs because the
workspace checkout is not a self-contained package artifact. Use
`openclaw update --channel dev` (package installs switch to a git checkout)
or reinstall with the installer's git method:
`curl -fsSL https://openclaw.ai/install.sh | bash -s -- --install-method git --version main`.
The npm install path rejects GitHub/git source targets outright and points
you at the git method instead.
- Downgrade protection: if the target version is older than the current
version, OpenClaw prompts for confirmation (skip with `--yes`).
- Extended-stable always uses its verified exact package target. It is not a
+5 -3
View File
@@ -60,9 +60,11 @@ OpenClaw cannot safely distinguish an old automatic pin from a user pin; run
`openclaw plugins update @openclaw/name` once on the extended-stable channel
to opt that plugin back into exact-core tracking.
`--channel dev` gives a persistent moving GitHub `main` checkout. For a one-off
package update, `--tag main` maps to the `github:openclaw/openclaw#main` package
spec and installs it directly through the target package manager (npm/pnpm/bun).
`--channel dev` gives a persistent moving GitHub `main` checkout. Package
installs reject the `--tag main` shorthand because the workspace checkout is
not a self-contained package artifact. Use `openclaw update --channel dev` to
switch to the supported checkout and build flow. Other explicit package specs
keep their package-manager behavior.
For managed plugins, a missing beta release is a warning, not a failure: the
core update can still succeed while a plugin falls back to its recorded
+27 -6
View File
@@ -4355,12 +4355,6 @@ describe("update-cli", () => {
packageSpec: undefined,
expectedSpec: "openclaw@9999.0.0",
},
{
name: "main shorthand",
options: { yes: true, tag: "main" },
packageSpec: undefined,
expectedSpec: "github:openclaw/openclaw#main",
},
{
name: "explicit git package spec",
options: { yes: true, tag: "github:openclaw/openclaw#main" },
@@ -4429,6 +4423,33 @@ describe("update-cli", () => {
},
);
it.each([
{ name: "real run", options: { yes: true, json: true, tag: "main" } },
{ name: "normalized alias", options: { yes: true, json: true, tag: "openclaw@main" } },
{
name: "dry-run",
options: { dryRun: true, json: true, tag: "main", yes: true },
},
] as const)("refuses --tag main before package resolution: $name", async ({ options }) => {
mockPackageInstallStatus(createCaseDir("openclaw-update-main-refusal"));
await updateCommand(options);
const result = lastWriteJsonCall() as UpdateRunResult | undefined;
expect(result).toMatchObject({
status: "error",
reason: "unsupported-package-target",
steps: [],
});
expect(packageInstallCommandCall()).toBeUndefined();
expectNoSideEffects(resolveGlobalManager, replaceConfigFile, runGatewayUpdate);
if ("dryRun" in options && options.dryRun) {
expect(cleanupStaleManagedServiceUpdateHandoffs).not.toHaveBeenCalled();
}
expect(defaultRuntime.exit).toHaveBeenCalledWith(1);
expect(getErrorOutput()).toContain("openclaw update --channel dev");
});
it("fails package updates when the installed correction version does not match the requested target", async () => {
const tempDir = createCaseDir("openclaw-update");
const nodeModules = path.join(tempDir, "node_modules");
+1 -2
View File
@@ -165,7 +165,6 @@ export function registerUpdateCli(program: Command) {
["openclaw update --channel beta", "Switch to beta channel (git + npm)"],
["openclaw update --channel dev", "Switch to dev channel (git + npm)"],
["openclaw update --tag beta", "One-off update to a dist-tag or version"],
["openclaw update --tag main", "One-off package update from GitHub main"],
["openclaw update --dry-run", "Preview actions without changing anything"],
["openclaw update --no-restart", "Update without restarting the service"],
["openclaw update --json", "Output result as JSON"],
@@ -187,7 +186,7 @@ ${theme.heading("Switch channels:")}
- Use --channel stable|extended-stable|beta|dev to persist the update channel in config
- Run openclaw update status to see the active channel and source
- Use --tag <dist-tag|version|spec> for a one-off package update without persisting
- Use --tag main for a one-off package update from GitHub main
- Use --channel dev for the moving GitHub main checkout; package installs reject --tag main
${theme.heading("Non-interactive:")}
- Use --yes to accept downgrade prompts
@@ -106,7 +106,8 @@ export async function reportPreMutationUpdateFailure(params: {
reason:
| ExtendedStableFailureReason
| typeof EXTENDED_STABLE_TAG_UNSUPPORTED_REASON
| "npm lifecycle policy preflight";
| "npm lifecycle policy preflight"
| "unsupported-package-target";
message?: string;
opts: UpdateCommandOptions;
controlPlaneUpdateSentinelMeta: ControlPlaneUpdateSentinelMetaFile["meta"] | null;
+8 -2
View File
@@ -289,11 +289,17 @@ async function updateCommandInternal(
}
const explicitTag = normalizeTag(opts.tag);
if (channel === "extended-stable" && explicitTag) {
const unsupportedMainTag = updateInstallKind === "package" && explicitTag === "main";
if ((channel === "extended-stable" && explicitTag) || unsupportedMainTag) {
await reportPreMutationUpdateFailure({
root,
installKind: updateInstallKind,
reason: EXTENDED_STABLE_TAG_UNSUPPORTED_REASON,
reason: unsupportedMainTag
? "unsupported-package-target"
: EXTENDED_STABLE_TAG_UNSUPPORTED_REASON,
message: unsupportedMainTag
? "`--tag main` cannot update a package install. Run `openclaw update --channel dev` to switch to the supported Git checkout and build flow."
: undefined,
opts,
controlPlaneUpdateSentinelMeta,
});