Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 67 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,71 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.2.2] - 2026-11-01

A patch release that **stops the snapshot leak** and gives the user
explicit control over whatever is left on disk.

Nothing in nodeup ever deleted a snapshot file. `nodeup upgrade`
writes one snapshot of the global npm packages per installed Node
version so the migration can be rolled back or replayed — and then
never removed it. The set only ever grew: one file per `(manager,
Node version)` pair the user had ever installed, plus the orphan
left behind by an upgrade that failed partway. The migration reports
in `<DataDir>/reports/` leaked the same way, with unbounded
random-suffixed filenames.

### Fixed
- The upgrade flow now reclaims the snapshots it wrote, once they
have served their purpose. The snapshots are deleted only when the
migration actually replayed them onto the new Node version — or
when there was nothing to migrate at all, in which case they are
redundant copies of the current state. A snapshot whose contents
were **not** replayed survives, because on the failure path it is
the only record of the user's globals and the "interrupted upgrade"
warning points straight at it. Closes #128.
- The upgrade sentinel (`<DataDir>/upgrade-in-progress.json`) was
armed even when there was nothing to install. `restoreSucceeded`
then never became true, so the sentinel was never cleared — and
*every* subsequent `nodeup` invocation warned about an interrupted
upgrade that never happened. The sentinel is now armed only when
there is actually something to install.
- `nodeup packages snapshot` captured the wrong version. It returned
`versions[0]` — the **lowest** installed version, because
`ListInstalled` sorts ascending. On a machine with Node 18 and
Node 20 it captured the (usually empty) 18 install and filed it
under the 20.x filename, producing a snapshot useless as a restore
source. It now prefers the manager's *active* version
(`Manager.Current`) and falls back to the **highest** installed
version for managers that cannot report one (`nvm-windows` returns
"not implemented").
- Snapshot writes are now atomic. A crash mid-write left a truncated
file that the snapshot lister silently skipped — an invisible
orphan that could never be cleaned up. Writes go through a `.tmp`
file plus a rename.

### Added
- `nodeup packages cleanup` — delete the snapshot and migration-report
files nodeup keeps on disk. It lists what's there (name, size, mtime)
and lets you pick files one by one (multi-select) or delete them all
at once. `--list` shows what would go without deleting anything,
`--reports` targets the report files instead of the snapshots, and
`--yes` skips the confirmation prompt (it implies `--all`).
- `--keep-snapshots` on `nodeup upgrade`, plus the
`packages.keep_snapshots` config key and the
`NODEUP_PACKAGES_KEEP_SNAPSHOTS` environment variable, to opt out of
the automatic snapshot deletion.

### Changed
- The GoReleaser configuration no longer relies on deprecated keys
(`archives[].format` → `formats`, `brews:` → `homebrew_casks:`), and
the build metadata ldflags moved out of `builds[].flags` into the
dedicated `builds[].ldflags` key. Passing ldflags through `flags`
produced two `-ldflags` arguments on the `go build` command line,
and Go takes the last one — so our `-X main.commit=…` was silently
dropped and the shipped binary reported GoReleaser's defaults
instead. Closes #129.

## [1.2.1] - 2026-11-01

A patch release that repairs the **npm install path**, which has been
Expand Down Expand Up @@ -909,7 +974,8 @@ without weakening the body-length guard for hand-authored commits.
### Added
- Project blueprint — internal design doc covering language choice, scope, detection engine, version resolution, package migration, architecture, CLI design, edge cases, git workflow, conventional commits, versioning, CI/CD, and distribution. (Superseded by `README.md`; the standalone doc was removed in the Unreleased section.)

