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
7 changes: 5 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.puku/
nodeup.md%
nodeup.md

# Go
*.exe
Expand Down Expand Up @@ -38,4 +38,7 @@ Thumbs.db
# need a package.json. Developers running `npx commitlint` locally
# install into a throwaway node_modules.
node_modules/
package-lock.json
package-lock.json

# Tooling session metadata (not part of the project source tree).
.claude/
32 changes: 32 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- YAML config file support (`internal/config`): the documented schema
from `docs/configuration.md` is now first-class. Settings live in
`~/.nodeup/config.yaml` (override with `$NODEUP_CONFIG` or redirect
`$NODEUP_HOME`) and are merged with env vars and built-in defaults
using a four-layer precedence chain (defaults < file < env < CLI
flags). Every field — including `track.lts: false` and other
explicit-zero values — is preserved across round-trips via a per-
field set-flag overlay so partial files don't clobber defaults.
Saves are atomic (temp + rename, mode 0600) and refuse to persist
invalid configs.
- `nodeup config` subcommands:
- `show` — print the merged effective config as YAML (the output
round-trips with the file format).
- `get <key>` — read a single dotted key (e.g. `packages.skip`).
- `set <key> <value>` — edit a key in the file layer and save it.
Validates before writing; rejects unknown keys and bad values.
- `init [--force]` — scaffold a fresh config at the default path;
refuses to overwrite without `--force`.
- Environment variable overlay (`NODEUP_MANAGER`, `NODEUP_TRACK_LTS`,
`NODEUP_TRACK_CURRENT`, `NODEUP_PACKAGES_MIGRATE`,
`NODEUP_PACKAGES_STRATEGY`, `NODEUP_CACHE_TTL`). Parse errors
include the variable name so env typos surface immediately.
- `nodeup upgrade` now reads its effective config from
`loadConfigOrDefault()`: `--manager` flag still wins over the file
value, and `cfg.Packages.Migrate` replaces the hard-coded `true`
so users can opt out globally.
- `scripts/issue-workflow.sh`: issue→branch→PR→squash-merge automation
script (bug-fix in the same change: fixed the bash regex parser
that was rejecting valid issue titles because space-separated
alternations aren't valid ERE).

### Changed
- Consolidated the internal `nodeup.md` design doc into `README.md`
(new "Compatibility notes" subsection, expanded Contributing
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,16 +178,18 @@ cache:
```

Flags override env vars (`NODEUP_MANAGER`, `NODEUP_TRACK_LTS`, `NODEUP_CACHE_TTL`)
override the file. See [`docs/configuration.md`](./docs/configuration.md) for the
full schema.
override the file. Manage it with `nodeup config {show,get,set,init}` — for
example `nodeup config set manager fnm` writes through to the file (refuses
to write invalid values, refuses to overwrite on `init` without `--force`).
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.

| Version | Status | Notes |
|---|---|---|
| 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 upgrade command. |
| 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 1 is the **detection surface** — every manager is recognized and the
version + installed-list reads return real data (PRs #1–#8). Subsequent
Expand Down
56 changes: 48 additions & 8 deletions docs/configuration.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,63 @@
# nodeup Configuration

The schema below is the source of truth. Config-file parsing and the
`nodeup config set/get/show` subcommands land in Phase 5
(`feat(config): …`), but the schema and env-var precedence rules are
fixed now so other subsystems can reference them.
nodeup's behavior can be tuned in three layers: a YAML config file,
environment variables, and CLI flags. The schema and precedence rules
below are the source of truth for all subsystems.

The optional config file lives at `~/.nodeup/config.yaml`.
The optional config file lives at `~/.nodeup/config.yaml` (path
override: `NODEUP_CONFIG`). The file is created and edited via the
`nodeup config {init,show,get,set}` subcommands.

Resolution precedence (highest first):

1. CLI flags (e.g. `--manager fnm`)
2. Environment variables (`NODEUP_MANAGER`, `NODEUP_TRACK_LTS`, `NODEUP_CACHE_TTL`)
3. Config file
1. CLI flags (e.g. `--manager fnm`, `--no-migrate`)
2. Environment variables (`NODEUP_MANAGER`, `NODEUP_TRACK_LTS`, `NODEUP_CACHE_TTL`, ...)
3. Config file (`~/.nodeup/config.yaml`)
4. Built-in defaults

Explicit zero values are preserved at every layer — `NODEUP_TRACK_LTS=false`
overrides a file that says `track.lts: true`, and `nodeup config set
packages.skip ""` clears the default package-skip list. To restore a
layer's default, omit the key from the file rather than writing its
zero value.

## Config-file example

```yaml
schema_version: 1
manager: fnm
track:
lts: true
current: false
packages:
migrate: true
strategy: latest
skip: [yarn, pnpm]
cleanup:
auto: false
prompt: true
cache:
ttl: 7200
```

The file is written atomically (sibling temp + rename) with mode 0600.
`nodeup config set` validates before writing, so an invalid value
leaves the previous file untouched.

## Subcommands

| Subcommand | Purpose |
|---|---|
| `nodeup config show` | Print the merged (defaults < file < env) config as YAML, with a `# path: ...` header comment |
| `nodeup config get <key>` | Print the merged value of a dotted key (e.g. `packages.strategy`) |
| `nodeup config set <key> <value>` | Update one key in the config file; refuses to write invalid values |
| `nodeup config init [--force]` | Write a fresh config file with defaults; refuses to overwrite without `--force` |

## Schema

| Key | Type | Default | Description |
|---|---|---|---|
| `schema_version` | int | `1` | Schema revision. Reserved for future migrations. |
| `manager` | string | _auto-detect_ | Force a specific version manager |
| `track.lts` | bool | `true` | Track LTS upgrades |
| `track.current` | bool | `false` | Track Current upgrades |
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.24.0
require (
github.com/Masterminds/semver/v3 v3.5.0
github.com/spf13/cobra v1.10.2
gopkg.in/yaml.v3 v3.0.1
)

require (
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiT
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading
Loading