Skip to content

fix: support pnpm self-update#1698

Open
xiawiie wants to merge 2 commits into
larksuite:mainfrom
xiawiie:fix/pnpm-self-update
Open

fix: support pnpm self-update#1698
xiawiie wants to merge 2 commits into
larksuite:mainfrom
xiawiie:fix/pnpm-self-update

Conversation

@xiawiie

@xiawiie xiawiie commented Jul 1, 2026

Copy link
Copy Markdown

Summary

Fixes #1681 by recognizing pnpm-managed installations in lark-cli update and using pnpm for automatic self-update instead of falling back to manual/npm-only update guidance.

Changes

  • Added pnpm as a first-class self-update install method, including detection for .pnpm global paths and pnpm command shims.
  • Tracked pnpm availability separately from npm availability so --check and update decisions report the correct auto_update state.
  • Added RunPnpmInstall, which runs pnpm add -g @larksuite/cli@<version> when the CLI was installed through pnpm.
  • Generalized the existing npm update flow so npm and pnpm both preserve self-replace preparation, rollback, binary verification, skills sync, JSON envelopes, and human-readable output.
  • Updated help text, manual fallback guidance, permission hints, and verification failure reinstall hints to show pnpm-specific commands when pnpm is the detected installer.
  • Added focused unit coverage for pnpm path detection, missing-pnpm fallback, pnpm shim detection, pnpm install invocation, JSON/human update output, --check, and manual install hints.

Test Plan

  • go test ./internal/selfupdate ./cmd/update
  • go vet ./...
  • gofmt -l .
  • go mod tidy followed by git diff -- go.mod go.sum with no diff
  • go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6 run --new-from-rev=origin/main (0 issues; existing config warning only)
  • make script-test
  • make examples-build
  • Dry-run/local E2E subset: go test -v -count=1 ./tests/cli_e2e/... -run 'DryRun|StdinRegression|ResolveBinaryPath|BuildArgs|SkipWithoutUserToken|EventConsumeUnknownKeyRegression|EventSubscribeInvalidRouteRegression|GitCredentialListLocal|GitCredentialRemoveLocal'
  • make unit-test was attempted. It did not complete green for environment/unrelated reasons outside this change:
    • internal/event/consume failed once with dial tcp 127.0.0.1:<port>: connect: can't assign requested address; rerunning go test -race -gcflags="all=-N -l" -count=1 ./internal/event/consume passed.
    • shortcuts/minutes fails because this machine resolves example.com to 198.18.4.33, so URL safety rejects fixture download URLs as local/internal. Confirmed with dscacheutil -q host -a name example.com and Python socket.getaddrinfo('example.com', 443).
  • make integration-test was attempted. The build step succeeded and dry-run/local tests passed, but live E2E is blocked by the current test credentials/scopes, including missing base:app:create, drive:drive, space:folder:create, wiki:*, and user IM scopes such as im:chat:create_by_user / im:feed.shortcut:*.

Related Issues

Summary by CodeRabbit

  • New Features

    • The update command now supports pnpm-based installations alongside npm, with auto-update and manual-update guidance tailored to the detected package manager.
    • Update messages and verification prompts now show package-manager-specific install and reinstall commands (including pnpm-specific skills-sync and permission guidance).
  • Bug Fixes

    • Improved update detection for pnpm installs, including shim-based setups and cases where pnpm is missing from the environment.
    • Failure and permission messaging is now more accurate and manager-aware (including Linux vs Windows behavior), with clearer manual fallback when auto-update isn’t available.

@xiawiie xiawiie requested a review from liangshuo-1 as a code owner July 1, 2026 05:52
Copilot AI review requested due to automatic review settings July 1, 2026 05:52
@github-actions github-actions Bot added the size/L Large or sensitive change across domains or core paths label Jul 1, 2026
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 49476863-f7a2-44c9-a5b7-184a4ac67f92

📥 Commits

Reviewing files that changed from the base of the PR and between fbc05dd and f4f62dd.

📒 Files selected for processing (4)
  • cmd/update/update.go
  • cmd/update/update_test.go
  • internal/selfupdate/updater.go
  • internal/selfupdate/updater_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/selfupdate/updater.go
  • cmd/update/update.go

📝 Walkthrough

Walkthrough