[Unreleased]: https://github.com/dipto0321/nodeup/compare/v1.2.1...HEAD
[Unreleased]: https://github.com/dipto0321/nodeup/compare/v1.2.2...HEAD
[1.2.2]: https://github.com/dipto0321/nodeup/compare/v1.2.1...v1.2.2
[1.2.1]: https://github.com/dipto0321/nodeup/compare/v1.2.0...v1.2.1
[1.2.0]: https://github.com/dipto0321/nodeup/compare/v1.1.0...v1.2.0
[1.1.0]: https://github.com/dipto0321/nodeup/compare/v1.0.1...v1.1.0
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ orchestrator.
| 5 — Config subsystem | Done | merged |
| 6 — Cross-platform polish | Done | merged (interrupted-upgrade sentinel, `QuotePath`, system-node classifier) |
| 7 — Distribution packaging | Done | GoReleaser, brew/scoop/npm wrappers all shipped (#17, #18); manual v1.0.0 npm publish (#35) bootstrapped the OIDC trust |
| 8 — v1.0.0 → v1.2.1 releases | Done | `nodeupx@1.2.1` live on npm (pinned to the v1.2.0 Go binary); trusted-publisher OIDC flow active for every tag (`#109`) |
| 8 — v1.0.0 → v1.2.2 releases | Done | `nodeupx@1.2.2` live on npm (matching the Go binary); trusted-publisher OIDC flow active for every tag (`#109`) |

## On-disk data layout

Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ brew install dipto0321/tap/nodeup
```

Best for: macOS / Linux users who already use Homebrew for tools. The
formula is auto-managed by GoReleaser from `brew: {}` in
cask is auto-managed by GoReleaser from `homebrew_casks: {}` in
`.goreleaser.yaml`, so a `v*.*.*` tag updates the tap.

#### Scoop (Windows)
Expand Down Expand Up @@ -268,9 +268,9 @@ See [`docs/configuration.md`](./docs/configuration.md) for the full schema.

## Project status

Current release: **v1.2.1** (`nodeupx@1.2.1` on npm — the wrapper
Current release: **v1.2.2** (`nodeupx@1.2.2` on npm — the wrapper
publishes automatically alongside the tag). See `CHANGELOG.md` for the
v1.1.0 → v1.2.1 history.
v1.1.0 → v1.2.2 history.

| Phase | Status | What shipped |
|---|---|---|
Expand All @@ -281,9 +281,9 @@ v1.1.0 → v1.2.1 history.
| 5 — Config subsystem | ✅ | YAML config at `~/.nodeup/config.yaml` + `config {show,get,set,init}` + env vars |
| 6 — Cross-platform polish | ✅ | `QuotePath`, interrupted-upgrade replay, system-node classifier, lock file |
| 7 — Distribution packaging | ✅ | GoReleaser + brew tap + Scoop bucket + npm wrapper (`nodeupx` via OIDC Trusted Publishing) |
| 8 — Tagged releases | ✅ | `v1.0.0` shipped (PR #35); `v1.1.0` shipped with the `internal/ui` rollout (`#117`/`#118`/`#119`); `v1.2.0` adds Vite+ support (`#124`); `v1.2.1` repairs the npm install path (`#126`) |
| 8 — Tagged releases | ✅ | `v1.0.0` shipped (PR #35); `v1.1.0` shipped with the `internal/ui` rollout (`#117`/`#118`/`#119`); `v1.2.0` adds Vite+ support (`#124`); `v1.2.1` repairs the npm install path (`#126`); `v1.2.2` stops the snapshot leak (`#130`) |

**Release train:** every `v*.*.*` tag triggers GoReleaser → brew formula
**Release train:** every `v*.*.*` tag triggers GoReleaser → brew cask
push → Scoop manifest push → `nodeupx` publish via OIDC. To ship a
new version: see `docs/release-checklist.md`.

Expand Down
8 changes: 4 additions & 4 deletions SPEC.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# SPEC — nodeup

Distilled from code (README, CLAUDE.md, CONTRIBUTING.md, docs/*, and
`internal/*` source). Last refresh: 2026-11-01 (post v1.2.0 release;
§C/§I/§V/§T updated to match current state — Vite+ support complete
via #124, `internal/ui` rollout complete via #117/#118/#119, #120/#121
are commitlint relaxations).
`internal/*` source). Last refresh: 2026-11-01 (post v1.2.2 release;
§C/§I/§V/§T updated to match current state — snapshot cleanup complete
via #130, Vite+ support complete via #124, `internal/ui` rollout
complete via #117/#118/#119, #120/#121 are commitlint relaxations).
Caveman encoding per FORMAT.md convention (not present at repo root —
using shapes from the caveman skill).

Expand Down
11 changes: 9 additions & 2 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

All five channels below are wired up and tested. The GoReleaser config
that produces the binary archives lives in `.goreleaser.yaml`; the
Homebrew tap formula and Scoop bucket manifest are pushed
Homebrew tap cask and Scoop bucket manifest are pushed
automatically on every `v*.*.*` tag (see `.github/workflows/release.yml`).

## Supported platforms
Expand Down Expand Up @@ -41,9 +41,16 @@ brew install dipto0321/tap/nodeup
Homebrew for dev tooling (`brew` is the package manager for
command-line apps, distinct from the App Store). The tap
[`dipto0321/homebrew-tap`](https://github.com/dipto0321/homebrew-tap)
holds the formula and is auto-pushed by GoReleaser on every
holds the cask and is auto-pushed by GoReleaser on every
`v*.*.*` tag.

> **Upgrading from the old formula:** `nodeup` shipped as a Homebrew
> formula up to v1.2.1 and switched to a cask in v1.2.2. The command
> is unchanged (`brew install dipto0321/tap/nodeup` still works), but
> Homebrew treats a formula and a cask as different packages — so if
> you installed the formula, run `brew uninstall --formula nodeup`
> after upgrading the tap.

**Tradeoffs:** upgrades happen via `brew upgrade`, which all Homebrew
users already run on a schedule. The install lives in Homebrew's
prefix (typically `/opt/homebrew/bin` on Apple Silicon), completely
Expand Down
4 changes: 2 additions & 2 deletions docs/managers.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Supported Version Managers

All 9 managers listed below are fully detected by `nodeup` as of v1.2.0
All 9 managers listed below are fully detected by `nodeup` as of v1.2.2
(macOS, Linux, and Windows where applicable). The matrix is the source of
truth — keep it in sync with `internal/detector/registry_*.go` and the
per-manager files (`fnm.go`, `nvm.go`, `volta.go`, `asdf.go`, `mise.go`,
Expand Down Expand Up @@ -78,7 +78,7 @@ bin dir (e.g., `fnm env --use-on-cd | source`, or add
points at a binary inside the manager's data dir, nodeup's next
run will classify it as `manager` and proceed without warning.

> _Status: Phase 7 complete. Post-upgrade cleanup prompt, per-manager native mutation commands, and full distribution packaging (GoReleaser, brew/scoop taps, npm wrapper at `nodeupx@1.2.0` via OIDC Trusted Publishing) have all shipped — see `CHANGELOG.md` for the v1.1.0 → v1.2.0 history._
> _Status: Phase 7 complete. Post-upgrade cleanup prompt, per-manager native mutation commands, and full distribution packaging (GoReleaser, brew/scoop taps, npm wrapper at `nodeupx@1.2.2` via OIDC Trusted Publishing) have all shipped — see `CHANGELOG.md` for the v1.1.0 → v1.2.2 history._

## Post-upgrade cleanup

Expand Down
32 changes: 31 additions & 1 deletion docs/release-checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,42 @@ git push origin vX.Y.Z

- [ ] GitHub Release visible at /releases/tag/vX.Y.Z
- [ ] All 5 binaries attached + checksums.txt
- [ ] Homebrew formula pushed to `dipto0321/homebrew-tap`
- [ ] Homebrew cask pushed to `dipto0321/homebrew-tap`
- [ ] Scoop manifest pushed to `dipto0321/scoop-bucket`
- [ ] `publish-npm` job green; `npm view nodeupx version` matches
the tag (automatic via OIDC Trusted Publishing — see below;
**manual for the very first `nodeupx` publish only**)

### The formula → cask transition (one-time, v1.2.2)

`nodeup` shipped as a Homebrew **formula** up to v1.2.1. GoReleaser
deprecated `brews:` in v2.10 in favour of `homebrew_casks:`, so v1.2.2
ships a **cask** instead — a cask's `binary:` stanza is declarative,
while a formula's `install:` block is raw Ruby, and Homebrew now
prefers casks for "download a binary and put it on PATH" distributions.

Homebrew treats a formula and a cask of the same name as two
different packages, so the old formula has to be removed from the tap
by hand, and the tap needs a `tap_migrations.json` so Homebrew can
perform the hand-off automatically on `brew update`:

```json
{
"Formula": {
"nodeup": "Cask",
"nodeup@1.2.1": "Cask"
}
}
```

- [ ] `tap_migrations.json` present in `dipto0321/homebrew-tap` with
the `Formula → Cask` mapping for `nodeup`
- [ ] The old `nodeup.rb` formula removed from the tap (GoReleaser
pushes the cask but does not delete the formula)
- [ ] `brew install dipto0321/tap/nodeup` verified on a scratch tap
- [ ] Existing formula installs get the hand-off on `brew update`
(Homebrew tells them to `brew uninstall --formula --force nodeup`)

### Publishing `nodeupx` (`nodeup-npm/`) to the npm registry

The npm wrapper lives in `nodeup-npm/` at the repo root. Publishing
Expand Down
4 changes: 2 additions & 2 deletions nodeup-npm/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nodeupx",
"version": "1.2.1",
"version": "1.2.2",
"description": "Automated Node.js version upgrade + global package migration CLI. Static Go binary wrapped for npm distribution.",
"license": "MIT",
"author": "Dipto Karmakar <dipto@local>",
Expand Down Expand Up @@ -42,5 +42,5 @@
"README.md",
"LICENSE"
],
"binaryVersion": "1.2.0"
"binaryVersion": "1.2.2"
}
Loading