Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions .github/prove-dsh-profile.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#!/usr/bin/env node
// Boot a profile that already ran `dsh plugin --profile <name> add <checkout>`
// and fail unless dsh-pstack's skill provider lists the required skills.
// There is no dsh flag that prints the skill catalog; `dsh --help` only offers
// plugin (pnpm) and --dump-config. imports dsh's internal profile-boot module
// (lib/profile-boot.js and the nested dsh-app-boot file); tied to the pinned dsh version.
import { execFileSync } from 'node:child_process'
import { mkdtempSync, realpathSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { dirname, join } from 'node:path'
import { pathToFileURL } from 'node:url'

const profile = process.env.DSH_PROFILE ?? 'ci'
const required = ['poteto-mode', 'setup-pstack', 'deslop', 'control-cli', 'control-ui']

function dshPackageRoot() {
const bin = execFileSync('sh', ['-c', 'command -v dsh'], { encoding: 'utf8' }).trim()
if (bin === '') throw new Error('dsh is not on PATH')
// npm links bin/dsh -> <package>/lib/bin.js
return dirname(dirname(realpathSync(bin)))
}

if (process.env.DSH_HOME === undefined || process.env.DSH_HOME.trim() === '') {
throw new Error('DSH_HOME must point at the home used for `dsh plugin add`')
}
if (process.env.DSH_AGENTS_HOME === undefined || process.env.DSH_AGENTS_HOME.trim() === '') {
process.env.DSH_AGENTS_HOME = mkdtempSync(join(tmpdir(), 'dsh-agents-'))
}

const listCwd = mkdtempSync(join(tmpdir(), 'dsh-list-'))
const dshPkg = dshPackageRoot()
const { runProfile } = await import(pathToFileURL(join(dshPkg, 'lib/profile-boot.js')).href)
const { loadLayeredEnv } = await import(pathToFileURL(join(dshPkg, 'node_modules/@deepseek-ai/dsh-app-boot/lib/index.js')).href)

let code = 0
let shutdown
try {
const booted = await runProfile({
environment: loadLayeredEnv('dsh', listCwd),
profile,
patchFiles: [],
args: [],
})
shutdown = booted.shutdown
const bundles = booted.ctx.get('profileContext')?.startedBundles ?? []
console.log(`started bundles: ${bundles.join(', ')}`)
if (!bundles.includes('dsh-pstack')) {
console.error('profile did not mount dsh-pstack')
code = 1
}
if (typeof booted.ctx.skills?.list !== 'function') {
console.error('profile booted without a skill registry')
code = 1
} else {
const skills = await booted.ctx.skills.list({ cwd: listCwd })
const pstack = skills.filter((skill) => skill.provider === 'pstack').map((skill) => skill.name).sort()
console.log(`pstack skills (${pstack.length}):`)
for (const name of pstack) console.log(name)
const missing = required.filter((name) => !pstack.includes(name))
if (missing.length > 0) {
console.error(`missing required pstack skills: ${missing.join(', ')}`)
code = 1
}
}
} catch (error) {
console.error(error instanceof Error ? error.stack ?? error.message : error)
code = 1
} finally {
if (shutdown !== undefined) {
try {
await shutdown.shutdown(code)
} catch (error) {
console.error(error instanceof Error ? error.stack ?? error.message : error)
code = 1
}
}
}
process.exit(code)
45 changes: 45 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ jobs:
node --check lib/index.js
node --check lib/parse.js
node --check scripts/validate.mjs
node --check .github/prove-dsh-profile.mjs

- name: Check shell scripts
run: |
Expand Down Expand Up @@ -93,3 +94,47 @@ jobs:

- name: Run statechart tests
run: node --test .agents/skills/statechart/scripts/test/statechart.test.mjs

harness:
name: Mount bundle in DSH
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm

- name: Install bundle dependencies
run: npm ci --omit=peer

- name: Install pnpm and DSH 0.2.1-rc.4
run: |
set -euo pipefail
prefix="${RUNNER_TEMP}/dsh-prefix"
# pinned to the runtime this proof was written against. bump it in its own pr.
npm install -g --prefix "$prefix" pnpm@10 @deepseek-ai/dsh@0.2.1-rc.4
echo "$prefix/bin" >> "$GITHUB_PATH"
"$prefix/bin/dsh" --version
"$prefix/bin/pnpm" --version

- name: Mount the checkout and list pstack skills
env:
DSH_HOME: ${{ runner.temp }}/dsh-home
DSH_PROFILE: ci
run: |
set -euo pipefail
mkdir -p "$DSH_HOME"
dsh plugin --profile ci add "$GITHUB_WORKSPACE"
dsh plugin --profile ci list --depth 0
dump="$DSH_HOME/dump-config.yml"
dsh --profile ci --dump-config > "$dump"
grep -F '# == dsh-pstack' "$dump"
grep -F -- '- id: pstack' "$dump"
grep -F 'name: dsh-pstack' "$dump"
node .github/prove-dsh-profile.mjs
Loading