199 lines
13 KiB
JavaScript
199 lines
13 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import { readFile } from 'node:fs/promises'
|
|
import test from 'node:test'
|
|
|
|
const workflowUrl = new URL('../../../.github/workflows/dsh-upstream-update.yml', import.meta.url)
|
|
const validationUrl = new URL('../../../.github/workflows/dsh-candidate-validation.yml', import.meta.url)
|
|
const productionUrl = new URL('../../../.github/workflows/production-publish.yml', import.meta.url)
|
|
const notificationsUrl = new URL('../../../.github/workflows/workflow-notifications.yml', import.meta.url)
|
|
const dockerUrl = new URL('../../../.github/workflows/docker.yaml', import.meta.url)
|
|
const gotifyWorkflow = 'yjrszcq/github-workflows/.github/workflows/gotify-notify.yml@c4c7fe9e17854cd4962913ac2792513eab0be988'
|
|
|
|
test('upstream workflow discovers candidates without production credentials', async () => {
|
|
const workflow = await readFile(workflowUrl, 'utf8')
|
|
assert.match(workflow, /schedule:/)
|
|
assert.match(workflow, /workflow_dispatch:/)
|
|
assert.match(workflow, /contents: write/)
|
|
assert.match(workflow, /pull-requests: write/)
|
|
assert.match(workflow, /BASE_BRANCH: dev/)
|
|
assert.match(workflow, /ref: \$\{\{ env\.BASE_BRANCH \}\}/)
|
|
assert.match(workflow, /supported-target\.mjs" advance/)
|
|
assert.match(workflow, /trusted-platform/)
|
|
assert.match(workflow, /node "\$trusted_platform\/tools\/supported-target\.mjs"/)
|
|
assert.match(workflow, /jq -r 'if \(\.changed \| type\) == "boolean" then \.changed else error\("expected boolean changed"\) end'/)
|
|
assert.doesNotMatch(workflow, /jq -er '\.changed'/)
|
|
assert.match(workflow, /candidate_remote_sha="\$\(git ls-remote --heads origin/)
|
|
assert.match(workflow, /git switch --create "\$CANDIDATE_BRANCH" "\$base_sha"/)
|
|
assert.match(workflow, /git push --force-with-lease="refs\/heads\/\$CANDIDATE_BRANCH:\$candidate_remote_sha"/)
|
|
assert.match(workflow, /git push origin/)
|
|
assert.match(workflow, /chore\(dsh\): validate/)
|
|
assert.match(workflow, /gh pr create[\s\S]*--base "\$BASE_BRANCH"/)
|
|
assert.match(workflow, /gh pr edit "\$pr_number"[\s\S]*--base "\$BASE_BRANCH"/)
|
|
assert.doesNotMatch(workflow, /github\.event\.repository\.default_branch/)
|
|
assert.doesNotMatch(workflow, /DSH_RELEASE_PRIVATE_KEY|DSH_RECOVERY|DOCKER_TOKEN/)
|
|
})
|
|
|
|
test('upstream workflow calls only the pinned reusable Gotify interface', async () => {
|
|
const workflow = await readFile(workflowUrl, 'utf8')
|
|
assert.equal(workflow.split(gotifyWorkflow).length - 1, 2)
|
|
assert.match(workflow, /gotify_url: \$\{\{ secrets\.GOTIFY_URL \}\}/)
|
|
assert.match(workflow, /gotify_token: \$\{\{ secrets\.GOTIFY_TOKEN \}\}/)
|
|
assert.doesNotMatch(workflow, /secrets: inherit/)
|
|
})
|
|
|
|
test('candidate validation is secret-free and exercises the complete current Environment', async () => {
|
|
const workflow = await readFile(validationUrl, 'utf8')
|
|
assert.match(workflow, /workflow_call:/)
|
|
assert.match(workflow, /workflow_dispatch:/)
|
|
assert.match(workflow, /pull_request_target:/)
|
|
assert.doesNotMatch(workflow, /^ pull_request:/m)
|
|
assert.match(workflow, /github\.event_name != 'pull_request_target'/)
|
|
assert.match(workflow, /github\.event\.pull_request\.head\.ref != 'automation\/dsh-upstream-candidate'/)
|
|
assert.match(workflow, /persist-credentials: false/)
|
|
assert.match(workflow, /inputs\.ref \|\| github\.event\.pull_request\.head\.sha \|\| github\.sha/)
|
|
assert.match(workflow, /permissions:\n contents: read/)
|
|
assert.match(workflow, /supported-target\.mjs validate/)
|
|
assert.match(workflow, /dist\.integrity/)
|
|
assert.match(workflow, /npm test --prefix container\/control-plane\/services\/gateway/)
|
|
assert.match(workflow, /npm test --prefix container\/platform/)
|
|
assert.match(workflow, /cache: npm/)
|
|
assert.match(workflow, /cache-dependency-path:[\s\S]*container\/control-plane\/services\/management\/package-lock\.json[\s\S]*container\/environment\/resources\/plugins\/platform-management\/package\/package-lock\.json/)
|
|
assert.match(workflow, /npm ci --omit=dev --ignore-scripts --no-audit --no-fund --prefix container\/control-plane\/services\/management/)
|
|
assert.match(workflow, /npm ci --omit=dev --ignore-scripts --no-audit --no-fund --legacy-peer-deps --prefix container\/environment\/resources\/plugins\/platform-management\/package/)
|
|
assert.match(workflow, /container-smoke\.sh/)
|
|
assert.match(workflow, /devtools-smoke\.sh/)
|
|
assert.doesNotMatch(workflow, /secrets\.|DSH_RELEASE_PRIVATE_KEY|DSH_RECOVERY|DOCKER_TOKEN/)
|
|
})
|
|
|
|
test('standalone candidate results notify without exposing secrets to candidate code', async () => {
|
|
const workflow = await readFile(notificationsUrl, 'utf8')
|
|
assert.match(workflow, /workflow_run:/)
|
|
assert.match(workflow, /DSH Candidate Validation/)
|
|
assert.match(workflow, /github\.event\.workflow_run\.event == 'pull_request_target'/)
|
|
assert.match(workflow, /github\.event\.workflow_run\.event == 'workflow_dispatch'/)
|
|
assert.match(workflow, /github\.event\.workflow_run\.conclusion == 'success'/)
|
|
assert.match(workflow, /github\.event\.workflow_run\.conclusion == 'failure'/)
|
|
assert.match(workflow, /github\.event\.workflow_run\.head_branch != 'automation\/dsh-upstream-candidate'/)
|
|
assert.match(workflow, /github\.event\.workflow_run\.html_url/)
|
|
assert.match(workflow, /DSH 候选版本验证通过/)
|
|
assert.match(workflow, /DSH 候选版本验证失败/)
|
|
assert.match(workflow, /手动发起的 DSH 候选版本兼容性验证通过/)
|
|
assert.match(workflow, /手动发起的 DSH 候选版本兼容性验证失败/)
|
|
assert.doesNotMatch(workflow, /actions\/checkout|\brun:/)
|
|
})
|
|
|
|
test('discovery run reports candidate status without duplicate result notifications', async () => {
|
|
const workflow = await readFile(workflowUrl, 'utf8')
|
|
assert.match(workflow, /uses: \.\/\.github\/workflows\/dsh-candidate-validation\.yml/)
|
|
assert.match(workflow, /statuses: write/)
|
|
assert.match(workflow, /gh pr comment "\$PR_NUMBER" \\\n+\s+--repo "\$GITHUB_REPOSITORY"/)
|
|
assert.match(workflow, /DSH candidate Runtime/)
|
|
assert.match(workflow, /notify-discovered:[\s\S]*发现 DSH 上游新版本/)
|
|
assert.match(workflow, /notify-result:[\s\S]*DSH 上游检查失败/)
|
|
assert.match(workflow, /候选版本验证通过/)
|
|
assert.match(workflow, /候选版本验证失败/)
|
|
assert.doesNotMatch(workflow, /notify-validation-passed:|notify-validation-failed:/)
|
|
})
|
|
|
|
test('production publication owns signing, advances an append-only channel, and releases only Environment versions', async () => {
|
|
const workflow = await readFile(productionUrl, 'utf8')
|
|
assert.match(workflow, /branches:\n - main/)
|
|
assert.match(workflow, /release\/supported-target\.json/)
|
|
assert.match(workflow, /release\/official-dsh-policy\.json/)
|
|
assert.match(workflow, /environment: production-release/)
|
|
assert.match(workflow, /if: github\.ref == 'refs\/heads\/main'/)
|
|
assert.match(workflow, /DSH_RELEASE_PRIVATE_KEY/)
|
|
assert.match(workflow, /prepare-release\.mjs/)
|
|
assert.match(workflow, /environment-release\.mjs/)
|
|
assert.match(workflow, /cache: npm/)
|
|
assert.match(workflow, /cache-dependency-path:[\s\S]*container\/control-plane\/services\/management\/package-lock\.json[\s\S]*container\/environment\/resources\/plugins\/platform-management\/package\/package-lock\.json/)
|
|
assert.match(workflow, /npm ci --omit=dev --ignore-scripts --no-audit --no-fund --prefix container\/control-plane\/services\/management/)
|
|
assert.match(workflow, /npm ci --omit=dev --ignore-scripts --no-audit --no-fund --legacy-peer-deps --prefix container\/environment\/resources\/plugins\/platform-management\/package/)
|
|
assert.match(workflow, /refs\/heads\/release-channel/)
|
|
assert.match(workflow, /sequence=1/)
|
|
assert.match(workflow, /current_sequence \+ 1/)
|
|
assert.match(workflow, /previous="\$channel\/targets\/\$current_sequence"/)
|
|
assert.match(workflow, /test -d "\$previous"/)
|
|
assert.match(workflow, /test -f "\$previous\/\$name"/)
|
|
assert.match(workflow, /cmp "\$channel\/\$name" "\$previous\/\$name"/)
|
|
assert.match(workflow, /release-channel\/targets\/\$\{TARGET_SEQUENCE\}/)
|
|
assert.match(workflow, /test ! -e "\$target"/)
|
|
assert.match(workflow, /tag="v\$\{ENVIRONMENT_VERSION\}"/)
|
|
assert.match(workflow, /--title "\$ENVIRONMENT_TAG"/)
|
|
assert.doesNotMatch(workflow, /--title "DSH Docker Environment/)
|
|
assert.match(workflow, /cmp "\$RUNNER_TEMP\/environment-release\.json"/)
|
|
assert.match(workflow, /--draft/)
|
|
assert.match(workflow, /--draft=false/)
|
|
assert.match(workflow, /--latest/)
|
|
assert.match(workflow, /--json isDraft/)
|
|
assert.match(workflow, /cp "\$RUNNER_TEMP\/environment-release\.json" "\$assets\/"/)
|
|
assert.match(workflow, /prune_environment_assets\(\)/)
|
|
assert.match(workflow, /gh release delete-asset "\$ENVIRONMENT_TAG" "\$name" --yes/)
|
|
assert.match(workflow, /test "\$name" != environment-release\.json/)
|
|
assert.doesNotMatch(workflow, /bootstrap\.manifest\.json bootstrap\.manifest\.sig\.json/)
|
|
assert.doesNotMatch(workflow, /\.artifacts\[\]\.url/)
|
|
assert.doesNotMatch(workflow, /environment-v\$\{ENVIRONMENT_VERSION\}\.tar\.gz/)
|
|
assert.doesNotMatch(workflow, /uses: \.\/\.github\/workflows\/docker\.yaml/)
|
|
assert.doesNotMatch(workflow, /environment: production-image|DOCKER_TOKEN/)
|
|
assert.equal(workflow.split(gotifyWorkflow).length - 1, 0)
|
|
assert.doesNotMatch(workflow, /platform-\$\{TARGET_SEQUENCE\}|DSH Platform \$\{TARGET_SEQUENCE\}/)
|
|
assert.doesNotMatch(workflow, /RECOVERY_PRIVATE|secrets: inherit/)
|
|
})
|
|
|
|
test('successful signed targets notify independently from image approval', async () => {
|
|
const workflow = await readFile(notificationsUrl, 'utf8')
|
|
assert.match(workflow, /workflow_run:/)
|
|
assert.match(workflow, /Publish Supported Platform Target/)
|
|
assert.match(workflow, /github\.event\.workflow_run\.name == 'Publish Supported Platform Target'/)
|
|
assert.match(workflow, /github\.event\.workflow_run\.conclusion == 'success'/)
|
|
assert.match(workflow, /github\.event\.workflow_run\.conclusion == 'failure'/)
|
|
assert.match(workflow, /DSH 正式发布失败/)
|
|
assert.match(workflow, /production-image/)
|
|
assert.match(workflow, /actions\/workflows\/docker\.yaml/)
|
|
assert.equal(workflow.split(gotifyWorkflow).length - 1, 1)
|
|
assert.equal((workflow.match(/^ notify-result:/gm) ?? []).length, 1)
|
|
assert.doesNotMatch(workflow, /actions\/checkout|\brun:/)
|
|
})
|
|
|
|
test('Docker publication consumes one signed channel target and mirrors only standard images to GHCR', async () => {
|
|
const workflow = await readFile(dockerUrl, 'utf8')
|
|
assert.match(workflow, /workflow_run:/)
|
|
assert.match(workflow, /Publish Supported Platform Target/)
|
|
assert.match(workflow, /github\.event\.workflow_run\.conclusion == 'success'/)
|
|
assert.match(workflow, /github\.event\.workflow_run\.head_sha/)
|
|
assert.match(workflow, /environment: production-image/)
|
|
assert.match(workflow, /contents: read\n packages: write/)
|
|
assert.match(workflow, /git -C channel log --format=%H -- channel\.json/)
|
|
assert.match(workflow, /No signed release-channel target matches source commit/)
|
|
assert.match(workflow, /latestSupportedDsh/)
|
|
assert.match(workflow, /channel\/targets\/\$sequence/)
|
|
assert.match(workflow, /verify-image-release\.mjs/)
|
|
assert.equal((workflow.match(/SIGNED_IMAGE_INPUT=true/g) ?? []).length, 4)
|
|
assert.match(workflow, /PLATFORM_REVISION=\$\{\{ steps\.channel\.outputs\.source_commit \}\}/)
|
|
assert.match(workflow, /ENVIRONMENT_VERSION=\$\{\{ steps\.signed-target\.outputs\.environment_version \}\}/)
|
|
assert.match(workflow, /TARGET_SEQUENCE=\$\{\{ steps\.signed-target\.outputs\.sequence \}\}/)
|
|
assert.match(workflow, /cache: npm/)
|
|
assert.match(workflow, /cache-dependency-path:[\s\S]*source\/container\/control-plane\/services\/management\/package-lock\.json[\s\S]*source\/container\/environment\/resources\/plugins\/platform-management\/package\/package-lock\.json/)
|
|
assert.match(workflow, /npm ci --omit=dev --ignore-scripts --no-audit --no-fund --prefix container\/control-plane\/services\/management/)
|
|
assert.match(workflow, /npm ci --omit=dev --ignore-scripts --no-audit --no-fund --legacy-peer-deps --prefix container\/environment\/resources\/plugins\/platform-management\/package/)
|
|
assert.match(workflow, /DOCKERHUB_IMAGE: szcq\/deepseek-harness/)
|
|
assert.match(workflow, /GHCR_IMAGE: ghcr\.io\/\$\{\{ github\.repository \}\}/)
|
|
assert.match(workflow, /\$\{GHCR_IMAGE\}:\$\{ENVIRONMENT_VERSION\}/)
|
|
assert.match(workflow, /\$\{GHCR_IMAGE\}:\$\{major\}\.\$\{minor\}/)
|
|
assert.match(workflow, /\$\{GHCR_IMAGE\}:dsh-\$\{DSH_VERSION\}/)
|
|
assert.match(workflow, /\$\{DOCKERHUB_IMAGE\}:\$\{DSH_VERSION\}-devtools/)
|
|
const versionedDevtools = workflow.indexOf('- name: Push versioned devtools image to Docker Hub')
|
|
const versionedStandard = workflow.indexOf('- name: Push versioned standard image to Docker Hub and GHCR')
|
|
const rollingTags = workflow.indexOf('- name: Promote Docker Hub rolling tags')
|
|
assert.ok(versionedDevtools >= 0)
|
|
assert.ok(versionedDevtools < versionedStandard)
|
|
assert.ok(versionedStandard < rollingTags)
|
|
const promotion = workflow.slice(rollingTags, workflow.indexOf('- name:', rollingTags + 1))
|
|
assert.ok(promotion.indexOf(':latest-devtools"') < promotion.indexOf(':latest"'))
|
|
assert.equal(workflow.split(gotifyWorkflow).length - 1, 1)
|
|
assert.match(workflow, /notify-result:[\s\S]*needs\.build\.result == 'success'[\s\S]*needs\.build\.result == 'failure'/)
|
|
assert.match(workflow, /DSH 镜像发布失败/)
|
|
assert.doesNotMatch(workflow, /\$\{GHCR_IMAGE\}:[^\n]*devtools/)
|
|
assert.doesNotMatch(workflow, /workflow_call:|gh release|DSH_RELEASE_PRIVATE_KEY|prepare-release\.mjs/)
|
|
})
|