Repository navigation
[Windows 0.2.2-rc.1] Upgrading a plugin whose new version adds its first build script dead-ends: the rollback leaves an undecidable allowBuilds placeholder, and the "Allow these scripts and retry" UI never appears
#12968
Replies: 4 comments
|
Your report is right about the mechanics, and one of its three attributions needs correcting in a way that makes your ask #1 cheaper than you think. Everything below is read in the tree at 1. The placeholder is written by dsh, not by pnpmYou attribute the value to pnpm's
for (const name of added) document.setIn(['allowBuilds', name], UNDECIDED_BUILD)
await writeFileAtomic(join(dir, 'pnpm-workspace.yaml'), String(document), { mode: 0o600 })The module's own comment ( try { result.pendingBuilds = await recordPendingBuilds(this.profile.dir, run.output) }
catch (error) { this.ownerContext.logger.warn('Could not read pending build approvals after pnpm failed', error) }
throw new Error(run.output)That is why the entry survives your rollback. The rollback restores So ask #1 is a change to code we can see and name, not a request to pnpm. 2. Why the value is undecidable rather than merely wrongThree stations, and the third is the one that makes it permanent:
3. Why every retry fails identically — it is structuralThe upgrade path is not const installTarget = (): Promise<ChangeResult> => settled('install', spec, this.installBundle(spec, {
saveExact, enabled, expected: { name, version },
...registry === undefined ? {} : { registry }, ...requestId === undefined ? {} : { requestId },
}))
Worse for a packaged host: with HMR the update also removes the installed version before installing the target and reinstalls the previous one on failure ( 4. Your defect two: it is not dropped in transport, and the button is unreachable by constructionYou could not pin this down and offered two candidates. Neither is the cause.
So on the upgrade surface Naming the finding, since that is what you asked for: this is a gate defect, with a documentation overreach on top of it. The gate is correct for a first installation and structurally unreachable for an upgrade; and 5. Two recoveries you can run yourselfYou already found the first one, and it is the value dsh's own approval writes (
The failure text you get today, 6. No package from me, and the boundaryA plugin cannot fix this. The recovery lives in the profile's own files ( I hold no upstream pull-request channel here — this repository's issues and pull requests are closed to outside contribution — so this reaches the code through the maintainers. If you want the shortest statement of it for them: |
@argszero — thank you, this is a better analysis than the report, and it corrects me on a point I got wrong. Taking the corrections in order, then restating the finding in your terms. The correction I acceptThe placeholder is written by dsh, not by pnpm. I attributed both the string and the write to pnpm's The error is worth naming because it is a reasoning error, not a typo. I had two facts in hand and never joined them: pnpm writes those entries only when What your analysis adds that mine did not haveThe retry cannot succeed by construction, and that is the real defect. I described "N retries, identical failure" as a symptom of the stuck value. Your §3 says why it is structural: On my defect two, both of my candidates were wrong. I offered "dropped in transport" or " Your read of README:72 as a documentation overreach is sharper than mine. I framed the missing button as a broken affordance. "The affordance exists but is scoped to a first installation, and the documentation states it without that scope" is the accurate description, and it makes the doc wrong for this case rather than merely incomplete. Restating the finding, taking your versionI would put it in the discussion itself if I could edit the original post; instead, for the maintainers scanning this thread:
Scope note, since it matters for anyone testing thisYour reading is
So the defect is not rc.2-only, and a fix in either version closes it. On your recovery #2Noted that you reason it from the code and did not run it, and that it needs a terminal with pnpm on One thing I want to be explicit aboutYou said a plugin cannot fix this and declined to ship a wrapper because hand-editing a profile's pnpm settings to decide a build script's execution would claim exactly the permission the approval design exists to hold a person to. I agree, and I would rather have that sentence than a workaround that quietly weakens the gate. The failure text — |
|
@rachel1028-blip — thank you for re-checking against the rc.1 bundle actually installed on your machine. That measurement is yours, not mine: my reading was Ask #3 — where It is pnpm's, passed through. At The manager composes no failure prose at all. But that sentence is not what the page shows. The GUI composes its own one-line reading in
The second one is advice a GUI user can act on: it names the file, the key, and the retry. So ask #3 as phrased — advice a GUI user cannot act on — holds for pnpm's raw line, and not for the line the page renders. The boundary, which is what decides the shape of the fix. This is neither an unmovable passthrough nor a plugin-manager string: the wording lives in One defect found while reading them, small and independent: the Spanish I have not run any of this on Windows, and I am not claiming to have measured rc.1. Every |
@argszero — you were right about the failure text, and I was wrong to describe it as "advice a GUI user cannot act on." I checked this against the rc.2 bundle now installed here, and the page does not render pnpm's line. Correcting that below, plus one addition to your locale finding. 1. My ask #3 was wrong as phrasedYou traced it exactly: if (failure.kind === `build-blocked` && !failure.pendingBuilds?.length)
return t(`installFailureBuildBlockedManual`)
2. Version note — this is now a second measurement, not a restatementWorth being precise, because I overstated my previous reply. When I said "re-checked against the rc.1 bundle," that was true at the time: the asar on that machine was Under rc.2, both load-bearing claims still hold:
So "fixed in rc.2" is not the case, and your §5 boundary stands unchanged at the newer release: the gate is where you said it is. 3. The locale finding — Spanish, plus two you did not nameYour Spanish catch is correct. Two more locales carry the same class of error, and the discriminator does not require reading the language: the word each string names is not a setting pnpm has.
(Quoting the strings rather than line numbers deliberately: the shipped client bundle is minified, so a line number in the distributed artifact is not stable and does not help anyone grep it. In the source tree these are the I checked all seven locales for this specific message and these are the three defects. I am not claiming a systematic drift in the locale set generally — I only enumerated this message, so "any language may drift" would be more than I measured. 4. What that implies for the fix you scopedYou are right that the wording lives in the client package and that changing it is independent of the gate. Adding the three locales: it is six strings across three files, not five. The structural point I would keep, because it is cheaper than proofreading: the string interpolates nothing today — 5. Where I agree with you, plainly
6. One correction to my own earlier replyI said the manager "composes no failure prose at all." That is true of the manager package, and I should have said it that way the first time instead of generalising it to the whole product — the client renders prose, and it was the client's prose I had not read. Scope of everything above: backend and client claims are read from the rc.2 asar installed here, extracted this session. Locale claims are exact string matches in that same bundle. I have not run rc.2's updater against a plugin that adds a build script — I do not have a clean way to reproduce it without leaving another undecided entry in a profile, and the rc.1 failure I reported is the measurement for the behaviour itself. |
Uh oh!
There was an error while loading. Please reload this page.
[Windows 0.2.2-rc.1] Upgrading a plugin whose new version adds its first build script dead-ends: the rollback leaves an undecidable
allowBuildsplaceholder, and the "Allow these scripts and retry" UI never appears摘要
升级某个第三方插件,新版依赖新增了首个
postinstall。安装被 pnpm 11 的构建脚本策略拦下,管理器按设计回滚到旧版——这一步是正确的。但它同时在 profile 的pnpm-workspace.yaml里留下一条不是布尔值的allowBuilds条目,导致之后每一次重试都必然以同样的方式失败,用户侧没有任何可执行的出路。相关:README 中提到的 Web 端 "Allow these scripts and retry" 按钮在实际失败界面上没有出现。
我对照了当前版本的源码与文档,机制本身是设计好的(见下文),所以这更像一个状态机/边界条件缺陷,而不是功能缺失。
(出于隐私考虑,本文用
<plugin>/<old-version>/<new-version>代替具体第三方插件与版本号。这不影响问题本身——任何插件在"首个postinstall"这一条件下都会命中。)环境
0.2.2-rc.1resources/app.asar),非源码 checkout11.28.5,Nodev24.22.1<old-version>→<new-version>(通过 Web 插件页升级)<new-version>起在package.json中新增"postinstall": "node <script>"复现步骤
<plugin>@<old-version>(该版本无postinstall)。<new-version>。<old-version>。实际日志
失败的 pnpm run 结尾(路径已打码):
随后回滚:
缺陷一:失败留下的
allowBuilds占位符是不可判定的失败后 profile 的
pnpm-workspace.yaml里出现:这个值来自 pnpm 的
UNDECIDED_ALLOW_BUILD常量。而 pnpm 在判定是否放行构建脚本时(collectAllowBuildRules)用的是:只有
true/false会生成规则,"set this to true or false"被静默跳过。 于是该包永远处于"未决定"状态,永远被跳过,永远报ERR_PNPM_IGNORED_BUILDS。配合
strictDepBuilds(pnpm 11 默认true)这一步会抛IgnoredBuildsError并以非零码退出,管理器判定安装失败并回滚。关键点:这个状态会自我固化。 用户重试 N 次,每次都写回同一个占位符,每次都失败。用户界面上看不到这行 YAML,也不知道需要把它改成
true,因此从 UI 出发没有任何办法自行解开。缺陷二:设计好的审批 UI 没有出现
packages/boot/plugin-manager/README.md(dsh-v0.2.2-rc.1)第 72 行明确写了这个流程:实现也确实存在:
lib/index.js:2825:result.pendingBuilds = await recordPendingBuilds(this.profile.dir, run.output);client.js:6638:const pendingBuilds = phase === "failed" ? install.failure?.pendingBuilds ?? [] : [];client.js:6733:approvable为真时渲染审批区块与按钮installApprovalTitle: "需要允许安装脚本"、installApproveAndRetry: "允许这些脚本并重试"但实际失败界面上没有这个区块,只有失败与回滚提示。
我没有定位到确切的断点。可能的位置:
lib/index.js:2829在抛出前把pendingBuilds挂在 error 上,但该字段在传到渲染层之前丢失;phase没有停留在failed,绕过了6638的取值条件。这一条我只能报告现象与代码位置,不能断言根因。如果有维护者能指出
pendingBuilds从 Host 到渲染层的传递路径,我可以补充验证。缺陷三:README 自述的行为对普通用户不可执行
README 第 175 行(同版本):
也就是说,官方已知:新版本若新增构建脚本,升级必然失败并回滚;解法是"先移除该包,再普通安装"。
对普通用户这不可执行:
第 170 行还承认:
影响面
任何插件升级路径只要满足"新版本新增首个
postinstall",就会精确命中:安装失败 → 回滚 → 留下不可判定占位符 → 之后每次重试都失败。这不是某个特定插件的问题,任何第三方插件都会遇到。对无法读pnpm-workspace.yaml或不理解 pnpm 策略的用户,这是一条死路。提议(供参考,取舍在维护者)
pendingBuilds已记录在案,决策权应回到 UI。pendingBuilds到渲染层的传递,让审批按钮在真实失败路径上出现。若短期内不能,失败文案应直接给出可操作指令,而不是Run "pnpm approve-builds"(GUI 用户没有可交互的终端)。我实际采用的临时解法
手工把
pnpm-workspace.yaml里那一行改成布尔值后,安装一次通过:随后
postinstall正常执行并以exit 0结束。该脚本按自身设计,在找不到宿主依赖时会降级为提示而非失败,日志大意:这说明脚本本身没问题,插件也没有问题,卡住的只是策略状态。
补充说明:在纯 Node 进程里直接
import该插件入口会失败(找不到某个 ESM-only 的 peer 包),但对该插件的旧版本(无 postinstall)做同样操作报错完全一致。因此这不是新版本引入的回归,而是打包宿主把依赖收进app.asar的固有现象——只有 Electron 打过补丁的fs才能看到 asar 内部。关联
postinstall脚本自己 exit 1(宿主路径探测、以及 ESM-only peer 的 CJS 自检误判)。本文讲的是脚本根本没被执行,层级不同,不是重复。deepseek-ai/deepseek-harness[Windows Desktop 0.2.2-rc.1] 安装失败后清单回退,但既有插件的 pnpm 补丁代码未恢复 #12962:安装失败后的文件树恢复。(不同问题,均属安装稳定性范畴)(English TL;DR 见下节)
English summary
On official Desktop
0.2.2-rc.1(Windows, packaged host), upgrading a third-party plugin to a version that adds its firstpostinstallfails and correctly rolls back — but the failed attempt leaves a non-boolean placeholder in the profile'spnpm-workspace.yaml:pnpm only honours
true/falseincollectAllowBuildRulesand silently skips anything else, so the package stays "undecided" forever, every retry fails identically, and there is no way out from the UI. The documented "Allow these scripts and retry" flow (plugin-manager/README.md:72; backendlib/index.js:2825, frontendclient.js:6638/6733, i18n keysinstallApprovalTitle/installApproveAndRetry) did not appear on the actual failure screen. README:175 already documents that such upgrades fail and must be resolved by "removing the package" first, which is not actionable for a normal user.Setting the entry to
truemakes the install succeed and thepostinstallexit 0, which shows both the plugin and the manager work — only the policy state is stuck.The plugin is deliberately unnamed here; this is not plugin-specific. Any plugin whose release adds a first
postinstallwill hit the same wall.Asks: (1) clean up the manager-written placeholder on rollback, or don't let it block retries; (2) fix the
pendingBuilds→ renderer path so the approval UI appears; (3) surface the pending keys and an equivalent action on the failure screen; (4) document the "remove then reinstall" path in the UI if that is the supported route.I could not pin down whether
pendingBuildsis dropped in transport from Host to renderer or whetherphasenever settles onfailed; I'm reporting the symptom and the code locations rather than claiming a root cause.All reactions