mirror of
https://gh-proxy.org/https://github.com/klinsmaya/workdsh.git
synced 2026-11-09 18:07:02 +08:00
Merge remote-tracking branch 'origin/master' into codex/ci-profile-stability
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
/** Headless artifact smoke for the Electron-backed dsh and pnpm command entries. */
|
||||
|
||||
import { spawnSync } from 'node:child_process'
|
||||
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { cpSync, mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
@@ -10,6 +10,8 @@ import { installDesktopPnpmRuntime } from '../lib/desktop-runtime-environment.js
|
||||
|
||||
const packageRoot = new URL('../', import.meta.url)
|
||||
const desktopCli = fileURLToPath(new URL('lib/desktop-cli.js', packageRoot))
|
||||
const dshAppBootPackage = fileURLToPath(new URL('node_modules/@deepseek-ai/dsh-app-boot/', packageRoot))
|
||||
const dshPackage = fileURLToPath(new URL('node_modules/@deepseek-ai/dsh/', packageRoot))
|
||||
const pnpmCli = fileURLToPath(new URL('node_modules/pnpm/bin/pnpm.mjs', packageRoot))
|
||||
const dshVersion = JSON.parse(readFileSync(new URL('node_modules/@deepseek-ai/dsh/package.json', packageRoot), 'utf8')).version
|
||||
const pnpmVersion = JSON.parse(readFileSync(new URL('node_modules/pnpm/package.json', packageRoot), 'utf8')).version
|
||||
@@ -39,25 +41,31 @@ function assertNoRunnerEnvironment(label, env) {
|
||||
}
|
||||
}
|
||||
|
||||
function verifyResult(label, result, expectedVersion) {
|
||||
function verifyResult(label, result, expectedOutput) {
|
||||
if (result.error !== undefined) throw result.error
|
||||
if (result.status !== 0) {
|
||||
throw new Error(`${label} artifact smoke exited ${String(result.status)}: ${result.stderr.trim()}`)
|
||||
}
|
||||
if (result.stdout.trim() !== expectedVersion) {
|
||||
throw new Error(`${label} artifact smoke returned ${JSON.stringify(result.stdout.trim())} instead of ${JSON.stringify(expectedVersion)}`)
|
||||
if (expectedOutput === undefined) return
|
||||
const output = result.stdout.trim()
|
||||
const matches = expectedOutput instanceof RegExp
|
||||
? expectedOutput.test(output)
|
||||
: output === expectedOutput
|
||||
if (!matches) {
|
||||
throw new Error(`${label} artifact smoke returned ${JSON.stringify(output)} instead of matching ${String(expectedOutput)}`)
|
||||
}
|
||||
}
|
||||
|
||||
function runElectronEntry(label, nodeArgs, entry, expectedVersion) {
|
||||
function runElectronEntry(label, nodeArgs, entry, args, expectedOutput, extraEnvironment = {}) {
|
||||
const env = cleanEnvironment()
|
||||
Object.assign(env, extraEnvironment)
|
||||
env.ELECTRON_RUN_AS_NODE = '1'
|
||||
const result = spawnSync(electronPath, [...nodeArgs, entry, '--version'], {
|
||||
const result = spawnSync(electronPath, [...nodeArgs, entry, ...args], {
|
||||
encoding: 'utf8',
|
||||
env,
|
||||
shell: false,
|
||||
})
|
||||
verifyResult(label, result, expectedVersion)
|
||||
verifyResult(label, result, expectedOutput)
|
||||
}
|
||||
|
||||
function environmentValue(env, name) {
|
||||
@@ -148,5 +156,30 @@ function runPackagedPnpmShim() {
|
||||
}
|
||||
}
|
||||
|
||||
runElectronEntry('dsh', ['--expose-internals'], desktopCli, dshVersion)
|
||||
function runFlatProfileDshEntry() {
|
||||
const root = mkdtempSync(join(tmpdir(), 'dsh-desktop-flat-cli-smoke-'))
|
||||
const desktopPackage = join(root, 'node_modules', 'dsh-plugin-desktop')
|
||||
const linkedAppBootPackage = join(root, 'node_modules', '@deepseek-ai', 'dsh-app-boot')
|
||||
const linkedDshPackage = join(root, 'node_modules', '@deepseek-ai', 'dsh')
|
||||
try {
|
||||
mkdirSync(join(root, 'node_modules', '@deepseek-ai'), { recursive: true })
|
||||
cpSync(fileURLToPath(new URL('lib/', packageRoot)), join(desktopPackage, 'lib'), { recursive: true })
|
||||
cpSync(fileURLToPath(new URL('package.json', packageRoot)), join(desktopPackage, 'package.json'))
|
||||
symlinkSync(dshAppBootPackage, linkedAppBootPackage, process.platform === 'win32' ? 'junction' : 'dir')
|
||||
symlinkSync(dshPackage, linkedDshPackage, process.platform === 'win32' ? 'junction' : 'dir')
|
||||
runElectronEntry(
|
||||
'flat profile dsh plugin help',
|
||||
['--expose-internals'],
|
||||
join(desktopPackage, 'lib', 'desktop-cli.js'),
|
||||
['plugin', '--help'],
|
||||
undefined,
|
||||
{ DSH_DESKTOP_DEFAULT_PROFILE: 'desktop' },
|
||||
)
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true })
|
||||
}
|
||||
}
|
||||
|
||||
runElectronEntry('dsh', ['--expose-internals'], desktopCli, ['--version'], dshVersion)
|
||||
runFlatProfileDshEntry()
|
||||
runPackagedPnpmShim()
|
||||
|
||||
@@ -1,6 +1,37 @@
|
||||
/** Physical-path helpers for dependencies Electron Builder removes from app.asar. */
|
||||
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { createRequire } from 'node:module'
|
||||
import { dirname, join } from 'node:path'
|
||||
|
||||
function resolvePackageRoot(moduleUrl: string, packageName: string): string {
|
||||
const resolve = createRequire(moduleUrl).resolve
|
||||
try {
|
||||
return dirname(resolve(`${packageName}/package.json`))
|
||||
} catch (manifestCause) {
|
||||
let directory: string
|
||||
try {
|
||||
directory = dirname(resolve(packageName))
|
||||
} catch {
|
||||
throw manifestCause
|
||||
}
|
||||
while (true) {
|
||||
try {
|
||||
const manifest: unknown = JSON.parse(readFileSync(join(directory, 'package.json'), 'utf8'))
|
||||
if (manifest !== null
|
||||
&& typeof manifest === 'object'
|
||||
&& (manifest as { name?: unknown }).name === packageName) {
|
||||
return directory
|
||||
}
|
||||
} catch {
|
||||
// Continue toward the filesystem root until the owning package is found.
|
||||
}
|
||||
const parent = dirname(directory)
|
||||
if (parent === directory) throw manifestCause
|
||||
directory = parent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map one logical ASAR path to the sibling physical unpacked tree.
|
||||
@@ -12,9 +43,9 @@ export function unpackedAsarPath(filename: string): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve one dependency entry beside a built desktop module.
|
||||
* Resolve one dependency entry from a built desktop module.
|
||||
* @param moduleUrl - URL of a module emitted below the package's `lib` directory.
|
||||
* @param dependencyEntry - path below packaged `node_modules`.
|
||||
* @param dependencyEntry - package subpath resolved through Node module lookup.
|
||||
* @returns a physical path suitable for Node execution and profile symlinks.
|
||||
*/
|
||||
export function packagedDependencyPath(moduleUrl: string, dependencyEntry: string): string {
|
||||
@@ -22,9 +53,15 @@ export function packagedDependencyPath(moduleUrl: string, dependencyEntry: strin
|
||||
if (dependencyEntry.length === 0
|
||||
|| dependencyEntry.startsWith('/')
|
||||
|| dependencyEntry.includes('\\')
|
||||
|| segments.some(segment => segment.length === 0 || segment === '.' || segment === '..')) {
|
||||
|| segments.some(segment => segment.length === 0 || segment === '.' || segment === '..')
|
||||
|| (segments[0]?.startsWith('@') === true && segments.length < 2)) {
|
||||
throw new Error('dsh-plugin-desktop: packaged dependency entry must be a relative POSIX path')
|
||||
}
|
||||
const logicalPath = fileURLToPath(new URL(`../node_modules/${dependencyEntry}`, moduleUrl))
|
||||
const packageSegments = segments[0]?.startsWith('@') === true ? 2 : 1
|
||||
const packageName = segments.slice(0, packageSegments).join('/')
|
||||
const logicalPath = join(
|
||||
resolvePackageRoot(moduleUrl, packageName),
|
||||
...segments.slice(packageSegments),
|
||||
)
|
||||
return unpackedAsarPath(logicalPath)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
@@ -81,17 +83,67 @@ describe('packaged dsh bootstrap', () => {
|
||||
expect(unpackedAsarPath('/Applications/DSH Desktop.app/Contents/Resources/app.asar/package.json'))
|
||||
.toBe('/Applications/DSH Desktop.app/Contents/Resources/app.asar.unpacked/package.json')
|
||||
expect(unpackedAsarPath('/workspace/node_modules/pkg')).toBe('/workspace/node_modules/pkg')
|
||||
const moduleUrl = pathToFileURL(join(process.cwd(), 'app.asar', 'lib', 'desktop-cli.js')).href
|
||||
expect(packagedDependencyPath(moduleUrl, '@deepseek-ai/dsh/lib/bin.js')).toBe(join(
|
||||
process.cwd(),
|
||||
'app.asar.unpacked',
|
||||
'node_modules',
|
||||
'@deepseek-ai',
|
||||
'dsh',
|
||||
'lib',
|
||||
'bin.js',
|
||||
))
|
||||
expect(() => packagedDependencyPath(import.meta.url, '../outside.js'))
|
||||
.toThrow('relative POSIX path')
|
||||
})
|
||||
|
||||
it('maps a resolved ASAR dependency to its physical unpacked path', () => {
|
||||
const root = mkdtempSync(join(tmpdir(), 'dsh-desktop-asar-profile-'))
|
||||
const desktopLib = join(root, 'app.asar', 'lib')
|
||||
const dshPackage = join(root, 'app.asar', 'node_modules', '@deepseek-ai', 'dsh')
|
||||
try {
|
||||
mkdirSync(desktopLib, { recursive: true })
|
||||
mkdirSync(join(dshPackage, 'lib'), { recursive: true })
|
||||
writeFileSync(join(dshPackage, 'package.json'), JSON.stringify({
|
||||
name: '@deepseek-ai/dsh',
|
||||
type: 'module',
|
||||
}))
|
||||
writeFileSync(join(dshPackage, 'lib', 'bin.js'), '')
|
||||
|
||||
const moduleUrl = pathToFileURL(join(desktopLib, 'desktop-cli.js')).href
|
||||
expect(packagedDependencyPath(moduleUrl, '@deepseek-ai/dsh/lib/bin.js')).toBe(join(
|
||||
realpathSync(root),
|
||||
'app.asar.unpacked',
|
||||
'node_modules',
|
||||
'@deepseek-ai',
|
||||
'dsh',
|
||||
'lib',
|
||||
'bin.js',
|
||||
))
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
it('resolves the DSH entry from a pnpm profile with flat package dependencies', () => {
|
||||
const root = mkdtempSync(join(tmpdir(), 'dsh-desktop-flat-profile-'))
|
||||
const desktopLib = join(root, 'node_modules', 'dsh-plugin-desktop', 'lib')
|
||||
const dshPackage = join(root, 'node_modules', '@deepseek-ai', 'dsh')
|
||||
const dshEntry = join(dshPackage, 'lib', 'bin.js')
|
||||
const pnpmPackage = join(root, 'node_modules', 'pnpm')
|
||||
const pnpmEntry = join(pnpmPackage, 'bin', 'pnpm.mjs')
|
||||
try {
|
||||
mkdirSync(desktopLib, { recursive: true })
|
||||
mkdirSync(join(dshPackage, 'lib'), { recursive: true })
|
||||
mkdirSync(join(pnpmPackage, 'bin'), { recursive: true })
|
||||
writeFileSync(join(dshPackage, 'package.json'), JSON.stringify({
|
||||
name: '@deepseek-ai/dsh',
|
||||
type: 'module',
|
||||
}))
|
||||
writeFileSync(dshEntry, '')
|
||||
writeFileSync(join(pnpmPackage, 'package.json'), JSON.stringify({
|
||||
name: 'pnpm',
|
||||
exports: { '.': './package.json' },
|
||||
}))
|
||||
writeFileSync(pnpmEntry, '')
|
||||
|
||||
const moduleUrl = pathToFileURL(join(desktopLib, 'desktop-cli.js')).href
|
||||
expect(packagedDependencyPath(moduleUrl, '@deepseek-ai/dsh/lib/bin.js'))
|
||||
.toBe(join(realpathSync(root), 'node_modules', '@deepseek-ai', 'dsh', 'lib', 'bin.js'))
|
||||
expect(packagedDependencyPath(moduleUrl, 'pnpm/bin/pnpm.mjs'))
|
||||
.toBe(join(realpathSync(root), 'node_modules', 'pnpm', 'bin', 'pnpm.mjs'))
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user