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
15 changes: 6 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,13 @@ jobs:
strategy:
fail-fast: false
matrix:
# 默认两矩阵(发布负责人可按支持面增删):
# - Node 22 + 最低宿主:目标宿主 ref 需在受支持分支上稳定可拉取;
# 宿主契约合并后,把 HOST_REF_22 填成含该契约的完整 SHA。
# - Node 24 + main:前向集成(拉当前 main 的 HEAD)。
# latest 必须继续兼容已发布 rc;next 才能依赖 alpha 契约。
# 宿主发布新版本时显式更新这里,禁止跟随可变的 master 冒充验收。
include:
- node: 22
host-ref: "master" # OSS 宿主默认分支
host-ref: "dsh-v0.1.1-rc.2"
- node: 24
host-ref: "master"
host-ref: "dsh-v0.1.2-alpha.1"
runs-on: ubuntu-latest
timeout-minutes: 40
steps:
Expand All @@ -35,12 +33,11 @@ jobs:
# 宿主 checkout 统一落在 $GITHUB_WORKSPACE/../../.dsh/source/current
# (= $GITHUB_WORKSPACE 的规范化父级 .dsh)——这样 vitest alias 与
# tsconfig paths 里 `../../.dsh/...` 的相对解析在 GitHub 上同样成立。
- name: Clone dsh main repo at DSH_ROOT
- name: Clone pinned DSH host at DSH_ROOT
run: |
DSH_ROOT="$GITHUB_WORKSPACE/../../.dsh/source/current"
mkdir -p "$(dirname "$DSH_ROOT")"
git clone --depth 1 "https://github.com/deepseek-ai/deepseek-harness.git" "$DSH_ROOT"
git -C "$DSH_ROOT" checkout "${{ matrix.host-ref }}"
git clone --depth 1 --branch "${{ matrix.host-ref }}" "https://github.com/deepseek-ai/deepseek-harness.git" "$DSH_ROOT"
test -f "$DSH_ROOT/packages/client/ui-primitives/src/index.ts" || { echo "::error::宿主预检失败:ui-primitives 源码缺失"; exit 1; }
echo "HOST_SHA=$(git -C "$DSH_ROOT" rev-parse HEAD)" >> "$GITHUB_ENV"

Expand Down
16 changes: 14 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ jobs:
env:
RELEASE_TAG: ${{ github.event.release.tag_name }}
PRERELEASE: ${{ github.event.release.prerelease }}
HOST_REF: ${{ github.event.release.prerelease && 'dsh-v0.1.2-alpha.1' || 'dsh-v0.1.1-rc.2' }}

steps:
# actions/checkout@v4
Expand Down Expand Up @@ -53,6 +54,12 @@ jobs:
exit 1
fi

VERSION_PRERELEASE="$(node -e 'console.log(process.argv[1].split("+")[0].includes("-") ? "true" : "false")' "$VERSION")"
if [ "$PRERELEASE" != "$VERSION_PRERELEASE" ]; then
echo "::error::package version prerelease state ($VERSION_PRERELEASE) does not match GitHub release prerelease state ($PRERELEASE)"
exit 1
fi

