fix(updater): resolve the dsh CLI through symlinks so one-click updates work on global installs #44
Labels
No labels
area/dsh
area/forgejo
experiment
priority/later
priority/next
priority/now
type/bug
type/task
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
dsh-plugins/dsh-gitea#44
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Symptom
On a normal global DSH install, the in-UI updater sees the newer version (0.8.0) but the install fails. The local panel shows the generic
Plugin update failed; see server logs., and no newoperation-*directory appears under<profile>/.plugin-manager/logs/— i.e.dsh plugin addis never spawned.Root cause
lib/plugin-updater.js#findDshCliEntry()resolves the runningdshentry withresolve(process.cwd(), process.argv[1])and then walks parent directories looking forpackage.jsonwithname: "@deepseek-ai/dsh".Global installs launch DSH through a symlink chain, e.g.
/usr/local/bin/dsh -> /opt/node/bin/dsh -> /opt/node-v24.../lib/node_modules/@deepseek-ai/dsh/lib/bin.js.process.argv[1]is the symlink path,resolve()does not follow it, so the walk starts in/usr/local/binand finds no package.json.cliEntryisundefined,canAutoUpdateis false, and POSTinstallExact()throwsAutomatic update is unavailable in this runtime.before spawning anything.Reproduced on the deployment host (same symlink layout):
The status GET already returns
canAutoUpdate: false, butlib/client.jsignores it and still shows the Update button, so the user only sees a failed click.Fix
findDshCliEntry(): resolve the entry (and the candidate bin) withrealpathSyncbefore walking, so symlinked global installs are found. Keep the direct-path case working.canAutoUpdatefrom the status response; hide/disable the Update button when false and show a manual-install hint (dsh plugin --profile <profile> add <tarball>). A missing CLI is a permanent condition, not a transient error.Acceptance
findDshCliEntry(symlink)returns the reallib/bin.jsfor a fake@deepseek-ai/dshpackage.canAutoUpdate; the UI does not offer an update it cannot perform.node <real bin.js> plugin --profile <name> add <tarball>.Layer
dsh-gitea plugin/tool (updater host + client). Found while testing 0.8.0 on a local install.
Fixed in
e43e4ca(PR #45).Root cause confirmed on the deployment host's global layout:
findDshCliEntry('/usr/local/bin/dsh')walked the symlink path (/usr/local/bin→/usr/local→/) and found nopackage.json, returningundefined. The updater then hadcliEntry === undefined,canAutoUpdate: false, and failed before spawningdsh plugin.Fix: realpath the entry and the candidate
binbefore comparing;findDshCliEntry('/usr/local/bin/dsh')now returns/opt/node-v24.22.0-linux-x64/lib/node_modules/@deepseek-ai/dsh/lib/bin.js.The UI now hides the Update button when
canAutoUpdateis false and shows a manual-install hint; the host error also includes the exactdsh plugin --profile <name> add <tarball>command.Tests: 143/143, leak scan + syntax clean. Released in 0.8.1 — because the currently installed 0.7.40-dsh.7 updater cannot detect the CLI either, 0.8.1 needs one manual tarball bootstrap, then the UI updater works for subsequent releases.