fix(cli): mops outdated exit codes, and report GitHub deps - #734
Merged
Conversation
`mops outdated` exited 0 whether or not anything was outdated, so it could not gate CI. It now exits 1 when updates are available and 2 when the check itself could not be completed — no mops.toml, unknown package, registry or GitHub lookup error — so a partial report is never mistaken for a clean bill of health. 1 for "found something" matches npm outdated and pnpm outdated. The exit code was only half the problem. `getAvailableUpdates` filtered to deps with a truthy version, which excludes GitHub deps, while `mops update` does update them by re-resolving the branch. So `outdated` could print "All dependencies are up to date!" for a project where `mops update` would rewrite a GitHub pin. Both now apply the same rule, so they cannot disagree. Also wires the `[pkg]` argument that `getAvailableUpdates` already accepted, matching `mops update [pkg]`, and skips the registry round trip entirely when a project has no registry dependencies. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
Cursor AI review👍 APPROVE — looks safe to merge Verified GitHub outdated logic against
VerdictDecision: APPROVE Generated for commit 2f45573 |
automation-sa-sre
approved these changes
Aug 12, 2026
automation-sa-sre
left a comment
There was a problem hiding this comment.
Automated approval: the AI review verdict for 2f45573 is APPROVE. See the "Cursor AI review" comment for details.
This was referenced Aug 12, 2026
Kamirus
added a commit
that referenced
this pull request
Aug 13, 2026
…oml (#742) Three consistency gaps between `mops update` and `mops outdated`, all of which make a scripted update quietly misbehave. ## `mops update` reported failure and exited 0 ``` $ mops update no-such-package Package "no-such-package" is not installed! Before: $? = 0 After: $? = 2 ``` A typo in a scripted update looked like success. It now exits `2` — the same code `mops outdated` uses for "the check could not be completed" ([#734](#734)) — for an unknown package and for a missing `mops.toml`. `1` is deliberately *not* reused: it means "updates are available", which cannot be a terminal state for a command whose job is to apply them. ## The two commands could disagree about GitHub deps #734 extracted `getAvailableGithubUpdates` precisely so `outdated` and `update` could not drift apart on what "out of date" means for a `repo = "..."` dependency — but `update` kept its own inline loop resolving branch heads, so the rule still existed twice. It now uses the helper. The helper needed no changes: it already carries the repo, branch, resolved head and dependency name, which is exactly enough to rebuild the pin URL. Behaviour is preserved deliberately and specifically: the same `add(url, { dev, lock: "skip" }, name)` call, `dev` still derived from where the dependency is declared, the alias key still passed through as the manifest key, and a failed lookup still prints and lets the rest of the run continue. Two visible nuances: lookups now run concurrently rather than sequentially (same request count against GitHub's 60/hour limit), and lookup errors print after the re-pins rather than interleaved with them. ## Neither `--help` nor the docs said it rewrites your manifest `mops update` is `cargo upgrade` semantics — it rewrites `mops.toml` — not `cargo update`, which only touches the lockfile. [#723](#723) records the decision to keep the name (under exact pins there is only one update operation that can exist, so there is no ambiguity to resolve, and renaming would be breaking). The agreed remedy was to say so plainly, which neither the help text nor the doc page did: ``` $ mops update --help Rewrite the versions in mops.toml to the highest semver-compatible ones within the caret bound (does not cross major versions, or pre-1.0 minor versions) Rewrites the new versions into mops.toml, and keeps mops.lock in sync. GitHub dependencies are re-pinned to their branch head (one GitHub API call each). Exit codes: 0 mops.toml is up to date 2 the update could not be run (no mops.toml, unknown [pkg]) ``` ## Follow-up worth flagging One divergence of the same family is left in place: `update` still exits `0` when a *GitHub lookup* fails — it prints `Failed to update <name>: …` and carries on — where `outdated` exits `2`. Changing it would alter behaviour beyond the scope of aligning the "not installed" case, so it is called out rather than folded in. Items 35, 36 and 37 in [#723](#723). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
mops outdatedexited0whether or not anything was outdated, so it could not gate CI.pnpm outdatedexits 1;cargo outdatedhas--exit-code.Exit codes are now
0up to date,1updates available,2the check could not be completed — missingmops.toml, unknown[pkg], or a registry/GitHub lookup failure.2wins when both apply, because a partial report must never read as a clean bill of health, and a failed lookup must never read as "outdated".1for "found something" and2for "failed to look" follows the grep/diff convention and matchesnpm outdated.The exit code was only half the problem
getAvailableUpdatesfiltered to dependencies with a truthyversion, which silently excludes GitHub (repo) dependencies — butmops updatedoes update them, by re-resolving the branch head and rewriting the pin. So the two commands disagreed about what "outdated" means:That's the defect that made the exit code useless: a green
mops outdateddidn't mean a subsequentmops updatewould be a no-op. Both commands now apply the same rule — same helper shape, same[pkg]filter, same "sha differs from branch head" comparison — so they can't drift apart again.A GitHub lookup that fails (rate limit, network) reports per-dependency and exits
2. It never degrades to a silent "up to date".Cost
One GitHub API call per
repodependency, and most projects have none. They're issued concurrently, unlikemops update's serial loop. Against that, the registry round trip is now skipped entirely when a project has no registry dependencies, so a GitHub-only project makes strictly fewer calls than before.No auth-token support and no
--no-githubescape hatch: neither npm nor cargo has an analogue, and inventing one before anyone hits the unauthenticated 60 req/h limit seemed premature.What is unchanged
mops update's behaviour is byte-for-byte identical.getAvailableUpdatesgained a fourth optional argument for opting into throwing instead ofprocess.exit(1), and onlyoutdatedpasses it, soupdatekeeps its existing print-and-exit path.getHighestSemverBatch'sassert(list.size() < 100)is untouched and unaffected —repodeps were never in that batch, and passing[pkg]only shrinks it. It remains a live trap for projects with 100+ direct dependencies, tracked as item 26 in #723.Follow-up worth flagging
mops update <not-installed-pkg>still exits0whilemops outdated <not-installed-pkg>now exits2. Worth aligningupdateto2. Andmops updatestill has its own inline GitHub resolution loop that duplicates the rule this PR extracted into a helper — a clean refactor now that the helper exists, but out of scope here since it would putupdate.tsin the diff.Part of the GA follow-up work from the Cargo/pnpm audit (#723, item 17), which the triage there marks as pre-GA because exit codes are a contract.
🤖 Generated with Claude Code