Răsfoiți Sursa

fix(release): omit optional dependencies from the packed install

The Landlock platform packages sit behind optionalDependencies, and npm fails
the install on their 404 rather than skipping them: they belong to the native
sequence, whose pack needs a musl toolchain and one build per architecture, so
this job cannot produce them and holds no credentials to fetch them. A consumer
that cannot install them must still start, which is what optional means here.

The release spec also gains block bodies where the lint rule rejects returning a
void expression from an arrow shorthand.
imccyu 1 lună în urmă
părinte
comite
21db3220d6

+ 2 - 2
.agents/notes/implemented/process/2026-08-10-npm-release-sequences.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-10-npm-release-sequences.md
-2026-08-10-npm-release-sequences.md: 23c5f26c7be2b87d1ef2edffdd3f79a87cf1f9a1
-2026-08-10-npm-release-sequences.zh.md: 77b772460d0bf6b66ae6924c7bab1dfe0860b401
+2026-08-10-npm-release-sequences.md: 7efe5c9a1c3aa30d4c0a52aa5983b61a01f514cd
+2026-08-10-npm-release-sequences.zh.md: cca853bae3f0b19dbf50aca0f2cf32aa573293ef

+ 2 - 0
.agents/notes/implemented/process/2026-08-10-npm-release-sequences.md

@@ -101,6 +101,8 @@ The `pack` job walks the whole release set once, packing each member into one di
 
 A dsh verification installs the vendored family's pack output too. The harness packages declare the vendored framework as a peer, those packages live in another sequence, and the credential-free job cannot fetch them from a private registry — so `release.yml` packs the vendored family for verification while publishing only its own set.
 
+The verification omits optional dependencies. The Landlock platform packages behind them belong to the native sequence, whose pack needs a musl toolchain and one build per architecture, so a job on one runner cannot produce them; a consumer that cannot install them must still start, which is what optional means here.
+
 ### Repository changes this carried
 
 | Item | Content |

+ 2 - 0
.agents/notes/implemented/process/2026-08-10-npm-release-sequences.zh.md

@@ -101,6 +101,8 @@ dsh 族套用仓库的发布 payload 策略(拒绝源码与声明映射)。v
 
 dsh 的验证会一并安装 vendored 族的 pack 产物。harness 的包把 vendored 框架声明成 peer,而那些包属于另一条序列,无凭据的 job 无法从私有 registry 取到——所以 `release.yml` 为验证而打包 vendored 族,发布的仍只有自己那一份。
 
+验证会略去可选依赖。它们背后的 Landlock 平台包属于 native 序列,那条序列的 pack 需要 musl 工具链且每个架构各构建一次,单台 runner 产不出来;而装不到它们的消费方也必须能起——这正是「可选」在这里的含义。
+
 ### 本次带出的仓库改动
 
 | 项 | 内容 |

+ 9 - 9
scripts/release/families.spec.ts

@@ -34,8 +34,8 @@ describe('release families', () => {
     const dsh = releaseFamily('dsh')
     const members = [member('apps/cli', '@deepseek-ai/dsh'), { ...member('apps/web', '@deepseek-ai/dsh-frontend'), version: '0.0.2' }]
 
-    expect(() => dsh.verifyVersions(members)).toThrow(/must share one version/)
-    expect(() => dsh.verifyVersions([members[0]!])).not.toThrow()
+    expect(() => { dsh.verifyVersions(members) }).toThrow(/must share one version/)
+    expect(() => { dsh.verifyVersions([members[0]!]) }).not.toThrow()
   })
 
   it('accepts independent vendored versions and rejects an unpublishable one', () => {
@@ -45,8 +45,8 @@ describe('release families', () => {
       { ...member('vendor/cosmokit', '@deepseek-ai/cosmokit'), version: '1.8.2' },
     ]
 
-    expect(() => vendor.verifyVersions(members)).not.toThrow()
-    expect(() => vendor.verifyVersions([{ ...members[0]!, version: 'latest' }])).toThrow(/unpublishable version/)
+    expect(() => { vendor.verifyVersions(members) }).not.toThrow()
+    expect(() => { vendor.verifyVersions([{ ...members[0]!, version: 'latest' }]) }).toThrow(/unpublishable version/)
   })
 
   it('publishes a dependency before its consumer, and orders ties by name', () => {
@@ -71,7 +71,7 @@ describe('release families', () => {
       member('packages/a/right', '@deepseek-ai/dsh-right', { dependencies: { '@deepseek-ai/dsh-left': 'workspace:^' } }),
     ]
 
-    expect(() => dsh.publishOrder(members)).toThrow(/dependency cycle/)
+    expect(() => { dsh.publishOrder(members) }).toThrow(/dependency cycle/)
   })
 
   it('applies the harness payload policy to dsh and keeps upstream payloads for vendored packages', () => {
@@ -80,10 +80,10 @@ describe('release families', () => {
     const harness = member('packages/a/library', '@deepseek-ai/dsh-library')
     const vendored = member('vendor/cordis', '@deepseek-ai/cordis')
 
-    expect(() => dsh.validatePayload(harness, ['package/lib/index.js', 'package/src/index.ts']))
+    expect(() => { dsh.validatePayload(harness, ['package/lib/index.js', 'package/src/index.ts']) })
       .toThrow(/publishes source file/)
-    expect(() => vendor.validatePayload(vendored, ['package/lib/index.js', 'package/src/index.ts'])).not.toThrow()
-    expect(() => vendor.validatePayload(vendored, [])).toThrow(/empty tarball/)
+    expect(() => { vendor.validatePayload(vendored, ['package/lib/index.js', 'package/src/index.ts']) }).not.toThrow()
+    expect(() => { vendor.validatePayload(vendored, []) }).toThrow(/empty tarball/)
   })
 
   it('drives the installed entry only for the family that publishes one', () => {
@@ -92,7 +92,7 @@ describe('release families', () => {
   })
 
   it('rejects an unknown family identifier', () => {
-    expect(() => releaseFamily('native')).toThrow(/unknown release family/)
+    expect(() => { releaseFamily('native') }).toThrow(/unknown release family/)
   })
 })
 

+ 6 - 1
scripts/release/verify-packed-install.ts

@@ -92,7 +92,12 @@ function main(): void {
 
     const environment = consumerEnvironment(consumerRoot)
     console.log(`release verify-packed-install: installing ${String(packed.size)} tarball(s) into ${consumerRoot}`)
-    capture('npm', ['install', '--no-audit', '--no-fund', '--package-lock=false'], { cwd: consumerRoot, env: environment })
+    // Optional dependencies are omitted: the platform packages behind them
+    // belong to the native release sequence, this job holds no credentials for
+    // the private scope, and a consumer that cannot install them must still
+    // start — which is what optional means here.
+    capture('npm', ['install', '--no-audit', '--no-fund', '--package-lock=false', '--omit=optional'],
+      { cwd: consumerRoot, env: environment })
 
     const bin = join(consumerRoot, 'node_modules', ...entry.packageName.split('/'), entry.binPath)
     const version = capture(process.execPath, [bin, '--version'], { cwd: consumerRoot, env: environment })