Skip to content

feat(update): add zero upgrade command to apply self-updates#461

Open
PierrunoYT wants to merge 4 commits into
Gitlawb:mainfrom
PierrunoYT:feat/zero-upgrade-command
Open

feat(update): add zero upgrade command to apply self-updates#461
PierrunoYT wants to merge 4 commits into
Gitlawb:mainfrom
PierrunoYT:feat/zero-upgrade-command

Conversation

@PierrunoYT

@PierrunoYT PierrunoYT commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #459. Extends the existing zero update --check (check-only) with an actual apply/install step:

  • zero update --apply / zero upgrade download, sha256-verify, and install the latest release.
  • npm installs delegate to npm install -g @gitlawb/zero@latest.
  • Standalone installs download the release archive, verify its checksum, extract it (zip-slip guarded), and atomically replace the running binary plus any pre-existing optional sandbox helper binaries.
  • On Windows, the running exe is renamed aside (NTFS allows this even while it's executing) since it can't be overwritten directly; the leftover .old file is cleaned up on a later invocation.

Test plan

  • go build ./..., go vet ./..., gofmt -l clean
  • go test ./internal/update/... ./internal/cli/... passes, including new tests for archive extraction (incl. zip-slip rejection), install-method detection, checksum-mismatch rejection, and CLI wiring (--apply, --check+--apply conflict, upgrade alias)
  • Manual end-to-end on Windows: built the binary, pointed it at a local fake GitHub-releases server, ran zero update --check then zero upgrade, confirmed the binary was downloaded, checksum-verified, and swapped in place

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added an upgrade command that installs available updates by default.
    • Added update --apply to install updates, with improved messaging and richer --json output.
  • Bug Fixes
    • Improved CLI validation and exit codes (disallowing incompatible flag combinations and reporting apply/check failures correctly).
    • Safer update installation: checksum verification, secure archive extraction, and atomic binary replacement (including best-effort handling of optional helpers).
    • Improved concurrent secret creation reliability on Windows to reduce spurious permission errors.
  • Tests
    • Expanded coverage for apply/upgrade behavior, CLI validation, install failure handling, and extraction security.

Extends the existing `zero update --check` (which only reported an
available release) with an actual install step:

- `zero update --apply` / `zero upgrade` download, sha256-verify, and
  install the latest release.
- npm installs delegate to `npm install -g @gitlawb/zero@latest`.
- Standalone installs download the release archive, verify its
  checksum, extract it (zip-slip guarded), and atomically replace the
  running binary plus any pre-existing optional sandbox helpers. On
  Windows, the running exe is renamed aside (NTFS allows this even
  while it's executing) since it can't be overwritten directly; the
  leftover ".old" file is cleaned up on a later invocation.

Closes Gitlawb#459.
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 96eda9cc-3399-4f15-9e2c-65c5aa2e2a32

📥 Commits

Reviewing files that changed from the base of the PR and between 5dc3b92 and e12fbd1.

📒 Files selected for processing (1)
  • internal/oauth/encrypt.go

Walkthrough

This PR adds zero upgrade and zero update --apply to install updates instead of only checking for them. It also adds update application logic for npm and standalone installs, plus archive extraction, install-method detection, platform-specific binary replacement, and a Windows lock retry tweak in OAuth secret creation.

Changes

Update Apply Feature

Layer / File(s) Summary
CLI upgrade command and --apply flag wiring
internal/cli/app.go, internal/cli/update.go, internal/cli/app_test.go
Adds the applyUpdate dependency hook, upgrade dispatch, shared update command handling, --apply parsing and validation, help text updates, and CLI tests for apply output, defaulting, rejection cases, and error reporting.
Install method detection
internal/update/installmethod.go, internal/update/installmethod_test.go
Defines InstallMethod and DetectInstallMethod, which classify an executable as npm-managed or standalone by checking marker files and package.json, with tests covering the detection cases.
Archive extraction utilities
internal/update/extract.go, internal/update/extract_test.go
Implements archive extraction for tar.gz and zip files, safe extraction path handling, extracted-file writing, recursive basename lookup, and tests for round-trip extraction and traversal rejection.
Platform-specific binary replacement
internal/update/replace_other.go, internal/update/replace_windows.go
Provides non-Windows and Windows implementations of replaceBinary and CleanupStaleBinary for executable swapping and stale .old cleanup.
Apply implementation and update installation
internal/update/apply.go, internal/update/apply_test.go
Adds Apply, ApplyResult, FormatApply, npm update installation, standalone archive-based installation, file download and copy helpers, and tests for no-op, replacement, warning, and checksum-mismatch cases.

OAuth secret file locking

Layer / File(s) Summary
Lock acquisition retry
internal/oauth/encrypt.go
createSecretFile retries lock-file creation on os.ErrExist and os.ErrPermission, with comments describing the Windows delete-pending case.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • Gitlawb/zero#71: Both PRs modify the Go CLI update command wiring and tests in internal/cli/app.go and internal/cli/app_test.go, extending update handling beyond check-only behavior.
  • Gitlawb/zero#97: Both PRs touch internal/cli/update.go; this PR adds --apply/upgrade handling while that PR forwards extra --check options.

Suggested reviewers: Vasanthdev2004, gnanam1990

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The oauth createSecretFile retry change is unrelated to the update-command objective and appears to be a separate CI fix. Move the internal/oauth fix to a separate PR unless it is required for this update feature's acceptance criteria.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding the zero upgrade command for self-updates.
Linked Issues check ✅ Passed The PR implements the linked request by adding CLI-based update support, including the explicit zero upgrade command.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

The optional-helper assertion in TestApplyStandaloneUpdateReplacesBinary
assumed every non-Windows platform has a linux-style "zero-seccomp"
helper to refresh, but applyStandaloneUpdate correctly ships no optional
helpers on macOS (matching scripts/postinstall.mjs). This made the test
fail on macOS CI. Only assert the helper refresh on linux/windows.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/cli/update.go (1)

61-68: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Reject --target on the apply path
internal/cli/update.go:61-68 passes --target through to the release check, but internal/update/apply.go still chooses the replacement binary name from runtime.GOOS only. That means zero update --apply --target linux-arm64 on a linux-amd64 host can replace the running binary with an incompatible arm64 build. Block --target when applying, or thread the selected target through install as well.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/cli/update.go` around lines 61 - 68, Reject --target on the apply
path by updating the update CLI flow in internal/cli/update.go so the --apply
branch does not accept or forward a target into release selection; instead
return a usage/error when options.target is set with apply, or pass the resolved
target through the apply/install path consistently. Check the update command
handler and the apply logic in internal/update/apply.go so the binary
replacement name is not derived from runtime.GOOS alone when a target was
requested.
🧹 Nitpick comments (3)
internal/update/apply.go (2)

162-172: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Optional helper refresh failures are silently dropped.

_ = installBinary(source, destPath) discards any error when refreshing an existing sandbox helper (e.g. seccomp binary). A failed refresh leaves a stale helper in place with no indication to the user that the upgrade was incomplete — degrading the sandbox's security guarantees without any signal.

♻️ Proposed fix
 		destPath := filepath.Join(targetDir, name)
 		if _, err := os.Stat(destPath); err != nil {
 			continue // only refresh helpers this install already has
 		}
-		_ = installBinary(source, destPath)
+		if err := installBinary(source, destPath); err != nil {
+			fmt.Fprintf(os.Stderr, "[zero] warning: failed to update helper %s: %v\n", name, err)
+		}
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/update/apply.go` around lines 162 - 172, The optional helper refresh
loop in apply.go is ignoring installBinary errors, so an existing sandbox helper
can remain stale without any signal. Update the installBinary(source, destPath)
call in the optionalBinaries refresh path to handle and propagate/log the error
instead of discarding it, using the surrounding apply/update flow to surface a
failed helper refresh to the user while keeping the optional-binary fallback
behavior intact.

45-56: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Move CleanupStaleBinary before the "already up to date" early return.

CleanupStaleBinary only runs when an update is actually applied (Line 55, after the Line 41-43 early return). If the binary is already current, a leftover Windows .old file from a prior upgrade never gets cleaned until the next real upgrade — which could be a long time or never for a user who stops upgrading.

♻️ Proposed fix
 	checkResult, err := Check(ctx, options)
 	if err != nil {
 		return ApplyResult{}, err
 	}
+	if executablePath, err := os.Executable(); err == nil {
+		if resolved, err := filepath.EvalSymlinks(executablePath); err == nil {
+			executablePath = resolved
+		}
+		CleanupStaleBinary(executablePath)
+	}
 	if !checkResult.UpdateAvailable {
 		return ApplyResult{Result: checkResult, Message: "already up to date"}, nil
 	}

 	executablePath, err := os.Executable()
-	if err != nil {
-		return ApplyResult{}, fmt.Errorf("resolve current executable: %w", err)
-	}
-	if resolved, err := filepath.EvalSymlinks(executablePath); err == nil {
-		executablePath = resolved
-	}
-	// Best-effort: remove a "<binary>.old" left behind by a previous Windows
-	// replaceBinary call now that enough time (a whole separate invocation)
-	// has passed for the old process to have released the file.
-	CleanupStaleBinary(executablePath)
+	if err != nil {
+		return ApplyResult{}, fmt.Errorf("resolve current executable: %w", err)
+	}
+	if resolved, err := filepath.EvalSymlinks(executablePath); err == nil {
+		executablePath = resolved
+	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/update/apply.go` around lines 45 - 56, Move the CleanupStaleBinary
call in Apply so it runs before the “already up to date” early return path, not
only after a successful update. Use the executablePath resolution logic in Apply
to clean up the stale "<binary>.old" file as soon as the current binary path is
known, while keeping the existing best-effort behavior and error handling
unchanged.
internal/cli/app_test.go (1)

1270-1389: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

LGTM!

New tests reasonably cover apply text/JSON output, upgrade defaulting to apply, --check+--apply rejection, and apply error propagation.

Given the --target+--apply concern raised in internal/cli/update.go, consider adding a regression test for that combination once addressed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/cli/app_test.go` around lines 1270 - 1389, Add a regression test in
TestRunUpdateApplyTextAndJSON or a new test around runWithDeps that covers the
--target and --apply combination mentioned in update.go. The issue is that the
current tests verify --apply, --check/--apply, and error handling, but do not
assert the behavior when a target version is provided with --apply. Add a case
using update.Options through appDeps.applyUpdate to ensure the CLI either
rejects or correctly handles --target with --apply, and verify the expected exit
code and stderr/output.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/update/apply.go`:
- Around line 214-243: The download path in downloadFile currently uses the
caller’s context directly, so zero upgrade can hang indefinitely if the archive
or checksum endpoint stalls. Add a bounded timeout for the apply download flow
by creating a timeout-aware context in update.Apply or by wrapping the request
context inside downloadFile, and make sure the timeout is applied consistently
to both fetches. Use the existing downloadFile helper and update.Apply
entrypoint to locate the change, and preserve normal error propagation when the
timeout is hit.

In `@internal/update/replace_windows.go`:
- Around line 14-25: The replaceBinary rollback path in replace_windows.go can
silently fail and leave targetPath missing after a failed os.Rename(newPath,
targetPath). Update replaceBinary to capture and check the error from restoring
oldPath back to targetPath, and if that rollback fails, return an error that
includes both the install failure and rollback failure so callers know manual
recovery may be needed. Keep the fix scoped to replaceBinary and its rename
error handling.

---

Outside diff comments:
In `@internal/cli/update.go`:
- Around line 61-68: Reject --target on the apply path by updating the update
CLI flow in internal/cli/update.go so the --apply branch does not accept or
forward a target into release selection; instead return a usage/error when
options.target is set with apply, or pass the resolved target through the
apply/install path consistently. Check the update command handler and the apply
logic in internal/update/apply.go so the binary replacement name is not derived
from runtime.GOOS alone when a target was requested.

---

Nitpick comments:
In `@internal/cli/app_test.go`:
- Around line 1270-1389: Add a regression test in TestRunUpdateApplyTextAndJSON
or a new test around runWithDeps that covers the --target and --apply
combination mentioned in update.go. The issue is that the current tests verify
--apply, --check/--apply, and error handling, but do not assert the behavior
when a target version is provided with --apply. Add a case using update.Options
through appDeps.applyUpdate to ensure the CLI either rejects or correctly
handles --target with --apply, and verify the expected exit code and
stderr/output.

In `@internal/update/apply.go`:
- Around line 162-172: The optional helper refresh loop in apply.go is ignoring
installBinary errors, so an existing sandbox helper can remain stale without any
signal. Update the installBinary(source, destPath) call in the optionalBinaries
refresh path to handle and propagate/log the error instead of discarding it,
using the surrounding apply/update flow to surface a failed helper refresh to
the user while keeping the optional-binary fallback behavior intact.
- Around line 45-56: Move the CleanupStaleBinary call in Apply so it runs before
the “already up to date” early return path, not only after a successful update.
Use the executablePath resolution logic in Apply to clean up the stale
"<binary>.old" file as soon as the current binary path is known, while keeping
the existing best-effort behavior and error handling unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 463c2172-e16d-4439-8833-8c0a14b786fb

📥 Commits

Reviewing files that changed from the base of the PR and between 0562e3b and 60f7e62.

📒 Files selected for processing (11)
  • internal/cli/app.go
  • internal/cli/app_test.go
  • internal/cli/update.go
  • internal/update/apply.go
  • internal/update/apply_test.go
  • internal/update/extract.go
  • internal/update/extract_test.go
  • internal/update/installmethod.go
  • internal/update/installmethod_test.go
  • internal/update/replace_other.go
  • internal/update/replace_windows.go

Comment thread internal/update/apply.go
Comment thread internal/update/replace_windows.go
- Reject --target when combined with --apply/upgrade: applying installs
  onto the current machine, so a mismatched --target could replace the
  running binary with an incompatible-arch build.
- Bound archive/checksum downloads with a dedicated timeout separate
  from the release-metadata check timeout, so a stalled connection
  can't hang zero upgrade forever.
- Surface a failed rollback in the Windows replaceBinary path instead
  of swallowing it, so a botched install is reported rather than
  silently leaving the binary missing.
- Run CleanupStaleBinary regardless of whether an update is available,
  so a leftover Windows ".old" file isn't stuck waiting for a future
  upgrade that may never come.
- Report optional sandbox-helper refresh failures as warnings on
  ApplyResult (surfaced in text and JSON output) instead of discarding
  them silently.

@Vasanthdev2004 Vasanthdev2004 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve — useful, and the self-update path is genuinely secure

Worth it: yes. A zero upgrade self-update is an oft-requested capability for a CLI shipped as both a standalone binary and an npm package (closes #459), and it doesn't reinvent anything — it extends the existing internal/update Check() pipeline with an apply step. Thanks @PierrunoYT.

I reviewed this primarily as a security change, since it replaces the running executable. It holds up — and I confirmed the load-bearing part by reading the source myself: in applyStandaloneUpdate, the flow is download archive → download checksum → release.VerifySHA256Checksum (which re-hashes the downloaded archive bytes and rejects on mismatch) → only then extract → install. Verification gates the swap; a mismatch returns before anything is extracted or replaced.

  • Atomic + safe replacement: stage a .new sibling on the same filesystem then rename; POSIX renames over the running inode, Windows renames the locked exe aside to .old and rolls back on failure.
  • Downgrade-safe: Apply only proceeds when latest > current, and --target is rejected with --apply, so it can't be steered to an older/arbitrary build.
  • Extraction hardened: safeExtractPath rejects absolute paths and ../ escapes and refuses non-regular entries (symlinks/devices).
  • npm installs correctly delegate to npm install -g @gitlawb/zero@latest.

Verified on Windows (worktree): build/gofmt/vet clean; internal/update + Update|Upgrade cli tests pass, including the checksum-mismatch rejection and zip-slip rejection tests.

Non-blocking residuals (future hardening, don't hold merge):

  1. No cryptographic signature — the checksum shares the archive's origin, so authenticity rests on GitHub + TLS. A detached signature (minisign/cosign) would be the one real authenticity upgrade. Optionally assert https:// on asset URLs so a user-overridden --endpoint can't pull over plaintext.
  2. On Windows the optional helper binaries are also renamed aside to .old, but CleanupStaleBinary only sweeps the main exe's .old, so zero-windows-command-runner.exe.old etc. accumulate. Cosmetic disk leak.

CI: the lone red — Smoke (windows-latest) — failed only on TestLoadOrCreateSecretConcurrentConverges (the known oauth concurrency flake, unrelated; the fix for it is in #445). Touched packages built and tested green in that same run. Approving.

@Vasanthdev2004

Copy link
Copy Markdown
Collaborator

just check and fix the ci test @PierrunoYT

@PierrunoYT

Copy link
Copy Markdown
Contributor Author

just check and fix the ci test @PierrunoYT

I thought the fix is already in another PR?

…ck CI

TestLoadOrCreateSecretConcurrentConverges was failing this PR's Windows
smoke job with "create token secret lock: ...Access is denied." — a
pre-existing flake in internal/oauth, not in the upgrade code. On Windows
a concurrent holder's os.Remove leaves the .lock in a delete-pending
state, so an O_EXCL create races it with ERROR_ACCESS_DENIED
(os.ErrPermission) rather than ErrExist; createSecretFile only treated
ErrExist as contention and hard-failed otherwise.

Treat os.ErrPermission as retryable too, mirroring acquireFileLock in
lock.go. This is the same root-cause fix as Gitlawb#445 (diagnosis by
@gnanam1990); folded in here to make this PR's CI green. Verified on
Windows: the concurrent-converge test passes 30/30.
@Vasanthdev2004

Copy link
Copy Markdown
Collaborator

Pushed a small commit (e12fbd1) to fix the failing Windows CI. The red Smoke (windows-latest) was not your upgrade code — it was the pre-existing internal/oauth flake TestLoadOrCreateSecretConcurrentConverges (create token secret lock: Access is denied), a Windows delete-pending race. I folded in the same root-cause fix as #445 (diagnosis by @gnanam1990) — treat os.ErrPermission as retryable contention in createSecretFile, mirroring acquireFileLock. Verified on Windows: the concurrent-converge test passes 30/30. Heads-up @gnanam1990 / maintainers: this overlaps #445, so once one lands the other's oauth change is redundant (rebase/close).

@gnanam1990 gnanam1990 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: approve

REGRESSION RISK: low

The PR extends the existing internal/update Check() pipeline with an apply step (zero update --apply / zero upgrade). The check-only path is unchanged. The apply path is new code that does not alter any existing code path, config schema, or user flow. The internal/oauth/encrypt.go change (treating os.ErrPermission as contention on Windows) is a separate fix bundled into this PR — it is a 5-line addition to an existing retry branch and does not alter the success path, but it is scope drift relative to the PR's stated purpose (closes #459).

Security review of the self-update path:

  • Checksum verification: release.VerifySHA256Checksum runs before extraction; a mismatch returns an error before any file is written. Test coverage confirms this (TestApplyStandaloneUpdateRejectsChecksumMismatch).
  • Archive extraction: safeExtractPath in extract.go rejects absolute paths, .. traversal, and non-regular/non-dir entry types (symlinks, devices). Both tar.gz and zip paths are covered with path-traversal rejection tests.
  • Binary replacement: POSIX uses atomic os.Rename (same-filesystem). Windows uses rename-aside (target → .old, new → target) with rollback on failure — if the second rename fails, the original is restored; if restoration also fails, the error message includes the .old path so the user can recover manually.
  • Download: uses http.DefaultClient with a 5-minute timeout. No TLS pinning, but this matches the existing Check() pipeline's transport.

BUILD / TEST

  • go build ./... — pass
  • go vet ./internal/update/... ./internal/cli/... — pass
  • gofmt -l on all changed files — clean
  • go test ./internal/update/... -count=1 — all 22 tests pass, including: TestExtractTarGzRejectsPathTraversal, TestExtractZipRejectsPathTraversal, TestApplyStandaloneUpdateReplacesBinary, TestApplyStandaloneUpdateRejectsChecksumMismatch, TestApplyReturnsNoopWhenUpToDate
  • go test ./internal/cli/... -run TestRunUpdate — all 10 tests pass, including: --apply text/JSON output, --check+--apply conflict rejection, --target+--apply rejection, apply error reporting
  • TestApplyStandaloneUpdateWarnsWhenHelperRefreshFails skips on macOS (no optional helpers) — runs on Linux/Windows
  • CI all green including Windows Smoke (4m5s)

CONTRIBUTING

Author is a community contributor (PierrunoYT). The PR links Fixes #459. Issue #459 has a bug label but no issue-approved label. Per CONTRIBUTING.md, the issue-approved gate applies to community PRs. Not met. Additionally, the internal/oauth/encrypt.go change is unrelated to #459 (scope drift). Both are process violations, but the code is reviewed regardless as instructed. Maintainer's call on whether the scope expansion is acceptable.

FINDINGS

  1. Nit — internal/update/replace_windows.go:27-31 — rollback failure error message could recommend manual recovery. If both the new-binary rename and the original-restore rename fail, the error message includes the .old path but does not suggest rename zero.exe.old zero.exe as a recovery step. A user who reads the error on a non-interactive CI runner may not know how to recover. Not a blocker — the path is reported.

  2. Nit — internal/oauth/encrypt.go:114-121 — scope drift. The Windows ERROR_ACCESS_DENIED retry fix for concurrent secret creation is unrelated to the zero upgrade feature. It should ideally be a separate PR. The fix itself is correct (mirrors acquireFileLock in lock.go) and safe, but bundling it here muddies the PR scope.

No security or correctness blockers found.

EXISTING REVIEWS

  • Vasanthdev2004 (APPROVED): Reviewed primarily as a security review — confirmed the self-update path is genuinely secure, with checksum verification, zip-slip protection, and atomic replacement. Valid on the current head. Did not explicitly flag the oauth/encrypt.go scope drift; otherwise thorough.
  • coderabbitai (APPROVED + 2 comments): Flagged (1) --target rejection with --apply as a stability concern — this is already tested (TestRunUpdateRejectsTargetWithApply) and the rejection is intentional; (2) the Windows rollback failure scenario in replace_windows.go — valid concern, addressed as nit #1 above. Both findings are valid but non-blocking.

All reviews on the current head (e12fbd1). No stale reviews.

BOTTOM LINE

A well-tested, security-conscious self-update feature with proper checksum verification and archive extraction guards; safe to merge, with a note on the oauth/encrypt.go scope drift for maintainer awareness.

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.

feat: automatic updates or via cli

3 participants