fix(repository-plugin): require published prepare dependency
This commit is contained in:
@@ -115,24 +115,9 @@ describe('RepositoryCache', () => {
|
||||
it('isolates and prepares a .dsh-plugin Git subpath from an enclosing pnpm workspace', { timeout: 60_000 }, async () => {
|
||||
const root = await temporaryRoot('repository-pnpm')
|
||||
const repository = join(root, 'source')
|
||||
const executableDirectory = join(root, 'bin')
|
||||
await mkdir(executableDirectory)
|
||||
await writeFile(join(executableDirectory, 'dsh-plugin-prepare'), [
|
||||
'#!/usr/bin/env node',
|
||||
"const { cpSync, mkdirSync, writeFileSync } = require('node:fs')",
|
||||
"mkdirSync('dsh-plugin-assets/skills', { recursive: true })",
|
||||
"cpSync('../skills', 'dsh-plugin-assets/skills/0', { recursive: true })",
|
||||
"writeFileSync('dsh-plugin.mjs', 'export function apply() {}\\n')",
|
||||
"writeFileSync('prepared.txt', `${process.env.REPOSITORY_TEST_VISIBLE ?? 'absent'}|${process.env.REPOSITORY_TEST_TOKEN ?? 'absent'}\\n`)",
|
||||
'',
|
||||
].join('\n'), { mode: 0o700 })
|
||||
await writeFile(join(executableDirectory, 'dsh-plugin-prepare.cmd'), [
|
||||
'@echo off',
|
||||
'node "%~dp0\\dsh-plugin-prepare" %*',
|
||||
'',
|
||||
].join('\r\n'))
|
||||
await mkdir(join(repository, '.dsh-plugin'), { recursive: true })
|
||||
await mkdir(join(repository, 'build-helper'), { recursive: true })
|
||||
await mkdir(join(repository, 'prepare-helper'), { recursive: true })
|
||||
await mkdir(join(repository, 'skills', 'fixture'), { recursive: true })
|
||||
await writeFile(join(repository, 'package.json'), `${JSON.stringify({
|
||||
name: 'repository-fixture',
|
||||
@@ -160,12 +145,29 @@ describe('RepositoryCache', () => {
|
||||
"require('node:fs').writeFileSync('dependency-built.txt', 'dependency available\\n')",
|
||||
'',
|
||||
].join('\n'), { mode: 0o700 })
|
||||
await writeFile(join(repository, 'prepare-helper', 'package.json'), `${JSON.stringify({
|
||||
name: 'repository-prepare-helper',
|
||||
version: '1.0.0',
|
||||
bin: { 'dsh-plugin-prepare': 'index.js' },
|
||||
})}\n`)
|
||||
await writeFile(join(repository, 'prepare-helper', 'index.js'), [
|
||||
'#!/usr/bin/env node',
|
||||
"const { cpSync, mkdirSync, writeFileSync } = require('node:fs')",
|
||||
"mkdirSync('dsh-plugin-assets/skills', { recursive: true })",
|
||||
"cpSync('../skills', 'dsh-plugin-assets/skills/0', { recursive: true })",
|
||||
"writeFileSync('dsh-plugin.mjs', 'export function apply() {}\\n')",
|
||||
"writeFileSync('prepared.txt', `${process.env.REPOSITORY_TEST_VISIBLE ?? 'absent'}|${process.env.REPOSITORY_TEST_TOKEN ?? 'absent'}\\n`)",
|
||||
'',
|
||||
].join('\n'), { mode: 0o700 })
|
||||
await writeFile(join(repository, 'skills', 'fixture', 'SKILL.md'), 'repository skill source\n')
|
||||
await writeFile(join(repository, '.dsh-plugin', 'package.json'), `${JSON.stringify({
|
||||
name: 'repository-plugin-fixture',
|
||||
version: '1.0.0',
|
||||
scripts: { prepack: 'repository-build-helper && dsh-plugin-prepare' },
|
||||
devDependencies: { 'repository-build-helper': 'file:../build-helper' },
|
||||
devDependencies: {
|
||||
'repository-build-helper': 'file:../build-helper',
|
||||
'repository-prepare-helper': 'file:../prepare-helper',
|
||||
},
|
||||
dsh: { skills: ['../skills'] },
|
||||
})}\n`)
|
||||
await execFileAsync('git', ['init', '--quiet'], { cwd: repository })
|
||||
@@ -180,9 +182,7 @@ describe('RepositoryCache', () => {
|
||||
vi.stubEnv('REPOSITORY_TEST_VISIBLE', 'visible')
|
||||
vi.stubEnv('REPOSITORY_TEST_TOKEN', 'hidden')
|
||||
|
||||
const installed = await new RepositoryCache(join(root, 'cache'), {
|
||||
executableDirectories: [executableDirectory],
|
||||
}).resolve(specifier)
|
||||
const installed = await new RepositoryCache(join(root, 'cache')).resolve(specifier)
|
||||
await expect(readFile(join(installed, 'dependency-built.txt'), 'utf8')).resolves.toBe('dependency available\n')
|
||||
await expect(readFile(join(installed, 'prepared.txt'), 'utf8')).resolves.toBe('visible|absent\n')
|
||||
await expect(readFile(join(installed, 'dsh-plugin.mjs'), 'utf8')).resolves.toContain('export function apply')
|
||||
|
||||
@@ -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 packages/self-modification/repository-plugin/README.md
|
||||
README.md: e10907408a98cd470ae935b327ae44322f2e54a7
|
||||
README.zh.md: b8ba7dccd929574cc5aa1b7a3d68bf4f1e2f80e4
|
||||
README.md: 3a6478d5cca05684e80ce0854cb6e0b5d2f88bd4
|
||||
README.zh.md: 274d11c96cb61d4d9fa4b837433994248c94bff4
|
||||
|
||||
@@ -27,12 +27,13 @@ Place an ordinary package in the repository's `.dsh-plugin` directory:
|
||||
"@modelcontextprotocol/sdk": "1.29.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-repository-plugin": "^0.0.1",
|
||||
"typescript": "6.0.3"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`scripts.prepack` must be non-empty and invoke `dsh-plugin-prepare`; it may run arbitrary package-owned build steps first. DSH supplies only that helper command from its installed runtime: the package declares and runs its own compiler, runtime dependencies, and other npm lifecycle code. The selected package is installed from its own manifest instead of inheriting an enclosing pnpm workspace, so declare every dependency it needs and do not depend on workspace-only hoisting. DSH does not transpile TypeScript or infer a package entry.
|
||||
`scripts.prepack` must be non-empty and invoke `dsh-plugin-prepare`; it may run arbitrary package-owned build steps first. The package declares `@deepseek-ai/dsh-repository-plugin` as an ordinary development dependency so its published executable is available to that lifecycle. DSH does not inject the helper: the repository package declares and runs its own compiler, runtime dependencies, preparation helper, and other npm lifecycle code. The selected package is installed from its own manifest instead of inheriting an enclosing pnpm workspace, so declare every dependency it needs and do not depend on workspace-only hoisting. DSH does not transpile TypeScript or infer a package entry.
|
||||
|
||||
`dsh.entry` is an optional relative path to a compiled ESM Cordis Plugin inside `.dsh-plugin`. The module may use either namespace exports or a default export and owns its ordinary `name`, `inject`, `Config`, registrations, and effects. `dsh.skills` is an optional array of local skill roots, and `dsh.mcpServers` is an optional path to one `.mcp.json`; at least one of the three fields is required. Skill and MCP paths may reach adjacent repository assets but must remain beneath the directory containing `.dsh-plugin`; the compiled entry must remain inside the package selected and packed by the package manager. A repository containing several Plugins gives each one its own `.dsh-plugin` package under a different selectable subdirectory.
|
||||
|
||||
@@ -59,7 +60,7 @@ Long-lived surfaces watch both `cordis.patch.yml` layers through Cordis HMR. A v
|
||||
|
||||
## Preparation
|
||||
|
||||
During exact Git installation, DSH places temporary host-owned `pnpm` and `dsh-plugin-prepare` commands on the isolated package lifecycle `PATH`; neither command is fetched from the repository. The pnpm command reinvokes DSH's pinned pnpm with `--ignore-workspace`, so an enclosing workspace lockfile cannot suppress dependencies declared only by the selected `.dsh-plugin` package. The required `prepack` lifecycle runs after that dependency installation and before the selected subdirectory is packed. Package-owned commands may build TypeScript or other source before invoking the helper. The helper validates `package.json#dsh`, verifies that the compiled entry is an in-package file, validates skill and MCP sources, copies static assets under `dsh-plugin-assets`, and writes `dsh-plugin.mjs`. Before importing that wrapper, DSH revalidates that the installed package retained a `prepack` declaration containing the helper command. Failure to install, build, or prepare fails before a cache generation is published. Rationale: [host-owned Git source preparation Agent Note](../../../.agents/notes/implemented/bug-fix/2026-08-08-host-owned-git-repository-plugin-preparation.md).
|
||||
During exact Git installation, DSH's bundled pnpm installs the selected package from its own manifest. A transaction-owned `pnpm` wrapper reinvokes the same pinned pnpm with `--ignore-workspace`, so an enclosing workspace lockfile cannot suppress dependencies declared only by the selected `.dsh-plugin` package. The required `prepack` lifecycle runs after that dependency installation and before the selected subdirectory is packed; its ordinary `node_modules/.bin` lookup obtains `dsh-plugin-prepare` from the declared `@deepseek-ai/dsh-repository-plugin` dependency. That package marks its Cordis/DSH runtime peers optional so using the executable alone does not install the runtime graph. Package-owned commands may build TypeScript or other source before invoking the helper. The helper validates `package.json#dsh`, verifies that the compiled entry is an in-package file, validates skill and MCP sources, copies static assets under `dsh-plugin-assets`, and writes `dsh-plugin.mjs`. Before importing that wrapper, DSH revalidates that the installed package retained a `prepack` declaration containing the helper command. Failure to resolve the published helper, install dependencies, build, or prepare fails before a cache generation is published. Rationale: [npm-backed Git source preparation Agent Note](../../../.agents/notes/implemented/bug-fix/2026-08-08-npm-backed-git-repository-plugin-preparation.md).
|
||||
|
||||
## Runtime composition
|
||||
|
||||
|
||||
@@ -27,12 +27,13 @@
|
||||
"@modelcontextprotocol/sdk": "1.29.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-repository-plugin": "^0.0.1",
|
||||
"typescript": "6.0.3"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`scripts.prepack` 必须非空并调用 `dsh-plugin-prepare`;可以先运行任意包自有的构建步骤。DSH 已安装的运行时只提供该辅助命令:包自行声明并运行编译器、运行时依赖和其他 NPM 生命周期代码。所选包按自身 manifest 独立安装,而不继承外层 pnpm workspace,因此必须声明所需的每项依赖,不能依赖仅由 workspace 提升而可见的包。DSH 不转译 TypeScript,也不推断包入口。
|
||||
`scripts.prepack` 必须非空并调用 `dsh-plugin-prepare`;可以先运行任意包自有的构建步骤。包将 `@deepseek-ai/dsh-repository-plugin` 声明为普通开发依赖,使该生命周期可以使用其已发布的可执行文件。DSH 不会注入辅助程序:repository 包自行声明并运行编译器、运行时依赖、准备辅助程序及其他 NPM 生命周期代码。所选包按自身 manifest 独立安装,而不继承外层 pnpm workspace,因此必须声明所需的每项依赖,不能依赖仅由 workspace 提升而可见的包。DSH 不转译 TypeScript,也不推断包入口。
|
||||
|
||||
`dsh.entry` 是指向 `.dsh-plugin` 内已编译 ESM Cordis 插件的可选相对路径。该模块可以使用 namespace 导出或 default export,并自行拥有常规的 `name`、`inject`、`Config`、注册和 effect。`dsh.skills` 是可选的本地 skill 根数组,`dsh.mcpServers` 是指向一个 `.mcp.json` 的可选路径;三个字段中至少声明一个。skill 和 MCP 路径可以引用相邻的 repository 资源,但必须留在包含 `.dsh-plugin` 的目录下;已编译入口必须留在由包管理器选中并打包的包内。一个仓库可以在不同的可选择子目录下放置多个各自独立的 `.dsh-plugin` 包。
|
||||
|
||||
@@ -59,7 +60,7 @@ Git 传输使用宿主的常规 Git 认证。公共仓库无需凭据;私有
|
||||
|
||||
## 准备阶段
|
||||
|
||||
安装精确指定的 Git 源时,DSH 会把临时的宿主自有 `pnpm` 与 `dsh-plugin-prepare` 命令放入隔离的包生命周期 `PATH`;两个命令都不从 repository 获取。该 pnpm 命令会以 `--ignore-workspace` 重新调用 DSH 锁定的 pnpm,因此外层 workspace lockfile 无法抑制仅由所选 `.dsh-plugin` 包声明的依赖。必需的 `prepack` 生命周期在该依赖安装完成后、选定子目录打包前运行。包自有命令可以在调用辅助程序前构建 TypeScript 或其他源码。辅助程序会校验 `package.json#dsh`,确认已编译入口是包内文件,校验 skill 与 MCP 源,把静态资源复制到 `dsh-plugin-assets`,并写入 `dsh-plugin.mjs`。导入该包装层前,DSH 会重新校验已安装包是否仍保留包含该辅助命令的 `prepack` 声明。安装、构建或准备失败时,流程会在发布缓存 generation 前失败。设计依据见[宿主自有 Git 源准备 Agent Note](../../../.agents/notes/implemented/bug-fix/2026-08-08-host-owned-git-repository-plugin-preparation.md)。
|
||||
安装精确指定的 Git 源时,DSH 随附的 pnpm 会按所选包自身的 manifest 安装。由事务持有的 `pnpm` 包装脚本会以 `--ignore-workspace` 重新调用同一份锁定的 pnpm,因此外层 workspace lockfile 无法抑制仅由所选 `.dsh-plugin` 包声明的依赖。必需的 `prepack` 生命周期在该依赖安装完成后、选定子目录打包前运行;其常规 `node_modules/.bin` 查找会从已声明的 `@deepseek-ai/dsh-repository-plugin` 依赖取得 `dsh-plugin-prepare`。该包把 Cordis/DSH 运行时对等依赖(peer dependency)标为可选,因此单独使用该可执行文件不会安装运行时依赖图。包自有命令可以在调用辅助程序前构建 TypeScript 或其他源码。辅助程序会校验 `package.json#dsh`,确认已编译入口是包内文件,校验 skill 与 MCP 源,把静态资源复制到 `dsh-plugin-assets`,并写入 `dsh-plugin.mjs`。导入该包装层前,DSH 会重新校验已安装包是否仍保留包含该辅助命令的 `prepack` 声明。无法解析已发布的辅助程序,或安装依赖、构建或准备失败时,流程会在发布缓存 generation 前失败。设计依据见[基于 NPM 的 Git 源准备 Agent Note](../../../.agents/notes/implemented/bug-fix/2026-08-08-npm-backed-git-repository-plugin-preparation.md)。
|
||||
|
||||
## 运行时组合
|
||||
|
||||
|
||||
@@ -36,6 +36,26 @@
|
||||
"@deepseek-ai/dsh-skill-local": "^0.0.1",
|
||||
"cordis": "^4.0.0-rc.7"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@cordisjs/plugin-loader": {
|
||||
"optional": true
|
||||
},
|
||||
"@deepseek-ai/dsh-invariants": {
|
||||
"optional": true
|
||||
},
|
||||
"@deepseek-ai/dsh-mcp-client": {
|
||||
"optional": true
|
||||
},
|
||||
"@deepseek-ai/dsh-paths": {
|
||||
"optional": true
|
||||
},
|
||||
"@deepseek-ai/dsh-skill-local": {
|
||||
"optional": true
|
||||
},
|
||||
"cordis": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"zod": "^4.4.3"
|
||||
},
|
||||
|
||||
@@ -14,11 +14,11 @@ export const PREPARED_ENTRY_FILENAME = 'dsh-plugin.mjs'
|
||||
export const PREPARED_ASSET_DIRECTORY = 'dsh-plugin-assets'
|
||||
/** Loader builtin used by every generated repository wrapper. */
|
||||
export const REPOSITORY_PLUGIN_BUILTIN = 'dsh-repository-plugin'
|
||||
/** Host-owned command that repository package `prepack` lifecycles must invoke. */
|
||||
/** Dependency-provided command that repository package `prepack` lifecycles must invoke. */
|
||||
export const REPOSITORY_PLUGIN_PREPARE_COMMAND = 'dsh-plugin-prepare'
|
||||
|
||||
/**
|
||||
* Whether a package lifecycle declaration names the host preparation helper.
|
||||
* Whether a package lifecycle declaration names the preparation dependency's helper.
|
||||
* @param script - package-authored lifecycle command.
|
||||
* @returns true when the required helper command is present.
|
||||
*/
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
} from './format.ts'
|
||||
import { parseMcpDocument, resolveMcpServers } from './mcp.ts'
|
||||
import {
|
||||
createRepositoryPrepareCommand,
|
||||
loadPreparedRepository,
|
||||
resolveRepositoryCacheDirectory,
|
||||
resolveRepositorySpecifier,
|
||||
@@ -131,24 +130,17 @@ export async function apply(ctx: Context, config: Config = {}): Promise<void> {
|
||||
if (new Set(repositories).size !== repositories.length) {
|
||||
throw new Error('repository sources must resolve to unique exact specifiers')
|
||||
}
|
||||
const prepareCommand = repositories.length === 0 ? undefined : await createRepositoryPrepareCommand()
|
||||
try {
|
||||
const cache = new RepositoryCache(resolveRepositoryCacheDirectory(config.cacheDir), {
|
||||
executableDirectories: prepareCommand === undefined ? [] : [prepareCommand.directory],
|
||||
})
|
||||
await ctx.effect(async function* () {
|
||||
ctx.loader.builtins[REPOSITORY_PLUGIN_BUILTIN] = preparedRuntime
|
||||
yield () => {
|
||||
if (ctx.loader.builtins[REPOSITORY_PLUGIN_BUILTIN] === preparedRuntime) {
|
||||
Reflect.deleteProperty(ctx.loader.builtins, REPOSITORY_PLUGIN_BUILTIN)
|
||||
}
|
||||
const cache = new RepositoryCache(resolveRepositoryCacheDirectory(config.cacheDir))
|
||||
await ctx.effect(async function* () {
|
||||
ctx.loader.builtins[REPOSITORY_PLUGIN_BUILTIN] = preparedRuntime
|
||||
yield () => {
|
||||
if (ctx.loader.builtins[REPOSITORY_PLUGIN_BUILTIN] === preparedRuntime) {
|
||||
Reflect.deleteProperty(ctx.loader.builtins, REPOSITORY_PLUGIN_BUILTIN)
|
||||
}
|
||||
for (const repository of repositories) {
|
||||
const plugin = await loadPreparedRepository(ctx, cache, repository)
|
||||
yield plugin.dispose
|
||||
}
|
||||
}, 'repository-plugin runtime and sources')
|
||||
} finally {
|
||||
await prepareCommand?.dispose()
|
||||
}
|
||||
}
|
||||
for (const repository of repositories) {
|
||||
const plugin = await loadPreparedRepository(ctx, cache, repository)
|
||||
yield plugin.dispose
|
||||
}
|
||||
}, 'repository-plugin runtime and sources')
|
||||
}
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
* @module
|
||||
*/
|
||||
|
||||
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import { join, resolve } from 'node:path'
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
import type { Context, Fiber, FiberState, Plugin } from 'cordis'
|
||||
import type { RepositoryCache } from '@cordisjs/plugin-loader/repository'
|
||||
import { resolveDshHome } from '@deepseek-ai/dsh-paths'
|
||||
@@ -24,56 +23,6 @@ const FIBER_ACTIVE = 2 as FiberState.ACTIVE
|
||||
/** Directory under the Harness home containing immutable repository generations. */
|
||||
export const DEFAULT_REPOSITORY_CACHE_DIRECTORY = 'repository-plugins'
|
||||
|
||||
/** Temporary host command supplied to repository package lifecycle scripts. */
|
||||
export interface RepositoryPrepareCommand {
|
||||
/** Absolute directory to prepend to the isolated install's executable search path. */
|
||||
directory: string
|
||||
/** Remove the temporary command directory. */
|
||||
dispose(): Promise<void>
|
||||
}
|
||||
|
||||
function shellQuote(value: string): string {
|
||||
return `'${value.replaceAll("'", "'\\''")}'`
|
||||
}
|
||||
|
||||
function batchQuote(value: string): string {
|
||||
return `"${value.replaceAll('%', '%%')}"`
|
||||
}
|
||||
|
||||
/**
|
||||
* Materialize the DSH-owned prepare executable used only while pnpm packs Git source.
|
||||
* @returns a command directory and its idempotent cleanup operation.
|
||||
*/
|
||||
export async function createRepositoryPrepareCommand(): Promise<RepositoryPrepareCommand> {
|
||||
const directory = await mkdtemp(join(tmpdir(), 'dsh-repository-plugin-bin-'))
|
||||
const target = fileURLToPath(new URL('../lib/bin.js', import.meta.url))
|
||||
try {
|
||||
await Promise.all([
|
||||
writeFile(join(directory, REPOSITORY_PLUGIN_PREPARE_COMMAND), [
|
||||
'#!/bin/sh',
|
||||
`exec ${shellQuote(process.execPath)} ${shellQuote(target)} "$@"`,
|
||||
'',
|
||||
].join('\n'), { mode: 0o700 }),
|
||||
writeFile(join(directory, `${REPOSITORY_PLUGIN_PREPARE_COMMAND}.cmd`), [
|
||||
'@echo off',
|
||||
`${batchQuote(process.execPath)} ${batchQuote(target)} %*`,
|
||||
'',
|
||||
].join('\r\n'), { mode: 0o700 }),
|
||||
])
|
||||
} catch (cause) {
|
||||
/* v8 ignore next -- requires a host filesystem failure after mkdtemp; cleanup semantics are the contract under test. */
|
||||
await rm(directory, { recursive: true, force: true })
|
||||
/* v8 ignore next -- preserves that unstageable host failure after best-effort cleanup. */
|
||||
throw cause
|
||||
}
|
||||
return {
|
||||
directory,
|
||||
async dispose() {
|
||||
await rm(directory, { recursive: true, force: true })
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// The ref segment excludes `#` so `github:o/r#a#b` fails here — at the config
|
||||
// parser, with the syntax the error message promises — instead of inside the
|
||||
// cache's pnpm install ('misconfiguration fails loud at the earliest
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mkdir, mkdtemp, readFile, rm, stat, writeFile } from 'node:fs/promises'
|
||||
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join, relative, resolve } from 'node:path'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
@@ -14,7 +14,6 @@ import * as RepositoryPlugin from '@deepseek-ai/dsh-repository-plugin'
|
||||
import * as RepositoryPluginInvariant from '@deepseek-ai/dsh-repository-plugin/invariant'
|
||||
import { parsePreparedPluginConfig } from '../src/format.ts'
|
||||
import {
|
||||
createRepositoryPrepareCommand,
|
||||
loadPreparedRepository,
|
||||
resolveRepositoryCacheDirectory,
|
||||
resolveRepositorySpecifier,
|
||||
@@ -87,7 +86,7 @@ describe('dsh-plugin-prepare', () => {
|
||||
.resolves.toContain('mcp.expo.dev')
|
||||
})
|
||||
|
||||
it('preserves a compiled package entry and accepts a build before the host prepare command', async () => {
|
||||
it('preserves a compiled package entry and accepts a build before the package prepare command', async () => {
|
||||
const root = await temporaryDirectory('compiled-entry')
|
||||
const directory = await writePlugin(root, 'compiled-entry-fixture', {
|
||||
entry: './lib/plugin.mjs',
|
||||
@@ -406,17 +405,6 @@ describe('prepared repository plugin Loader composition', () => {
|
||||
})
|
||||
|
||||
describe('configured GitHub repository sources', () => {
|
||||
it('creates host-owned prepare commands and removes them idempotently', async () => {
|
||||
const command = await createRepositoryPrepareCommand()
|
||||
expect(await readFile(join(command.directory, RepositoryPlugin.REPOSITORY_PLUGIN_PREPARE_COMMAND), 'utf8'))
|
||||
.toContain(process.execPath)
|
||||
expect(await readFile(join(command.directory, `${RepositoryPlugin.REPOSITORY_PLUGIN_PREPARE_COMMAND}.cmd`), 'utf8'))
|
||||
.toContain(process.execPath)
|
||||
await command.dispose()
|
||||
await command.dispose()
|
||||
await expect(stat(command.directory)).rejects.toMatchObject({ code: 'ENOENT' })
|
||||
})
|
||||
|
||||
it('defaults an omitted source list and rejects unknown configuration fields', () => {
|
||||
expect(RepositoryPlugin.Config.parse(undefined)).toEqual({ repositories: [] })
|
||||
expect(RepositoryPlugin.Config.safeParse({ repositories: [], unexpected: true }).success).toBe(false)
|
||||
@@ -608,7 +596,7 @@ describe('configured GitHub repository sources', () => {
|
||||
await ctx.fiber.dispose()
|
||||
})
|
||||
|
||||
it('rejects an installed source whose prepack omits the host prepare command', async () => {
|
||||
it('rejects an installed source whose prepack omits the package prepare command', async () => {
|
||||
const root = await temporaryDirectory('installed-skipped-prepare')
|
||||
await writeFile(join(root, 'package.json'), JSON.stringify({
|
||||
name: 'installed-skipped-prepare',
|
||||
|
||||
Reference in New Issue
Block a user