ci: 补齐NapCat运行时镜像构建下游Job
This commit is contained in:
parent
1e39a431b7
commit
114eef3195
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,4 +1,5 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
dist/
|
dist/
|
||||||
.turbo/
|
.turbo/
|
||||||
|
.kt-workspace/
|
||||||
*.log
|
*.log
|
||||||
|
|||||||
@ -59,9 +59,10 @@ pnpm run admin-login -- --url http://127.0.0.1:5999/#/auth/login
|
|||||||
|
|
||||||
- `ci/jenkins/KT-NapCatQQ-Upstream-Sync.Jenkinsfile` 是 NapCatQQ 上游审计入口,默认只调用 ktWorkflow 的 `napcat-upstream-audit` dry-run,保留人工审查边界。
|
- `ci/jenkins/KT-NapCatQQ-Upstream-Sync.Jenkinsfile` 是 NapCatQQ 上游审计入口,默认只调用 ktWorkflow 的 `napcat-upstream-audit` dry-run,保留人工审查边界。
|
||||||
- Codex 上游审计必须同时设置 `RUN_CODEX_AUDIT=true` 并填写 `CONFIRM_CODEX_WORKSPACE_WRITE=RUN_CODEX_WORKSPACE_WRITE_AUDIT`;这是因为 Codex runner 使用 workspace-write sandbox,不能作为定时任务默认行为。
|
- Codex 上游审计必须同时设置 `RUN_CODEX_AUDIT=true` 并填写 `CONFIRM_CODEX_WORKSPACE_WRITE=RUN_CODEX_WORKSPACE_WRITE_AUDIT`;这是因为 Codex runner 使用 workspace-write sandbox,不能作为定时任务默认行为。
|
||||||
- `ci/jenkins/KT-NapCatQQ-Runtime-Release.Jenkinsfile` 是运行时发布入口,先调用 `napcat-runtime-release-readiness`,再通过显式参数触发运行时镜像构建和 API promotion;下游 job 使用白名单 choice,避免任意 Jenkins job 名被传入。
|
- `ci/jenkins/KT-NapCatQQ-Runtime-Release.Jenkinsfile` 是运行时发布入口,先调用 `napcat-runtime-release-readiness`,再通过显式参数触发运行时镜像构建和 API promotion;下游 job 使用白名单 choice,避免任意 Jenkins job 名被传入,并把 upstream release commit / NapCat base image digest 透传到镜像构建证据。
|
||||||
|
- `ci/jenkins/KT-NapCatQQ-Runtime-Image-Build.Jenkinsfile` 是运行时镜像构建下游,只从 NAS 本地裸仓库 clone 经过审查的 NapCatQQ fork ref,构建 `NapCat.Shell`,调用 API 仓库 `scripts/napcat-desktop-cn-stage-build.mjs` 生成 staged context,执行 `docker build` 和容器内 `/ci/napcat-desktop-cn/verify.sh`;它不触发 API promotion、不执行 `kubectl`、不合并或推送 Git。
|
||||||
- 生产 API promotion 除了 `PROMOTE_API_RUNTIME=true` 和明确 image/profile 参数外,还必须填写 `CONFIRM_API_PROMOTION_TARGET=PROMOTE_KT_TEMPLATE_API_MAIN`。
|
- 生产 API promotion 除了 `PROMOTE_API_RUNTIME=true` 和明确 image/profile 参数外,还必须填写 `CONFIRM_API_PROMOTION_TARGET=PROMOTE_KT_TEMPLATE_API_MAIN`。
|
||||||
- 两个模板默认使用 NAS 工作区 `/vol1/docker/kt-codex/workspace/KT`,运行证据落在 `/vol1/docker/kt-codex/artifacts` 下。
|
- 三个模板默认使用 NAS 工作区 `/vol1/docker/kt-codex/workspace/KT`,运行证据落在 `/vol1/docker/kt-codex/artifacts` 下。
|
||||||
- 模板只保存入口和参数形状,不记录 Jenkins credential、Token、SSH key 或其他真实凭据。
|
- 模板只保存入口和参数形状,不记录 Jenkins credential、Token、SSH key 或其他真实凭据。
|
||||||
|
|
||||||
## MCP 客户端配置
|
## MCP 客户端配置
|
||||||
|
|||||||
190
ci/jenkins/KT-NapCatQQ-Runtime-Image-Build.Jenkinsfile
Normal file
190
ci/jenkins/KT-NapCatQQ-Runtime-Image-Build.Jenkinsfile
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
pipeline {
|
||||||
|
agent { label 'kt-node-agent' }
|
||||||
|
|
||||||
|
options {
|
||||||
|
buildDiscarder(logRotator(numToKeepStr: '30', artifactNumToKeepStr: '30'))
|
||||||
|
disableConcurrentBuilds()
|
||||||
|
skipDefaultCheckout(true)
|
||||||
|
timestamps()
|
||||||
|
timeout(time: 90, unit: 'MINUTES')
|
||||||
|
}
|
||||||
|
|
||||||
|
parameters {
|
||||||
|
string(name: 'NAPCAT_FORK_REF', defaultValue: 'main', description: 'Reviewed NapCatQQ fork branch, tag, or commit.')
|
||||||
|
string(name: 'UPSTREAM_RELEASE_TAG', defaultValue: 'unknown', description: 'Reviewed upstream release tag for marker metadata.')
|
||||||
|
string(name: 'UPSTREAM_RELEASE_COMMIT', defaultValue: '5c18a62530d87dbadf53d267002894faa6ca7e90', description: 'Reviewed upstream release commit for marker metadata.')
|
||||||
|
string(name: 'QQBOT_NAPCAT_IMAGE_OVERRIDE', defaultValue: '', description: 'Explicit runtime image tag to build, for example kt-napcat-desktop-cn:desktop-cn-v9.')
|
||||||
|
string(name: 'QQBOT_NAPCAT_DESKTOP_PROFILE_VERSION_OVERRIDE', defaultValue: '', description: 'Explicit desktop profile version paired with the runtime image tag.')
|
||||||
|
string(name: 'NAPCAT_BASE_IMAGE_DIGEST', defaultValue: 'mlikiowa/napcat-docker@sha256:9254ec12af101576c5eeb4910847abd1d219297bc6d9a35c52511e12500f0f45', description: 'Immutable NapCat base image digest used for Docker build.')
|
||||||
|
string(name: 'KT_ARTIFACT_ROOT', defaultValue: '/vol1/docker/kt-codex/artifacts/napcat-runtime-release', description: 'NAS artifact root for runtime release evidence.')
|
||||||
|
}
|
||||||
|
|
||||||
|
environment {
|
||||||
|
KT_WORKSPACE_ROOT = '/vol1/docker/kt-codex/workspace/KT'
|
||||||
|
API_ROOT = '/vol1/docker/kt-codex/workspace/KT/Node/kt-template-online-api'
|
||||||
|
NAPCAT_GIT_REPOSITORY = '/vol1/docker/kt-codex/git/NapCatQQ.git'
|
||||||
|
KT_WORKFLOW_RUNNER_IMAGE = 'kt-jenkins-node-agent:current'
|
||||||
|
}
|
||||||
|
|
||||||
|
stages {
|
||||||
|
stage('Validate parameters') {
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
requireRuntimeImageParameters(
|
||||||
|
params.NAPCAT_FORK_REF,
|
||||||
|
params.UPSTREAM_RELEASE_COMMIT,
|
||||||
|
params.QQBOT_NAPCAT_IMAGE_OVERRIDE,
|
||||||
|
params.QQBOT_NAPCAT_DESKTOP_PROFILE_VERSION_OVERRIDE,
|
||||||
|
params.NAPCAT_BASE_IMAGE_DIGEST
|
||||||
|
)
|
||||||
|
requireImageProfileMatch(
|
||||||
|
params.QQBOT_NAPCAT_IMAGE_OVERRIDE,
|
||||||
|
params.QQBOT_NAPCAT_DESKTOP_PROFILE_VERSION_OVERRIDE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Build and verify runtime image') {
|
||||||
|
steps {
|
||||||
|
sh '''
|
||||||
|
set -e
|
||||||
|
docker run --rm \
|
||||||
|
-e API_ROOT="$API_ROOT" \
|
||||||
|
-e BUILD_TAG="$BUILD_TAG" \
|
||||||
|
-e BUILD_URL="$BUILD_URL" \
|
||||||
|
-e COREPACK_HOME=/vol1/docker/kt-codex/runtime/corepack \
|
||||||
|
-e KT_ARTIFACT_ROOT="$KT_ARTIFACT_ROOT" \
|
||||||
|
-e KT_WORKSPACE_ROOT="$KT_WORKSPACE_ROOT" \
|
||||||
|
-e NAPCAT_BASE_IMAGE_DIGEST="$NAPCAT_BASE_IMAGE_DIGEST" \
|
||||||
|
-e NAPCAT_FORK_REF="$NAPCAT_FORK_REF" \
|
||||||
|
-e NAPCAT_GIT_REPOSITORY="$NAPCAT_GIT_REPOSITORY" \
|
||||||
|
-e QQBOT_NAPCAT_DESKTOP_PROFILE_VERSION_OVERRIDE="$QQBOT_NAPCAT_DESKTOP_PROFILE_VERSION_OVERRIDE" \
|
||||||
|
-e QQBOT_NAPCAT_IMAGE_OVERRIDE="$QQBOT_NAPCAT_IMAGE_OVERRIDE" \
|
||||||
|
-e UPSTREAM_RELEASE_COMMIT="$UPSTREAM_RELEASE_COMMIT" \
|
||||||
|
-e UPSTREAM_RELEASE_TAG="$UPSTREAM_RELEASE_TAG" \
|
||||||
|
-v /vol1/docker/kt-codex:/vol1/docker/kt-codex \
|
||||||
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
|
--entrypoint bash "$KT_WORKFLOW_RUNNER_IMAGE" -lc '
|
||||||
|
set -euo pipefail
|
||||||
|
source /vol1/docker/kt-codex/runtime/nvm/nvm.sh
|
||||||
|
corepack prepare pnpm@10.28.2 --activate
|
||||||
|
ARTIFACTS_BASE_REAL=$(realpath -m /vol1/docker/kt-codex/artifacts)
|
||||||
|
ARTIFACT_ROOT_REAL=$(realpath -m "$KT_ARTIFACT_ROOT")
|
||||||
|
case "$ARTIFACT_ROOT_REAL" in
|
||||||
|
"$ARTIFACTS_BASE_REAL"/*) ;;
|
||||||
|
*)
|
||||||
|
echo "KT_ARTIFACT_ROOT must stay under /vol1/docker/kt-codex/artifacts" >&2
|
||||||
|
exit 2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
BUILD_ID_SAFE=$(printf "%s" "${BUILD_TAG:-manual}" | tr "/ " "__")
|
||||||
|
ARTIFACT_DIR="$ARTIFACT_ROOT_REAL/runtime-image-build"
|
||||||
|
BUILD_WORKDIR="$ARTIFACT_ROOT_REAL/runtime-image-build-workspace/$BUILD_ID_SAFE"
|
||||||
|
NAPCAT_ROOT="$BUILD_WORKDIR/NapCatQQ"
|
||||||
|
STAGE_OUT=".kt-workspace/napcat-desktop-cn-build-runtime-image"
|
||||||
|
VERIFY_NAME="kt-napcat-runtime-image-verify-$BUILD_ID_SAFE"
|
||||||
|
cleanup() {
|
||||||
|
docker rm -f "$VERIFY_NAME" >/dev/null 2>&1 || true
|
||||||
|
rm -rf "$BUILD_WORKDIR"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
rm -rf "$ARTIFACT_DIR" "$BUILD_WORKDIR"
|
||||||
|
mkdir -p "$ARTIFACT_DIR" "$BUILD_WORKDIR"
|
||||||
|
git clone "$NAPCAT_GIT_REPOSITORY" "$NAPCAT_ROOT"
|
||||||
|
cd "$NAPCAT_ROOT"
|
||||||
|
git checkout --detach "$NAPCAT_FORK_REF"
|
||||||
|
git rev-parse HEAD > "$ARTIFACT_DIR/fork-commit.txt"
|
||||||
|
git status --short > "$ARTIFACT_DIR/fork-status.txt"
|
||||||
|
pnpm install --frozen-lockfile
|
||||||
|
pnpm --filter napcat-webui-frontend run build
|
||||||
|
pnpm run build:shell
|
||||||
|
pnpm run build:framework
|
||||||
|
|
||||||
|
cd "$API_ROOT"
|
||||||
|
node scripts/napcat-desktop-cn-stage-build.mjs \
|
||||||
|
--napcat-root "$NAPCAT_ROOT" \
|
||||||
|
--out "$STAGE_OUT" \
|
||||||
|
--upstream-release-tag "$UPSTREAM_RELEASE_TAG" \
|
||||||
|
--upstream-release-commit "$UPSTREAM_RELEASE_COMMIT" \
|
||||||
|
--napcat-base-image-digest "$NAPCAT_BASE_IMAGE_DIGEST" \
|
||||||
|
--jenkins-build-url "$BUILD_URL" \
|
||||||
|
| tee "$ARTIFACT_DIR/stage-build.json"
|
||||||
|
cp "$STAGE_OUT/ci/napcat-desktop-cn/fork-artifact.json" "$ARTIFACT_DIR/fork-artifact.json"
|
||||||
|
docker build \
|
||||||
|
--build-arg NAPCAT_BASE_IMAGE="$NAPCAT_BASE_IMAGE_DIGEST" \
|
||||||
|
-t "$QQBOT_NAPCAT_IMAGE_OVERRIDE" \
|
||||||
|
-f "$STAGE_OUT/ci/napcat-desktop-cn/Dockerfile" \
|
||||||
|
"$STAGE_OUT"
|
||||||
|
docker run -d --name "$VERIFY_NAME" "$QQBOT_NAPCAT_IMAGE_OVERRIDE" >/dev/null
|
||||||
|
sleep 3
|
||||||
|
docker exec "$VERIFY_NAME" sh /ci/napcat-desktop-cn/verify.sh
|
||||||
|
docker image inspect "$QQBOT_NAPCAT_IMAGE_OVERRIDE" \
|
||||||
|
--format "tag={{index .RepoTags 0}} id={{.Id}} size={{.Size}} created={{.Created}} repoDigests={{json .RepoDigests}}" \
|
||||||
|
| tee "$ARTIFACT_DIR/image.txt"
|
||||||
|
'
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
sh '''
|
||||||
|
set +e
|
||||||
|
docker run --rm \
|
||||||
|
-e KT_ARTIFACT_ROOT="$KT_ARTIFACT_ROOT" \
|
||||||
|
-e WORKSPACE="$WORKSPACE" \
|
||||||
|
-v /vol1/docker/kt-codex:/vol1/docker/kt-codex \
|
||||||
|
-v kt-node-agent-workdir:/home/jenkins/agent \
|
||||||
|
--entrypoint bash "$KT_WORKFLOW_RUNNER_IMAGE" -lc '
|
||||||
|
set +e
|
||||||
|
rm -rf "$WORKSPACE/artifacts"
|
||||||
|
mkdir -p "$WORKSPACE/artifacts/napcat-runtime-image-build"
|
||||||
|
cp -a "$KT_ARTIFACT_ROOT/runtime-image-build/." "$WORKSPACE/artifacts/napcat-runtime-image-build/" 2>/dev/null || true
|
||||||
|
'
|
||||||
|
'''
|
||||||
|
archiveArtifacts artifacts: 'artifacts/napcat-runtime-image-build/**', allowEmptyArchive: true, fingerprint: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops runtime image builds unless all immutable release parameters are present.
|
||||||
|
* @param forkRef Reviewed fork branch, tag, or commit that is cloned into the disposable build workspace.
|
||||||
|
* @param upstreamReleaseCommit Reviewed upstream release commit stored in the image marker.
|
||||||
|
* @param imageTag Reviewed image tag that will be created on NAS Docker.
|
||||||
|
* @param profile Reviewed desktop profile version paired with the image tag.
|
||||||
|
* @param baseImageDigest Immutable upstream/base Docker image digest in `repo@sha256` form.
|
||||||
|
* @return Nothing; aborts this pipeline through `error` when required values are missing.
|
||||||
|
*/
|
||||||
|
void requireRuntimeImageParameters(String forkRef, String upstreamReleaseCommit, String imageTag, String profile, String baseImageDigest) {
|
||||||
|
if (!forkRef?.trim()) {
|
||||||
|
error('NAPCAT_FORK_REF is required before runtime image build.')
|
||||||
|
}
|
||||||
|
if (!upstreamReleaseCommit?.trim()) {
|
||||||
|
error('UPSTREAM_RELEASE_COMMIT is required before runtime image build.')
|
||||||
|
}
|
||||||
|
if (!imageTag?.trim()) {
|
||||||
|
error('QQBOT_NAPCAT_IMAGE_OVERRIDE is required before runtime image build.')
|
||||||
|
}
|
||||||
|
if (!profile?.trim()) {
|
||||||
|
error('QQBOT_NAPCAT_DESKTOP_PROFILE_VERSION_OVERRIDE is required before runtime image build.')
|
||||||
|
}
|
||||||
|
if (!baseImageDigest?.trim()?.contains('@sha256:')) {
|
||||||
|
error('NAPCAT_BASE_IMAGE_DIGEST must be an immutable repo@sha256 digest.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures the promoted image tag and desktop profile cannot drift silently.
|
||||||
|
* @param imageTag Runtime image tag, expected to end with the profile tag suffix.
|
||||||
|
* @param profile Runtime desktop profile value passed to API promotion.
|
||||||
|
* @return Nothing; aborts this pipeline through `error` when values drift.
|
||||||
|
*/
|
||||||
|
void requireImageProfileMatch(String imageTag, String profile) {
|
||||||
|
if (!imageTag?.trim()?.endsWith(":${profile?.trim()}")) {
|
||||||
|
error('QQBOT_NAPCAT_IMAGE_OVERRIDE tag suffix must match QQBOT_NAPCAT_DESKTOP_PROFILE_VERSION_OVERRIDE.')
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -12,6 +12,8 @@ pipeline {
|
|||||||
parameters {
|
parameters {
|
||||||
string(name: 'NAPCAT_FORK_REF', defaultValue: 'main', description: 'Reviewed NapCatQQ fork branch, tag, or commit.')
|
string(name: 'NAPCAT_FORK_REF', defaultValue: 'main', description: 'Reviewed NapCatQQ fork branch, tag, or commit.')
|
||||||
string(name: 'UPSTREAM_RELEASE_TAG', defaultValue: '', description: 'Reviewed upstream release tag, when applicable.')
|
string(name: 'UPSTREAM_RELEASE_TAG', defaultValue: '', description: 'Reviewed upstream release tag, when applicable.')
|
||||||
|
string(name: 'UPSTREAM_RELEASE_COMMIT', defaultValue: '5c18a62530d87dbadf53d267002894faa6ca7e90', description: 'Reviewed upstream release commit used for runtime marker metadata.')
|
||||||
|
string(name: 'NAPCAT_BASE_IMAGE_DIGEST', defaultValue: 'mlikiowa/napcat-docker@sha256:9254ec12af101576c5eeb4910847abd1d219297bc6d9a35c52511e12500f0f45', description: 'Immutable NapCat base image digest used by the runtime image build.')
|
||||||
string(name: 'QQBOT_NAPCAT_IMAGE_OVERRIDE', defaultValue: '', description: 'Explicit runtime image tag to build or promote, for example kt-napcat-desktop-cn:desktop-cn-v9.')
|
string(name: 'QQBOT_NAPCAT_IMAGE_OVERRIDE', defaultValue: '', description: 'Explicit runtime image tag to build or promote, for example kt-napcat-desktop-cn:desktop-cn-v9.')
|
||||||
string(name: 'QQBOT_NAPCAT_DESKTOP_PROFILE_VERSION_OVERRIDE', defaultValue: '', description: 'Explicit API desktop profile version override.')
|
string(name: 'QQBOT_NAPCAT_DESKTOP_PROFILE_VERSION_OVERRIDE', defaultValue: '', description: 'Explicit API desktop profile version override.')
|
||||||
booleanParam(name: 'BUILD_RUNTIME_IMAGE', defaultValue: false, description: 'When true, trigger the reviewed Jenkins runtime image build/publish job.')
|
booleanParam(name: 'BUILD_RUNTIME_IMAGE', defaultValue: false, description: 'When true, trigger the reviewed Jenkins runtime image build/publish job.')
|
||||||
@ -38,6 +40,8 @@ pipeline {
|
|||||||
-e KT_ARTIFACT_ROOT="$KT_ARTIFACT_ROOT" \
|
-e KT_ARTIFACT_ROOT="$KT_ARTIFACT_ROOT" \
|
||||||
-e NAPCAT_FORK_REF="$NAPCAT_FORK_REF" \
|
-e NAPCAT_FORK_REF="$NAPCAT_FORK_REF" \
|
||||||
-e UPSTREAM_RELEASE_TAG="$UPSTREAM_RELEASE_TAG" \
|
-e UPSTREAM_RELEASE_TAG="$UPSTREAM_RELEASE_TAG" \
|
||||||
|
-e UPSTREAM_RELEASE_COMMIT="$UPSTREAM_RELEASE_COMMIT" \
|
||||||
|
-e NAPCAT_BASE_IMAGE_DIGEST="$NAPCAT_BASE_IMAGE_DIGEST" \
|
||||||
-e QQBOT_NAPCAT_IMAGE_OVERRIDE="$QQBOT_NAPCAT_IMAGE_OVERRIDE" \
|
-e QQBOT_NAPCAT_IMAGE_OVERRIDE="$QQBOT_NAPCAT_IMAGE_OVERRIDE" \
|
||||||
-e QQBOT_NAPCAT_DESKTOP_PROFILE_VERSION_OVERRIDE="$QQBOT_NAPCAT_DESKTOP_PROFILE_VERSION_OVERRIDE" \
|
-e QQBOT_NAPCAT_DESKTOP_PROFILE_VERSION_OVERRIDE="$QQBOT_NAPCAT_DESKTOP_PROFILE_VERSION_OVERRIDE" \
|
||||||
-e COREPACK_HOME=/vol1/docker/kt-codex/runtime/corepack \
|
-e COREPACK_HOME=/vol1/docker/kt-codex/runtime/corepack \
|
||||||
@ -72,6 +76,8 @@ pipeline {
|
|||||||
parameters: [
|
parameters: [
|
||||||
string(name: 'NAPCAT_FORK_REF', value: params.NAPCAT_FORK_REF),
|
string(name: 'NAPCAT_FORK_REF', value: params.NAPCAT_FORK_REF),
|
||||||
string(name: 'UPSTREAM_RELEASE_TAG', value: params.UPSTREAM_RELEASE_TAG),
|
string(name: 'UPSTREAM_RELEASE_TAG', value: params.UPSTREAM_RELEASE_TAG),
|
||||||
|
string(name: 'UPSTREAM_RELEASE_COMMIT', value: params.UPSTREAM_RELEASE_COMMIT),
|
||||||
|
string(name: 'NAPCAT_BASE_IMAGE_DIGEST', value: params.NAPCAT_BASE_IMAGE_DIGEST),
|
||||||
string(name: 'QQBOT_NAPCAT_IMAGE_OVERRIDE', value: params.QQBOT_NAPCAT_IMAGE_OVERRIDE),
|
string(name: 'QQBOT_NAPCAT_IMAGE_OVERRIDE', value: params.QQBOT_NAPCAT_IMAGE_OVERRIDE),
|
||||||
string(name: 'QQBOT_NAPCAT_DESKTOP_PROFILE_VERSION_OVERRIDE', value: params.QQBOT_NAPCAT_DESKTOP_PROFILE_VERSION_OVERRIDE),
|
string(name: 'QQBOT_NAPCAT_DESKTOP_PROFILE_VERSION_OVERRIDE', value: params.QQBOT_NAPCAT_DESKTOP_PROFILE_VERSION_OVERRIDE),
|
||||||
string(name: 'KT_ARTIFACT_ROOT', value: "${env.KT_ARTIFACT_ROOT}/napcat-runtime-release")
|
string(name: 'KT_ARTIFACT_ROOT', value: "${env.KT_ARTIFACT_ROOT}/napcat-runtime-release")
|
||||||
|
|||||||
@ -524,6 +524,12 @@ export async function runSelfTest(): Promise<void> {
|
|||||||
),
|
),
|
||||||
"utf8",
|
"utf8",
|
||||||
).replace(/\r\n/g, "\n");
|
).replace(/\r\n/g, "\n");
|
||||||
|
const runtimeImageBuildJenkinsTemplate = readFileSync(
|
||||||
|
resolveInsideRoot(
|
||||||
|
"mcp/ktWorkflow/ci/jenkins/KT-NapCatQQ-Runtime-Image-Build.Jenkinsfile",
|
||||||
|
),
|
||||||
|
"utf8",
|
||||||
|
).replace(/\r\n/g, "\n");
|
||||||
for (const expected of [
|
for (const expected of [
|
||||||
"RUN_CODEX_AUDIT",
|
"RUN_CODEX_AUDIT",
|
||||||
"CONFIRM_CODEX_WORKSPACE_WRITE",
|
"CONFIRM_CODEX_WORKSPACE_WRITE",
|
||||||
@ -626,6 +632,10 @@ export async function runSelfTest(): Promise<void> {
|
|||||||
runtimeReleaseJenkinsTemplate,
|
runtimeReleaseJenkinsTemplate,
|
||||||
"NapCat runtime",
|
"NapCat runtime",
|
||||||
);
|
);
|
||||||
|
assertNapCatJenkinsUsesNasAgent(
|
||||||
|
runtimeImageBuildJenkinsTemplate,
|
||||||
|
"NapCat runtime image build",
|
||||||
|
);
|
||||||
/**
|
/**
|
||||||
* Verifies that a NapCat Jenkins template runs ktWorkflow inside a helper container with NAS mounts.
|
* Verifies that a NapCat Jenkins template runs ktWorkflow inside a helper container with NAS mounts.
|
||||||
* @param template - Full Jenkinsfile text that Jenkins will execute.
|
* @param template - Full Jenkinsfile text that Jenkins will execute.
|
||||||
@ -658,6 +668,44 @@ export async function runSelfTest(): Promise<void> {
|
|||||||
runtimeReleaseJenkinsTemplate,
|
runtimeReleaseJenkinsTemplate,
|
||||||
"NapCat runtime",
|
"NapCat runtime",
|
||||||
);
|
);
|
||||||
|
assertNapCatJenkinsUsesWorkflowRunner(
|
||||||
|
runtimeImageBuildJenkinsTemplate,
|
||||||
|
"NapCat runtime image build",
|
||||||
|
);
|
||||||
|
/**
|
||||||
|
* Ensures destructive Jenkins artifact cleanup is bounded by canonical NAS paths.
|
||||||
|
* @param template - Full Jenkinsfile text whose shell guard will run before `rm -rf`.
|
||||||
|
* @param name - Human-readable template name for the self-test failure.
|
||||||
|
* @returns Nothing; throws when the guard accepts raw `..` path prefixes.
|
||||||
|
*/
|
||||||
|
const assertNapCatJenkinsCanonicalArtifactRoot = (
|
||||||
|
template: string,
|
||||||
|
name: string,
|
||||||
|
): void => {
|
||||||
|
for (const expected of [
|
||||||
|
'ARTIFACTS_BASE_REAL=$(realpath -m /vol1/docker/kt-codex/artifacts)',
|
||||||
|
'ARTIFACT_ROOT_REAL=$(realpath -m "$KT_ARTIFACT_ROOT")',
|
||||||
|
'case "$ARTIFACT_ROOT_REAL" in',
|
||||||
|
'"$ARTIFACTS_BASE_REAL"/*) ;;',
|
||||||
|
'ARTIFACT_DIR="$ARTIFACT_ROOT_REAL/runtime-image-build"',
|
||||||
|
'BUILD_WORKDIR="$ARTIFACT_ROOT_REAL/runtime-image-build-workspace/$BUILD_ID_SAFE"',
|
||||||
|
]) {
|
||||||
|
if (!template.includes(expected)) {
|
||||||
|
throw new Error(
|
||||||
|
`${name} Jenkins template must canonicalize artifact roots before cleanup: ${expected}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (template.includes('case "$KT_ARTIFACT_ROOT" in')) {
|
||||||
|
throw new Error(
|
||||||
|
`${name} Jenkins template must not validate destructive cleanup paths with a raw prefix check`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
assertNapCatJenkinsCanonicalArtifactRoot(
|
||||||
|
runtimeImageBuildJenkinsTemplate,
|
||||||
|
"NapCat runtime image build",
|
||||||
|
);
|
||||||
for (const forbidden of ["git merge", "git push", "docker build", "kubectl"]) {
|
for (const forbidden of ["git merge", "git push", "docker build", "kubectl"]) {
|
||||||
if (upstreamSyncJenkinsTemplate.includes(forbidden)) {
|
if (upstreamSyncJenkinsTemplate.includes(forbidden)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@ -666,6 +714,8 @@ export async function runSelfTest(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const expected of [
|
for (const expected of [
|
||||||
|
"UPSTREAM_RELEASE_COMMIT",
|
||||||
|
"NAPCAT_BASE_IMAGE_DIGEST",
|
||||||
"choice(name: 'RUNTIME_BUILD_JOB'",
|
"choice(name: 'RUNTIME_BUILD_JOB'",
|
||||||
"choice(name: 'API_PROMOTION_JOB'",
|
"choice(name: 'API_PROMOTION_JOB'",
|
||||||
"CONFIRM_API_PROMOTION_TARGET",
|
"CONFIRM_API_PROMOTION_TARGET",
|
||||||
@ -748,6 +798,45 @@ export async function runSelfTest(): Promise<void> {
|
|||||||
"NapCat runtime Jenkins API promotion confirmation must run before API build job",
|
"NapCat runtime Jenkins API promotion confirmation must run before API build job",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
for (const expected of [
|
||||||
|
"NAPCAT_FORK_REF",
|
||||||
|
"NAPCAT_GIT_REPOSITORY = '/vol1/docker/kt-codex/git/NapCatQQ.git'",
|
||||||
|
"QQBOT_NAPCAT_IMAGE_OVERRIDE",
|
||||||
|
"QQBOT_NAPCAT_DESKTOP_PROFILE_VERSION_OVERRIDE",
|
||||||
|
"NAPCAT_BASE_IMAGE_DIGEST",
|
||||||
|
"requireRuntimeImageParameters(",
|
||||||
|
"requireImageProfileMatch(",
|
||||||
|
"-v /var/run/docker.sock:/var/run/docker.sock",
|
||||||
|
"git clone \"$NAPCAT_GIT_REPOSITORY\" \"$NAPCAT_ROOT\"",
|
||||||
|
"BUILD_WORKDIR=\"$ARTIFACT_ROOT_REAL/runtime-image-build-workspace/$BUILD_ID_SAFE\"",
|
||||||
|
"KT_ARTIFACT_ROOT must stay under /vol1/docker/kt-codex/artifacts",
|
||||||
|
"scripts/napcat-desktop-cn-stage-build.mjs",
|
||||||
|
"--napcat-root \"$NAPCAT_ROOT\"",
|
||||||
|
"--napcat-base-image-digest \"$NAPCAT_BASE_IMAGE_DIGEST\"",
|
||||||
|
"docker build",
|
||||||
|
"docker exec \"$VERIFY_NAME\" sh /ci/napcat-desktop-cn/verify.sh",
|
||||||
|
"archiveArtifacts artifacts: 'artifacts/napcat-runtime-image-build/**'",
|
||||||
|
]) {
|
||||||
|
if (!runtimeImageBuildJenkinsTemplate.includes(expected)) {
|
||||||
|
throw new Error(
|
||||||
|
`NapCat runtime image build Jenkins template missing: ${expected}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const forbidden of [
|
||||||
|
"PROMOTE_API_RUNTIME",
|
||||||
|
"CONFIRM_API_PROMOTION_TARGET",
|
||||||
|
"kubectl",
|
||||||
|
"git push",
|
||||||
|
"git merge",
|
||||||
|
"/vol1/docker/kt-codex/workspace/KT/GitHub/NapCatQQ",
|
||||||
|
]) {
|
||||||
|
if (runtimeImageBuildJenkinsTemplate.includes(forbidden)) {
|
||||||
|
throw new Error(
|
||||||
|
`NapCat runtime image build Jenkins template forbidden text leaked: ${forbidden}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
const codexExecutionArtifactRoot = resolveInsideRoot(
|
const codexExecutionArtifactRoot = resolveInsideRoot(
|
||||||
".kt-workspace/test-artifacts/napcat-codex-runner-self-test",
|
".kt-workspace/test-artifacts/napcat-codex-runner-self-test",
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user