if [ "$PRERELEASE" = "true" ]; then
echo "npm_tag=next" >> "$GITHUB_OUTPUT"
else
Expand Down Expand Up @@ -87,8 +94,7 @@ jobs:
run: |
DSH_ROOT="$GITHUB_WORKSPACE/../../.dsh/source/current"
mkdir -p "$(dirname "$DSH_ROOT")"
git clone --depth 1 https://github.com/deepseek-ai/deepseek-harness.git "$DSH_ROOT"
git -C "$DSH_ROOT" checkout master
git clone --depth 1 --branch "$HOST_REF" https://github.com/deepseek-ai/deepseek-harness.git "$DSH_ROOT"
test -f "$DSH_ROOT/packages/client/ui-primitives/src/index.ts" || {
echo "::error::DSH host preflight failed: ui-primitives source is missing"
exit 1
Expand All @@ -110,5 +116,11 @@ jobs:
- name: Verify package
run: node scripts/verify-pack.mjs

- name: Smoke selected release channel
run: node scripts/e2e.mjs --smoke --install link --port 3190
env:
DSH_ROOT: ${{ github.workspace }}/../../.dsh/source/current
DSH_BIN: ${{ github.workspace }}/../../.dsh/source/current/apps/cli/lib/bin.js

- name: Publish to npm
run: npm publish --access public --tag "${{ steps.release.outputs.npm_tag }}"
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
## [Unreleased]
### 发布
- npm 发布作用域改为个人账号 `@changfenhuang/dsh-genui`;GitHub 仓库继续保留在 `omdsh-dev` 组织。同步更新运行时模块标识、资源路由、安装脚本、文档和测试,不保留旧 npm 名称兼容层。
- **正式版与测试版分流**:发布版本带预发布后缀时必须勾选 GitHub Pre-release,并只进入 npm `next`;无后缀版本只能作为正式 Release 进入 `latest`。CI 与发布验收固定覆盖 `dsh-v0.1.1-rc.2` / `dsh-v0.1.2-alpha.1`,不再跟随可变的宿主开发分支。

### 兼容性
- **dsh 0.1.0-rc.8**:对齐全部宿主 peer 依赖并补齐实际使用的 conversation、input-trigger、session 直接声明;改用 ui-tool 的公开客户端入口,测试和构建不再读取本机旧源码快照。`tsc`、`tsdown`、Vitest 全通过(316 passed / 104 skipped,0 失败)。
Expand Down
12 changes: 6 additions & 6 deletions scripts/e2e.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const arg = (name) => {
return index === -1 ? undefined : process.argv[index + 1]
}
function findDshWebUrl(output) {
return output.match(/dsh web: (http:\/\/127\.0\.0\.1:\d+\/\?token=[A-Za-z0-9_-]+)/u)?.[1]
return output.match(/dsh web: (http:\/\/127\.0\.0\.1:\d+(?:\/\?token=[A-Za-z0-9_-]+)?)/u)?.[1]
}
const PORT = Number(arg('--port') ?? 3088)
const KEEP = process.argv.includes('--keep')
Expand Down Expand Up @@ -190,7 +190,7 @@ try {
await page.goto(readyUrl, { waitUntil: 'domcontentloaded', timeout: 60000 })
await page.waitForTimeout(5000)

// 0.1.2 起只服务启动图公告的不可变组合 URL,不再开放旧版单包裸路径
// 0.1.2 起使用启动图公告的不可变组合 URL;更早的受支持宿主使用裸插件地址
const boot = await page.evaluate(() => {
const boot = window.__DSH_BOOT__
if (boot === null || typeof boot !== 'object' || !Array.isArray(boot.entries)) return undefined
Expand All @@ -200,14 +200,14 @@ try {
await page.screenshot({ path: join(artifactsDir, 'e2e-fail-empty-host-boot.png') })
fail('dsh 宿主启动图为空(连内置浏览器插件都未注册)')
}
if (boot?.clientUrl === undefined) fail('启动图缺少 @changfenhuang/dsh-genui(插件未注册)')
const clientRes = await fetch(`${BASE}${boot.clientUrl}`)
const clientUrl = boot?.clientUrl ?? '/plugins/@changfenhuang/dsh-genui/client.js'
const clientRes = await fetch(`${BASE}${clientUrl}`)
if (!clientRes.ok) {
await page.screenshot({ path: join(artifactsDir, 'e2e-fail-client404.png') })
await logTail()
fail(`启动图公告的 GenUI bundle 返回 ${clientRes.status}`)
fail(`GenUI bundle 返回 ${clientRes.status}`)
}
log(`✓ 启动图 GenUI bundle ${clientRes.status}`)
log(`✓ GenUI bundle ${clientRes.status}`)

if (pageErrors.length > 0) {
await page.screenshot({ path: join(artifactsDir, 'e2e-fail-pageerror.png') })
Expand Down
5 changes: 3 additions & 2 deletions tests/e2e-auth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ if (match === null) throw new Error('e2e script must define findDshWebUrl')
const findDshWebUrl = Function(`${match[0]}; return findDshWebUrl`)() as (output: string) => string | undefined

describe('dsh web authentication', () => {
it('extracts only the tokenized local startup URL', () => {
it('extracts tokenized alpha and bare rc startup URLs', () => {
expect(findDshWebUrl('booting\ndsh web: http://127.0.0.1:3190/?token=abc_DEF-123\n'))
.toBe('http://127.0.0.1:3190/?token=abc_DEF-123')
expect(findDshWebUrl('dsh web: http://127.0.0.1:3190/')).toBeUndefined()
expect(findDshWebUrl('dsh web: http://127.0.0.1:3190'))
.toBe('http://127.0.0.1:3190')
})
})
Loading