This PR adds pnpm support to self-update detection and execution. internal/selfupdate/updater.go classifies installs as npm, pnpm, or manual, adds pnpm install execution, and extends update status/permission handling. cmd/update/update.go routes pnpm updates through manager-aware logic, with matching tests in both packages.

Changes

pnpm-aware self-update

Layer / File(s) Summary
Detection contract and helpers
internal/selfupdate/updater.go
Adds InstallPnpm, extends DetectResult, updates auto-update/manual reasoning, adds pnpm-specific detection, and exposes RunPnpmInstall plus testing hooks.
Detection and pnpm install tests
internal/selfupdate/updater_test.go
Covers pnpm layouts, shim detection, npm detection, pnpm install arguments, and typed errors when pnpm is unavailable.
Update command dispatch and shared logic
cmd/update/update.go
Adds pnpm help text, dispatches pnpm updates explicitly, and centralizes manager-aware install, verification, and hint formatting.
Update command pnpm tests
cmd/update/update_test.go
Adds pnpm success, failure, check-mode, fallback, and manual-hint coverage for the update command.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Possibly related issues

Possibly related PRs

  • larksuite/cli#391: Touches the same update flow and install detection logic that this PR extends with pnpm support.
  • larksuite/cli#884: Also changes update/rollback hint output in cmd/update/update.go, which this PR makes package-manager aware.
  • larksuite/cli#723: Overlaps with the shared update flow and skills-sync reporting paths modified here.

Suggested labels: feature

Suggested reviewers: liangshuo-1, MaxHuang22

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and clearly summarizes the main change: adding pnpm support to self-update.
Description check ✅ Passed The description follows the template with Summary, Changes, Test Plan, and Related Issues filled in.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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.

Copilot AI 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.

Pull request overview

This PR fixes lark-cli update misclassifying pnpm-managed installations by adding pnpm-aware install detection and a pnpm-based self-update path, keeping the existing self-replace/verify/skills-sync flow consistent across package managers.

Changes:

  • Added pnpm as a first-class install method in self-update detection (including .pnpm path detection and pnpm shim heuristics).
  • Implemented a pnpm update runner (pnpm add -g @larksuite/cli@<version>) and routed update to it when pnpm is detected.
  • Expanded unit tests to cover pnpm detection, --check auto-update state, pnpm install invocation, and pnpm-specific messaging.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
