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
15 changes: 10 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,12 @@ internal/packages/ npm global snapshot / restore / migrate (merged in PR
internal/platform/
platform.go DataDir(), SnapshotsDir(), CacheDir(), LockPath(), IsWindows(), …
shell.go RunShell() — all shell exec goes here
internal/ui/ scaffold (mode.go + theme.go + writer.go) merged via #74 PR1; spinners/huh/full migration tracked by #105
internal/ui/ single source of truth for user-facing output
mode.go Mode (PlainMode / FancyMode) + DecideMode (TTY + NO_COLOR + --no-color gating)
theme.go DefaultTheme — lipgloss styles for FancyWriter
writer.go Writer interface + PlainWriter / FancyWriter implementations
spinner.go bubbletea-backed braille spinner for long-running steps
prompt.go huh-backed Confirm / Select helpers for interactive flows
```

## Key invariants — read before writing code
Expand All @@ -52,9 +57,9 @@ internal/ui/ scaffold (mode.go + theme.go + writer.go) merged via

**Platform-specific code:** Use `//go:build windows` build tags on `*_windows.go` files. Files without build tags must compile on all three OSes.

**Dependencies:** No new dependencies without a rationale line in the PR body. Core runtime deps: `cobra`, `Masterminds/semver/v3`, `yaml.v3`, `lipgloss` (for `internal/ui` — see #74). Planned but not yet in `go.mod`: `huh`, `bubbletea` (the remaining Charm stack for `internal/ui` spinners + prompts — see #105).
**Dependencies:** No new dependencies without a rationale line in the PR body. Core runtime deps: `cobra`, `Masterminds/semver/v3`, `yaml.v3`, plus the Charm stack for `internal/ui`: `lipgloss` (styling), `bubbletea` (spinner), `huh` (prompts). The plain-vs-fancy switch is the single decision point — see `internal/ui/mode.go`.

**Manager detection order:** `--manager` flag → `~/.nodeup/config.yaml` → auto-detect (env vars → PATH → well-known dirs). `DetectAll()` returns a `Registry`; `ResolveManager(reg, preferred)` picks one or errors. When multiple managers found and no preference, the caller should use `ResolveInteractive` (planned under #105 as part of the `huh` migration).
**Manager detection order:** `--manager` flag → `~/.nodeup/config.yaml` → auto-detect (env vars → PATH → well-known dirs). `DetectAll()` returns a `Registry`; `ResolveManager(reg, preferred)` picks one or errors. When multiple managers found and no preference, the caller should use `ResolveInteractive` (a `huh`-backed Select that lives in `internal/detector/interactive.go` — wired up by #118 as part of the `huh` migration).

**Packages to skip during migration:** `npm`, `corepack`, `npx` — these are bundled with Node and must not be migrated.

Expand Down Expand Up @@ -127,8 +132,8 @@ orchestrator.
| 4 — Upgrade command + UI | Done | merged |
| 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 tracked by #35 |
| 8 — v1.0.0 release | Done | `nodeupx@1.0.1` live on npm (closes #35); trusted-publisher OIDC flow in place for future tags |
| 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.1.0 releases | Done | `nodeupx@1.0.1` (and now `@1.1.0` matching the Go binary) live on npm; trusted-publisher OIDC flow active for every tag (`#109`) |

## On-disk data layout

Expand Down
15 changes: 5 additions & 10 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ opening an issue or a PR.

## Quick orientation

- **Language / runtime:** Go 1.22+
- **Language / runtime:** Go 1.24+
- **Architecture:** see the `How it works` section in [`README.md`](./README.md)
for the high-level pipeline. The single source of truth for implementation
details is the Go source under `internal/` plus inline godoc.
Expand Down Expand Up @@ -123,7 +123,7 @@ by hand in PRs.
- **Feature requests:** use `.github/ISSUE_TEMPLATE/feature_request.md`.
Read the in-template "Out-of-scope check" before filing — `.nvmrc` /
`.node-version` management, `npm`-itself updates, `yarn`/`pnpm`
globals, and self-updates are explicitly out of scope for v1.
globals, and self-updates are explicitly out of scope for v1.x.
- **Security issues:** do **not** open a public issue. Email the
maintainer directly (see GitHub profile) and give 90 days for a
coordinated fix.
Expand All @@ -139,11 +139,6 @@ by hand in PRs.
`fmt.Println` from business logic. This keeps output testable and
uniform (color, spinners, summary tables).
- **No new dependencies without a rationale line in the PR body.** We
lean on the stdlib + cobra + yaml.v3.

## Sign-off (future)

When the project gains external contributors, we'll require a
[DCO](https://developercertificateoforigin.org/) sign-off (`Signed-off-by:`
trailer on every commit). Not required today; will be enabled by a
`.github/workflows/dco.yml` check before the v1.0.0 release.
lean on the stdlib + `cobra` + `yaml.v3` + the Charm stack
(`lipgloss`, `bubbletea`, `huh` — all of which live behind the
`internal/ui` boundary).
32 changes: 19 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,9 @@ Expand-Archive nodeup.zip -DestinationPath .
go install github.com/dipto0321/nodeup/cmd/nodeup@latest
```

Requires Go 1.22+. Best for: `nodeup` contributors and anyone who
wants to track `main` between releases.
Requires Go 1.24+ (matches `go.mod` and the CI pin). Best for:
`nodeup` contributors and anyone who wants to track `main` between
releases.

## Quickstart

Expand Down Expand Up @@ -243,19 +244,24 @@ See [`docs/configuration.md`](./docs/configuration.md) for the full schema.

## Project status

This is the **v1.0.0 development line**. See `CHANGELOG.md` for what's done.
Current release: **v1.1.0** (`nodeupx@1.0.1` on npm — the npm wrapper
trails the Go binary by one publish). See `CHANGELOG.md` for the
v1.0.0 → v1.1.0 history.

| Version | Status | Notes |
| Phase | Status | What shipped |
|---|---|---|
| v1.0.0 | 🛠 in development | Phase 1 ✅ — 8/8 managers detected. Phase 2 ✅ — `nodeup check` with nodejs.org/dist/index.json fetch + TTL cache. Phase 3 ✅ — package snapshot/restore + migration report. Phase 4 ✅ — end-to-end `nodeup upgrade`. Phase 5 ✅ — YAML config file + `config` subcommands (show / get / set / init). Phase 6 ✅ — cross-platform polish: `QuotePath` for paths with spaces, interrupted-upgrade sentinel + replay, system-node classifier with warnings. Phase 7 🔧 (in progress) — post-upgrade cleanup prompt (`--cleanup` / `--cleanup-version` / `--no-cleanup` / `cleanup.auto` / `cleanup.prompt`) plus native mutation commands for all 7 supported managers have shipped. Distribution packaging (GoReleaser + brew/scoop taps + npm wrapper) is the remaining Phase 7 work — see issues #17 and #18. |

Phase 1 is the **detection surface** — every manager is recognized and the
version + installed-list reads return real data (PRs #1–#8). Subsequent
phases layer commands on top: `nodeup check` (Phase 2) → `nodeup packages`
(Phase 3) → `nodeup upgrade` end-to-end (Phase 4) → `nodeup config`
(Phase 5) → cross-platform polish (Phase 6) → first tagged release
(Phase 7). The v1.0.0 cut is blocked on Phase 7 (issue #17) and the
release-prep runbook (issue #18).
| 1 — Detector engine | ✅ | 8/8 managers detected (`fnm`, `nvm`, `Volta`, `asdf`, `mise`, `n`, `nodenv`, `nvm-windows`) |
| 2 — Node version API | ✅ | `nodeup check` + nodejs.org/dist/index.json fetch + TTL cache + HTTP retry/backoff/timeout |
| 3 — Package snapshot/restore | ✅ | `nodeup packages {snapshot,restore,diff}` + interrupted-upgrade sentinel + MigrationReport |
| 4 — Upgrade command | ✅ | End-to-end `nodeup upgrade` with snapshot → install → migrate → cleanup |
| 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 — First tagged release | ✅ | `v1.0.0` shipped (PR #35); `v1.1.0` shipped with the `internal/ui` rollout (`#117`/`#118`/`#119`) |

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

## Docs index

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

Distilled from code (README, CLAUDE.md, CONTRIBUTING.md, docs/*, and
`internal/*` source). Last refresh: 2026-07-05 (post #103 fix-wave;
§C/V2 + §T updated to match current state).
`internal/*` source). Last refresh: 2026-07-05 (post v1.1.0 release;
§C/§I/§V/§T updated to match current state — `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 All @@ -26,8 +27,9 @@ manual steps, single static binary, no Node runtime dependency.
`/` or `\`.
- `*_windows.go` → `//go:build windows`; other files compile ∀ 3 OS
(macOS, Linux, Windows).
- ∀ user-facing output → `internal/ui` (scaffold merged via #74 PR1;
spinners + huh prompts + full call-site migration tracked by #105).
- ∀ user-facing output → `internal/ui` (Writer + Plain/Fancy modes +
bubbletea spinners + huh prompts + full call-site migration shipped
via #74 → #117 → #118 → #119).
- packages `{npm, corepack, npx}` ⊥ migrate (bundled w/ Node itself).
- Manager resolution order: `--manager` flag → `~/.nodeup/config.yaml` →
env vars → PATH → well-known dirs.
Expand Down Expand Up @@ -62,9 +64,9 @@ manual steps, single static binary, no Node runtime dependency.
## §V Invariants

- V1: ∀ shell exec → `platform.RunShell()` | `RunShellScript()` (never raw `os/exec`).
- V2: ∀ user-facing string → `internal/ui` (partialscaffold via #74 PR1;
full migration tracked by #105; cmd.Printf still present in upgrade/check/
list/packages/config/cleanup).
- V2: ∀ user-facing string → `internal/ui` (completefull migration
shipped via #119; every `cmd.Printf` / `fmt.Fprintf` call site in
`internal/cli/*` now flows through `ui.Writer`).
- V3: ∀ error return → handled, wrapped w/ `%w` for context.
- V4: ∀ cobra `RunE` → `cmd.Context()`, ⊥ `context.Background()`.
- V5: ∀ path → `filepath.Join()`, ⊥ hardcoded separators.
Expand Down Expand Up @@ -116,10 +118,10 @@ T14|x|fail closed (not open) when Current() errors during cleanup|V14,#58,#87
T15|x|verify/fix `n` manager's reliance on `n current` subcommand|#59,#88
T16|x|fix cleanup docs inaccuracies (CHANGELOG issue ref, nvm-windows note, PATH failure mode, Phase 7 conflict)|#60,#97
T17|x|dead-code cleanup: report.go, FileOverlay(), confirm var, fnm.go error wrap, nvm Current() test gap|#61,#98
T18|.|design + implement internal/ui output layer|V2,#74,#105
T19|.|fix double-prompt + sticky-no-override at cleanup decision point|#76,#95
T18|x|design + implement internal/ui output layer|V2,#74,#105,#117,#118,#119
T19|x|fix double-prompt + sticky-no-override at cleanup decision point|#76,#95
T20|x|rest of cleanup/chore/ci/release batch|#62-#69,#89-#94,#99,#100,#107-#109
T21|.|doc drift: CLAUDE.md ui/lipgloss/phase-table stale; SPEC §T stale|#106
T21|x|doc drift: CLAUDE.md ui/lipgloss/phase-table stale; SPEC §T stale|#106

## §B Bugs

Expand Down
2 changes: 1 addition & 1 deletion docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ self-contained — no runtime, no Node, no manager.
go install github.com/dipto0321/nodeup/cmd/nodeup@latest
```

Requires Go 1.22+.
Requires Go 1.24+ (matches `go.mod` and the CI pin in `.github/workflows/ci.yml` + `release.yml`).

**Who this is for:** `nodeup` contributors and Go developers who want
to track `main` between tagged releases. The binary lands in
Expand Down
2 changes: 1 addition & 1 deletion docs/managers.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,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 in progress — post-upgrade cleanup prompt and per-manager native mutation commands have shipped (PR #56 / issue #41). Distribution packaging (GoReleaser, brew/scoop taps, npm wrapper) is the remaining Phase 7 work — see issues #17 and #18. README's "Phase 7 ✅" line refers to the cleanup/mutation work, not the distribution work._
> _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.0.1` via OIDC Trusted Publishing) have all shipped — see `CHANGELOG.md` for the v1.0.0 → v1.1.0 history._

## Post-upgrade cleanup

Expand Down
6 changes: 4 additions & 2 deletions docs/release-checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

The release pipeline (`.github/workflows/release.yml` + GoReleaser v2)
fires automatically on a `v*.*.*` tag push. This checklist is the
human-side companion — use it for the **first stable release** (v1.0.0),
and ad-hoc when shipping a patch release.
human-side companion for shipping any release — including patch
releases off `main`. The first `nodeupx@1.0.1` bootstrap publish was
the only manual step ever required and is preserved below for
historical reference.

## Pre-release (on `chore/release/vX.Y.Z` branch)

Expand Down
Loading