fix(packages): continue past failed installs; write MigrationReport on restore#115
Merged
dipto0321 merged 1 commit intoJul 5, 2026
Conversation
…n restore The previous installPackages() returned on the first failed `npm install -g`, so a single broken / renamed / yanked package silently aborted the migration of every package after it. A user upgrading 30 globals where #3 fails lost 27 packages with no visible signal. Plus the entire MigrationReport.Save() / report.go path was dead code — the file shipped but was never called. Three concerns, same root cause: package install failures were treated as fatal instead of informational. Now treated as data. - internal/packages/snapshot.go: installPackages LOOP-ALL. ctx.Err() is checked at the top of each iteration so Ctrl-C still aborts promptly; only per-package npm failures continue. Returns ([]PackageResult, error) where the slice is populated for every package attempted (success or fail) so callers can build a MigrationReport. Aggregate error wraps the first failure with an "N of M packages failed" headline and preserves the original via %w. Restore and RestoreFromSnapshot now return a RestoreOutcome struct (parsed snapshot + per-package results) so callers can populate MigrationReport metadata without re-parsing the snapshot. - internal/packages/snapshot.go: package-level runShell test seam so the install loop is unit-testable without spawning a real npm. Tests must NOT call t.Parallel() — the seam is package-global, same pattern fnm.go uses. - internal/packages/report.go: Path() returns a stable filename per-report (with a 4-byte crypto/rand tag to prevent sub-second collisions), and Save() uses the same path. Save is NOT safe to call concurrently on the same instance, but no real caller does. - internal/cli/upgrade.go: builds a MigrationReport on every restore (success or partial) using the snapshot's recorded manager / from-version when available. Skips the write on empty results. Partial failure keeps sentinel armed (restoreSucceeded stays false), so `nodeup packages restore --from <path>` still works for retry. - internal/cli/packages.go: same report write on both --from and positional branches; report reflects the snapshot's recorded manager / from-version so the --from branch gets accurate metadata instead of "unknown". - internal/packages/snapshot_test.go: 6 new tests covering loop-all behavior (table-driven 5 packages with 2 mid-list failures), all-success, ctx-cancel mid-loop, empty list, MigrationReport path stability + content round-trip, and report path collision resistance between two reports created in the same wall-clock second. - internal/packages/sentinel_test.go: updated for the new RestoreFromSnapshot signature. Closes #103. Co-Authored-By: puku-ai-2.8 <noreply@puku.sh>
|
✔️ fb4b7d1 - Conventional commits check succeeded. |
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.
Summary
The previous
installPackages()returned on the first failednpm install -g, so a single broken / renamed / yanked package silently aborted the migration of every package after it. A user upgrading 30 globals where #3 fails lost 27 packages with no visible signal. Plus the entireMigrationReport.Save()/report.gopath was dead code — the file shipped but was never called. Three concerns, same root cause: package install failures were treated as fatal instead of informational. Now treated as data.Changes
internal/packages/snapshot.go—installPackagesnow LOOP-ALL.ctx.Err()is checked at the top of each iteration so Ctrl-C still aborts promptly; only per-package npm failures continue. Returns([]PackageResult, error)where the slice is populated for every package attempted (success or fail) so callers can build aMigrationReport. Aggregate error wraps the first failure with an"N of M packages failed"headline and preserves the original via%w.RestoreandRestoreFromSnapshotreturn aRestoreOutcomestruct (parsed snapshot + per-package results) so callers can populateMigrationReportmetadata without re-parsing the snapshot.internal/packages/snapshot.go— package-levelrunShelltest seam so the install loop is unit-testable without spawning a realnpm. Tests must NOT callt.Parallel()— the seam is package-global, same patternfnm.gouses.internal/packages/report.go—Path()returns a stable filename per-report (with a 4-bytecrypto/randtag to prevent sub-second collisions), andSave()uses the same path. Save is NOT safe to call concurrently on the same instance, but no real caller does.internal/cli/upgrade.go— builds aMigrationReporton every restore (success or partial) using the snapshot's recorded manager / from-version when available. Skips the write on empty results. Partial failure keeps sentinel armed (restoreSucceededstays false), sonodeup packages restore --from <path>still works for retry.internal/cli/packages.go— same report write on both--fromand positional branches; report reflects the snapshot's recorded manager / from-version so the--frombranch gets accurate metadata instead of"unknown".internal/packages/snapshot_test.go— 6 new tests covering loop-all behavior (table-driven 5 packages with 2 mid-list failures), all-success, ctx-cancel mid-loop, empty list,MigrationReportpath stability + content round-trip, and report path collision resistance between two reports created in the same wall-clock second.internal/packages/sentinel_test.go— updated for the newRestoreFromSnapshotsignature.Sentinel semantics on partial failure
restoreSucceededonly flips totruewhen zero packages failed. On partial failure, the sentinel stays armed and the user can re-runnodeup packages restore --from <snapshot-path>to retry the failed packages — the report file (always written, success or partial) tells them which ones failed. Documented inline atupgrade.go:88-103.Verification
go test -race ./internal/packages/...— greenmake ci— green (golangci-lint: 0 issues; coverage: 62.8%)crypto/randsuffix)Closes #103.