Skip to content

perf(update): resolve in parallel, refresh only changed packages, single recompute - #3055

Open
mikeland73 wants to merge 5 commits into
mainfrom
claude/perf-update-add-1ba891
Open

mikeland73 wants to merge 5 commits into
mainfrom
claude/perf-update-add-1ba891

Conversation

@mikeland73

Copy link
Copy Markdown
Collaborator

Implements Tier 3 items 12, 14, 16 and the first cleanup in item 17 of specs/perf-improvements.md.

Changes

  • Item 12: parallel resolve in devbox update. updatePendingPackages now resolves packages in a bounded errgroup (8 at a time), then merges lockfile entries serially in input order. Error handling is unchanged: flake refs warn and continue, versioned packages abort.

  • Item 14: --refresh only for changed packages. updatePendingPackages returns the packages whose lockfile entry actually changed, and only those go into packagesBeingUpdated. installNixPackagesToStore groups builds by allow-insecure and refresh, so --refresh applies only to changed packages. Unchanged packages are built only if they're missing from the store.

  • Item 16: one recompute in Add / Update. New config-only helpers addToConfig and removeFromConfig. Add replacing a package with the same canonical name no longer calls Remove, which ran its own ensureStateIsUpToDate. Update migrating legacy packages no longer calls Remove + Add per package. Each command now runs ensureStateIsUpToDate once at the end.

  • Item 17: remove nix.FlakeUpdate. It isn't needed:

    • Every generated flake input is a locked ref from devbox.lock (Stdenv and each flake's Resolved).
    • makeFlakeFile deletes flake.lock whenever flake.nix changes.
    • The call ran after the environment was already built and computed, and errors were ignored.
    • add/install never ran it.

    internal/nix/flake_update.go is deleted.

Behavior notes

  • In Update, a legacy package migration now saves devbox.json only after the whole update succeeds. Before, it was saved per package, before the lockfile update ran.

Testing

  • go test ./internal/devbox/... ./internal/lock/... ./internal/nix/... passes. The new tests also pass under -race.
  • New unit tests for resolveAndMergePackages: it returns only changed packages, a flake failure warns and continues, a versioned failure aborts, and unversioned packages are skipped.
  • Testscripts update, update_flake_local, add, add_replace, add_platforms and global_add pass locally.
  • update.test.txt now runs a second devbox update and asserts it reports "Already up-to-date" and installs nothing.

🤖 Generated with Claude Code

mikeland73 and others added 2 commits November 13, 2026 09:29
…gle recompute

- Resolve packages concurrently in `devbox update` (bounded errgroup), then
  merge lockfile entries serially in input order. Flakes still warn and
  continue on failure; versioned packages still abort.
- Only pass `--refresh` to `nix build` for packages whose lockfile entry
  changed. Unchanged packages are built only if missing from the store.
- Factor out config-only addToConfig/removeFromConfig so Add replacing a
  same-canonical-name package and Update migrating legacy packages run
  ensureStateIsUpToDate once instead of per package.
- Remove the post-update `nix flake update` call. Generated flake inputs are
  locked refs from devbox.lock and flake.lock is deleted whenever flake.nix
  changes, so it never had anything to update.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@claude

claude Bot commented Nov 13, 2026

Copy link
Copy Markdown

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

@greptile-apps

greptile-apps Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; the previous findings are fixed and no new actionable issues were found.

Summary

This PR speeds up package updates:

  • Package updates resolve together and refresh only changed lock entries.
  • Adding and updating packages now syncs staged config changes together.

All three previous findings are addressed. RunXRegistry now locks initialization. Legacy fallback replacements explicitly drop their old lock entries. Ordinary removal preserves pins still needed by includes. No new actionable issues were found in the changes since the previous review.

Reviews (3) · Last reviewed commit: "refactor(update): extract migrateLegacyP..." · Reviewed by Greptile

Comment thread internal/devbox/update.go Resolved
Comment thread internal/devbox/update.go Resolved
- RunXRegistry lazily initialized a package-level cache without a lock;
  parallel resolves in `devbox update` could race on it. Guard it with a
  mutex.
- removeFromConfig now drops the package's lockfile entry. The old Remove
  path tidied it via ensureStateIsUpToDate; without that, a legacy package
  re-added under the same name reused its old locked revision.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mikeland73

mikeland73 commented Nov 13, 2026 •

Copy link
Copy Markdown
Collaborator Author

@greptileai please re-review. Both P1 findings are addressed in 30b254d ("fix(update): guard runx registry init, drop lock entry on config remove"): the runx registry init is now mutex-guarded, and removeFromConfig drops the removed package's lock entry.


Written by Claude on behalf of / prompted by @mikeland73.

Comment thread internal/devbox/packages.go Resolved
mikeland73 and others added 2 commits November 13, 2026 10:38
Deleting the lock entry in removeFromConfig dropped pins that an include
still needed. Plain removal now leaves cleanup to lock.File.Tidy again; the
entry is forgotten only when a package is removed and re-added under the
same name (Add fallback replace, Update legacy migration), which is the case
that needs a fresh resolve.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Update's cognitive complexity was 33 (> 32) after the previous changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mikeland73

Copy link
Copy Markdown
Collaborator Author

@greptileai please re-review. All CI checks are green on 3e8b648. The removal-pin finding is addressed in d737261: removeFromConfig no longer touches the lockfile, and the entry is forgotten only on a same-name replacement. 3e8b648 extracts migrateLegacyPackages to satisfy the lint complexity limit.


Written by Claude on behalf of / prompted by @mikeland73.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant