diff --git a/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.i18n.yaml b/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.i18n.yaml index 632b8b3d0d..61728f400d 100644 --- a/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.i18n.yaml +++ b/.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: 7efe5c9a1c3aa30d4c0a52aa5983b61a01f514cd -2026-08-10-npm-release-sequences.zh.md: cca853bae3f0b19dbf50aca0f2cf32aa573293ef +2026-08-10-npm-release-sequences.md: d51054b90aa0acd82d252cdb6e97dc1f3c0e51b5 +2026-08-10-npm-release-sequences.zh.md: 8c2b7b048af407a79f62c5842f0bd04790a61c9a diff --git a/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.md b/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.md index 7efe5c9a1c..d51054b90a 100644 --- a/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.md +++ b/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.md @@ -101,7 +101,7 @@ 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. +The verification also packs the Landlock entry, which `dsh-sandbox-local` declares as a plain dependency, and omits optional dependencies. The platform packages behind those optional entries need 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. The verification therefore reads a directory by its contents rather than a pack order, because a directory can hold tarballs packed only to satisfy a cross-sequence dependency. ### Repository changes this carried diff --git a/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.zh.md b/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.zh.md index cca853bae3..8c2b7b048a 100644 --- a/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.zh.md +++ b/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.zh.md @@ -101,7 +101,7 @@ dsh 族套用仓库的发布 payload 策略(拒绝源码与声明映射)。v dsh 的验证会一并安装 vendored 族的 pack 产物。harness 的包把 vendored 框架声明成 peer,而那些包属于另一条序列,无凭据的 job 无法从私有 registry 取到——所以 `release.yml` 为验证而打包 vendored 族,发布的仍只有自己那一份。 -验证会略去可选依赖。它们背后的 Landlock 平台包属于 native 序列,那条序列的 pack 需要 musl 工具链且每个架构各构建一次,单台 runner 产不出来;而装不到它们的消费方也必须能起——这正是「可选」在这里的含义。 +验证还会打一份 Landlock entry 的 tarball——`dsh-sandbox-local` 把它声明为普通 `dependencies`——同时略去可选依赖。那些可选项背后的平台包需要 musl 工具链且每个架构各构建一次,单台 runner 产不出来;而装不到它们的消费方也必须能起,这正是「可选」在这里的含义。因此验证按目录内容读取 tarball,而不是读发布顺序:一个目录可能只装着为满足跨序列依赖而打出来的包,任何发布顺序都不描述它。 ### 本次带出的仓库改动 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7659b3b5a3..cab178a3d2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -86,8 +86,16 @@ jobs: - name: Pack the vendored framework for verification run: pnpm run release:pack --family vendor --out dist/npm-vendor + # dsh-sandbox-local declares the Landlock entry as a runtime dependency, so + # the verification needs its tarball. Its platform packages stay out: they + # are optional, and building them needs a musl toolchain per architecture. + - name: Pack the Landlock entry for verification + run: | + pnpm --dir native/landlock-run run build:ts + pnpm --dir native/landlock-run/packages/entry pack --pack-destination "$PWD/dist/npm-landlock" + - name: Verify packed install - run: pnpm run release:verify-packed-install --family dsh --from dist/npm --from dist/npm-vendor + run: pnpm run release:verify-packed-install --family dsh --from dist/npm --from dist/npm-vendor --from dist/npm-landlock - uses: actions/upload-artifact@v4 with: diff --git a/scripts/release/verify-packed-install.ts b/scripts/release/verify-packed-install.ts index f4e5c4b07f..29ec7b851e 100644 --- a/scripts/release/verify-packed-install.ts +++ b/scripts/release/verify-packed-install.ts @@ -15,14 +15,14 @@ * checkout cannot stand in for a missing file here. */ -import { mkdtempSync, rmSync, writeFileSync } from 'node:fs' +import { mkdtempSync, readdirSync, rmSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { join, resolve } from 'node:path' import { pathToFileURL } from 'node:url' import { parseArgs } from 'node:util' import { releaseFamily } from './families.ts' import { capture, isEntry } from './process.ts' -import { packedIdentity, readPublishOrder } from './tarball.ts' +import { packedIdentity } from './tarball.ts' /** * Environment for the installed artifact: no host Node hooks, no host DeepSeek @@ -44,13 +44,19 @@ function consumerEnvironment(consumerRoot: string): NodeJS.ProcessEnv { /** * Every packed tarball in the given directories, as `file:` dependency entries. - * @param directories - absolute pack output directories. + * + * The directories are read by their contents rather than a pack order file: a + * directory here can hold tarballs packed only to satisfy a cross-sequence + * dependency, which no release order describes. + * @param directories - absolute directories holding packed tarballs. * @returns Package name to tarball file URL, and the version each carries. */ function packedDependencies(directories: readonly string[]): Map { const dependencies = new Map() for (const directory of directories) { - for (const filename of readPublishOrder(directory)) { + const tarballs = readdirSync(directory).filter(name => name.endsWith('.tgz')).sort() + if (tarballs.length === 0) throw new Error(`${directory} holds no packed tarball`) + for (const filename of tarballs) { const tarball = join(directory, filename) const { name, version } = packedIdentity(tarball) dependencies.set(name, { url: pathToFileURL(tarball).href, version }) @@ -92,10 +98,11 @@ function main(): void { const environment = consumerEnvironment(consumerRoot) console.log(`release verify-packed-install: installing ${String(packed.size)} tarball(s) into ${consumerRoot}`) - // 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. + // Optional dependencies are omitted: the Landlock platform packages behind + // them need a musl toolchain and one build per architecture, and a consumer + // that cannot install them must still start — which is what optional means + // here. Their entry package is a plain dependency of dsh-sandbox-local, so + // its tarball is supplied through --from. capture('npm', ['install', '--no-audit', '--no-fund', '--package-lock=false', '--omit=optional'], { cwd: consumerRoot, env: environment })