internal/selfupdate/updater.go Adds pnpm detection and a pnpm install runner for self-update.
internal/selfupdate/updater_test.go Adds unit tests for pnpm path + shim detection and pnpm install args.
cmd/update/update.go Routes pnpm installs to a pnpm update path and generalizes package-manager update messaging.
cmd/update/update_test.go Adds update/check tests for pnpm flows and pnpm-specific hints.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread cmd/update/update.go
Comment on lines +335 to +341
func manualInstallHint(method selfupdate.InstallMethod, latest string) string {
manager := "npm"
if method == selfupdate.InstallPnpm {
manager = "pnpm"
}
return fmt.Sprintf("\nOr install via %s (note: skills will not be synced):\n %s\n npx skills add larksuite/cli -y -g # sync skills separately\n", manager, reinstallCommand(manager, latest))
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in f4f62dd. pnpm manual fallback now uses pnpm dlx skills add larksuite/cli -y -g, while npm keeps npx skills add ....

Comment thread cmd/update/update.go
Comment on lines 351 to +355
if updater.CanRestorePreviousVersion() {
return "the previous version has been restored"
}
return fmt.Sprintf("automatic rollback is unavailable on this platform; reinstall manually (skills will not be synced): npm install -g %s@%s && npx skills add larksuite/cli -y -g, or download %s", selfupdate.NpmPackage, latest, releaseURL(latest))
return fmt.Sprintf("automatic rollback is unavailable on this platform; reinstall manually (skills will not be synced): %s && npx skills add larksuite/cli -y -g, or download %s", reinstall, releaseURL(latest))
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in f4f62dd. Verification failure hints now take the detected package manager and use pnpm dlx skills add ... for pnpm rollback/manual recovery guidance.

Comment thread cmd/update/update_test.go
Comment on lines +910 to +923
func TestManualInstallHintUsesDetectedPackageManager(t *testing.T) {
pnpmHint := manualInstallHint(selfupdate.InstallPnpm, "2.0.0")
if !strings.Contains(pnpmHint, "pnpm add -g @larksuite/cli@2.0.0") {
t.Fatalf("pnpm manual hint = %q, want pnpm add command", pnpmHint)
}
if strings.Contains(pnpmHint, "npm install -g") {
t.Fatalf("pnpm manual hint should not suggest npm install: %q", pnpmHint)
}

npmHint := manualInstallHint(selfupdate.InstallNpm, "2.0.0")
if !strings.Contains(npmHint, "npm install -g @larksuite/cli@2.0.0") {
t.Fatalf("npm manual hint = %q, want npm install command", npmHint)
}
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in f4f62dd. The manual hint test now asserts manager-specific skills sync commands and verifies pnpm hints do not suggest npm/npx.

@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: 4

🧹 Nitpick comments (2)
internal/selfupdate/updater_test.go (1)

218-232: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert argv boundaries, not only the joined command string.

"$*" flattens all arguments, so a broken implementation that passes "add -g @larksuite/cli@2.0.0" as one argv element can still produce the same log. Log each arg on its own line and compare the exact slice.

Proposed test tightening
-	if err := os.WriteFile(script, []byte("#!/bin/sh\nprintf '%s\\n' \"$*\" >> \""+logPath+"\"\nexit 0\n"), 0o755); err != nil {
+	if err := os.WriteFile(script, []byte("#!/bin/sh\nfor arg do printf '%s\\n' \"$arg\"; done >> \""+logPath+"\"\nexit 0\n"), 0o755); err != nil {
 		t.Fatal(err)
 	}
@@
-	if got, want := strings.TrimSpace(string(raw)), "add -g `@larksuite/cli`@2.0.0"; got != want {
+	if got, want := strings.Split(strings.TrimSpace(string(raw)), "\n"), []string{"add", "-g", "`@larksuite/cli`@2.0.0"}; !reflect.DeepEqual(got, want) {
 		t.Fatalf("args = %q, want %q", got, want)
 	}
🤖 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/selfupdate/updater_test.go` around lines 218 - 232, The
RunPnpmInstall test is only checking a flattened command string, so it can miss
argv boundary bugs. Update the test around New().RunPnpmInstall to log each
argument separately from the script and assert the exact argument slice, using
the RunPnpmInstall symbol and the script fixture setup to verify “add”, “-g”,
and “@larksuite/cli@2.0.0” are passed as distinct argv elements.
cmd/update/update.go (1)

325-333: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Parameter output shadows the imported output package.

packageManagerPermissionHint(manager, output string) names its second parameter output, shadowing the internal/output package alias used throughout this file. Currently harmless since the function body doesn't reference the package, but it's a landmine for anyone extending this function later.

♻️ Suggested rename
-func packageManagerPermissionHint(manager, output string) string {
+func packageManagerPermissionHint(manager, combinedOutput string) string {
 	if manager == "npm" {
-		return permissionHint(output)
+		return permissionHint(combinedOutput)
 	}
-	if manager == "pnpm" && strings.Contains(output, "EACCES") && !isWindows() {
+	if manager == "pnpm" && strings.Contains(combinedOutput, "EACCES") && !isWindows() {
 		return "Permission denied. Check pnpm global directory permissions or run: pnpm setup"
 	}
 	return ""
 }
🤖 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 `@cmd/update/update.go` around lines 325 - 333, The
`packageManagerPermissionHint` function currently uses `output` as a parameter
name, which shadows the imported `internal/output` package alias in this file.
Rename that parameter to something non-conflicting, and update the
`permissionHint` call site inside `packageManagerPermissionHint` accordingly;
use the function name to locate the change.
🤖 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 `@cmd/update/update_test.go`:
- Around line 910-923: The current tests only cover npm and pnpm success hints,
so add coverage for the missing manual/other path and pnpm-specific failure
branches. Extend the tests around manualInstallHint, doManualUpdate,
packageManagerPermissionHint, and doPackageManagerUpdate to verify that a
non-npm/non-pnpm detect.Method produces the true manual install guidance instead
of defaulting to npm, that pnpm EACCES returns the correct permission hint, and
that pnpm install/verification failures are exercised as explicit failure cases.
Use the existing TestManualInstallHintUsesDetectedPackageManager as the starting
point and add focused assertions for the new branches.

In `@cmd/update/update.go`:
- Around line 335-341: manualInstallHint currently defaults every non-pnpm case
to npm, which misleads the genuine manual/binary-install path. Update
manualInstallHint to distinguish selfupdate.InstallNpm from the manual/other
InstallMethod used by doManualUpdate/DetectResult.ManualReason, and only emit
npm/pnpm package-manager commands when the method actually matches those
managers. For the non-npm/non-pnpm case, return guidance that does not suggest
npm install -g, and use the existing manual-install flow in update.go to keep
the message consistent with the GitHub Releases download instructions.

In `@internal/selfupdate/updater.go`:
- Around line 229-242: The pnpm runner in updater.go is returning plain
fmt.Errorf errors, which drops the typed category/subtype metadata expected by
the update command. Update the error paths in the pnpm install flow around the
LookPath handling and the timeout check in the runner to use the appropriate
errs.* constructors, and attach the underlying failures with .WithCause(...).
Preserve both the missing-pnpm failure and the context deadline timeout as typed
command-facing errors, with the timeout path carrying context.DeadlineExceeded
as its cause.
- Around line 164-172: In detectInstallMethod, the pnpm-specific path check is
too narrow and the generic "/node_modules/" branch can misclassify pnpm global
shim targets as npm. Update the logic so detectInstallMethod recognizes pnpm
global target shapes like the one covered in updater_test before falling back to
InstallNpm, keeping the pnpm check ahead of the generic node_modules match and
extending it to cover the resolved global target path pattern used by pnpm.

---

Nitpick comments:
In `@cmd/update/update.go`:
- Around line 325-333: The `packageManagerPermissionHint` function currently
uses `output` as a parameter name, which shadows the imported `internal/output`
package alias in this file. Rename that parameter to something non-conflicting,
and update the `permissionHint` call site inside `packageManagerPermissionHint`
accordingly; use the function name to locate the change.

In `@internal/selfupdate/updater_test.go`:
- Around line 218-232: The RunPnpmInstall test is only checking a flattened
command string, so it can miss argv boundary bugs. Update the test around
New().RunPnpmInstall to log each argument separately from the script and assert
the exact argument slice, using the RunPnpmInstall symbol and the script fixture
setup to verify “add”, “-g”, and “@larksuite/cli@2.0.0” are passed as distinct
argv elements.
🪄 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: 15970cf7-f9d4-4446-bf38-1333cab7747e

📥 Commits

Reviewing files that changed from the base of the PR and between ec6fdc9 and fbc05dd.

📒 Files selected for processing (4)
  • cmd/update/update.go
  • cmd/update/update_test.go
  • internal/selfupdate/updater.go
  • internal/selfupdate/updater_test.go

Comment thread cmd/update/update_test.go
Comment thread cmd/update/update.go
Comment thread internal/selfupdate/updater.go
Comment thread internal/selfupdate/updater.go
@CLAassistant

CLAassistant commented Jul 1, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@xiawiie

xiawiie commented Jul 1, 2026

Copy link
Copy Markdown
Author

Addressed the latest review feedback in f4f62dd.

Changes made:

  • Use pnpm dlx skills add larksuite/cli -y -g for pnpm manual/verification recovery hints, while npm keeps npx skills add ....
  • Stop suggesting npm/pnpm commands for the true manual/other install path; that path now relies on the existing GitHub Releases guidance.
  • Detect pnpm global target paths before the generic /node_modules/ npm fallback.
  • Return typed errors from the pnpm runner for missing pnpm and pnpm timeout paths.
  • Rename the shadowing output parameter to combinedOutput.
  • Tighten the pnpm install argv test to assert argument boundaries.
  • Add regression coverage for manual/other hints, pnpm EACCES hints, pnpm install failure, and pnpm verification failure hints.

Verification:

  • go test ./internal/selfupdate ./cmd/update
  • go test -race -gcflags="all=-N -l" -count=1 ./internal/selfupdate ./cmd/update
  • gofmt -l .
  • go vet ./...
  • go mod tidy with no go.mod / go.sum diff
  • go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6 run --new-from-rev=origin/main (0 issues; existing gocritic config warning only)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/L Large or sensitive change across domains or core paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lark-cli update incorrectly detects pnpm-installed CLI as npm-installed

3 participants