Skip to content

fix(cli): wire platform.AcquireLock into upgrade + config set/init#79

Merged
dipto0321 merged 1 commit into
mainfrom
fix/platform/wire-lock-into-cli-config
Jul 3, 2026
Merged

fix(cli): wire platform.AcquireLock into upgrade + config set/init#79
dipto0321 merged 1 commit into
mainfrom
fix/platform/wire-lock-into-cli-config

Conversation

@dipto0321

Copy link
Copy Markdown
Owner

Summary

platform.AcquireLock/Release (flock on POSIX, LockFileEx on Windows, stale-PID detection) existed but had zero call sites. The README and package doc comments advertised "concurrent runs are blocked via a lock file" — that claim was false.

This PR wires the lock into runUpgrade, config set, and config init (each wraps the critical section in defer platform.AcquireLock()), surfaces ErrAlreadyLocked with an actionable hint instead of silently racing, and corrects an incidental doc bug: the README claimed the lock lives at ~/.nodeup/nodeup.lock, but the real path is <DataDir>/nodeup.lock (Linux XDG, macOS ~/Library/Application Support/..., Windows %APPDATA%\...).

Closes #44.

Linked issues

Type of change

  • fix — bug fix (PATCH bump)

Checklist

  • Title follows Conventional Commits (fix(scope): subject)
  • I ran make ci locally and it passes (fmt, vet, test pass locally; make lint fails on the v2→v1 schema mismatch tracked in build(lint): .golangci.yml v1 schema incompatible with golangci-lint v2 (brew installs v2, make lint fails to even parse config) #62 — pre-existing on main, independent of this fix; CI runs golangci-lint v1.64.8 per .github/workflows/ci.yml and will parse the config fine)
  • I added or updated tests for the change
  • I updated relevant docs (README, docs/, inline godoc)
  • No new linter warnings
  • If breaking: I documented the migration path in the PR body and updated CHANGELOG.md (N/A — fix)

Scope notes

Screenshots / output

User-visible behaviour change:

  • Before: A second nodeup upgrade (or nodeup config set) silently raced against the first; users had no warning that two invocations were stomping each other.
  • After: A second nodeup upgrade returns:
    refusing to upgrade: nodeup is already running
      (another nodeup process holds the lock; wait for it to finish, or kill the stale process)
    
    And for nodeup config set/config init:
    refusing to edit config: nodeup is already running
      (another nodeup process holds the lock)
    

Verification path: in two terminals, run nodeup upgrade in one and nodeup config set manager fnm in the other — the second one now fast-fails with the message above instead of corrupting state.

`platform.AcquireLock`/`Release` (flock on POSIX, LockFileEx on
Windows, stale-PID detection) had been implemented but had zero
call sites anywhere in `internal/cli` or `internal/config`. The
README and package doc comments advertised "concurrent runs are
blocked via a lock file" — that claim was false.

Two parallel `nodeup upgrade` invocations could snapshot/install/
migrate against the same data dir with no guard between them. Two
parallel `nodeup config set` invocations raced on read-modify-write
of the YAML file (Save itself is atomic via temp-file+rename, but
the second writer's atomic rename silently clobbers the first
writer's in-memory changes — a lost update).

`runUpgrade`, `config set`, and `config init` now defer
`platform.AcquireLock()` around their critical sections (after the
dry-run early-return, because dry-run is read-only), and surface
`ErrAlreadyLocked` with an actionable hint instead of silently
racing. A new package-level test (`lock_acquire_test.go`) pins down
the acquire/release/`ErrAlreadyLocked` contract the wire-up depends
on and asserts that `LockPath()` always lives under `DataDir()` —
so a future refactor that splits them cannot quietly break the
filesystem-permission enforcement story.

Fixes an incidental doc bug: README claimed the lock lives at
`~/.nodeup/nodeup.lock`, but the real path is `<DataDir>/nodeup.lock`
(Linux XDG, macOS `~/Library/Application Support/...`, Windows
`%APPDATA%\...`). The README now spells out the platform resolution
explicitly.

Refs #44

Co-Authored-By: puku-ai-2.8 <noreply@puku.sh>
@cocogitto-bot

cocogitto-bot Bot commented Jul 3, 2026

Copy link
Copy Markdown

✔️ 9d75644 - Conventional commits check succeeded.

@dipto0321 dipto0321 merged commit 087179e into main Jul 3, 2026
10 checks passed
@dipto0321 dipto0321 deleted the fix/platform/wire-lock-into-cli-config branch July 3, 2026 02:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(platform): lock file implemented but never wired into upgrade/config — concurrent runs not actually blocked

1 participant