fix: 收紧NapCatQQ自动化Jenkins入口
This commit is contained in:
parent
4fc7c4a776
commit
36da962bb0
@ -57,8 +57,10 @@ pnpm run admin-login -- --url http://127.0.0.1:5999/#/auth/login
|
|||||||
|
|
||||||
## Jenkins 入口模板
|
## Jenkins 入口模板
|
||||||
|
|
||||||
- `ci/jenkins/KT-NapCatQQ-Upstream-Sync.Jenkinsfile` 是 NapCatQQ 上游审计入口,只调用 ktWorkflow 的 `napcat-upstream-audit`,保留人工审查边界。
|
- `ci/jenkins/KT-NapCatQQ-Upstream-Sync.Jenkinsfile` 是 NapCatQQ 上游审计入口,默认只调用 ktWorkflow 的 `napcat-upstream-audit` dry-run,保留人工审查边界。
|
||||||
- `ci/jenkins/KT-NapCatQQ-Runtime-Release.Jenkinsfile` 是运行时发布入口,先调用 `napcat-runtime-release-readiness`,再通过显式参数触发运行时镜像构建和 API promotion。
|
- 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 名被传入。
|
||||||
|
- 生产 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 或其他真实凭据。
|
||||||
|
|
||||||
|
|||||||
@ -13,9 +13,10 @@ pipeline {
|
|||||||
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.')
|
||||||
string(name: 'RUNTIME_BUILD_JOB', defaultValue: 'KT-NapCatQQ/Runtime-Image-Build', description: 'Jenkins job that performs the reviewed Docker runtime build and publish.')
|
choice(name: 'RUNTIME_BUILD_JOB', choices: ['KT-NapCatQQ/Runtime-Image-Build'], description: 'Whitelisted Jenkins job that performs the reviewed runtime image build and publish.')
|
||||||
booleanParam(name: 'PROMOTE_API_RUNTIME', defaultValue: false, description: 'When true, trigger API promotion with explicit image/profile overrides.')
|
booleanParam(name: 'PROMOTE_API_RUNTIME', defaultValue: false, description: 'When true, trigger API promotion with explicit image/profile overrides.')
|
||||||
string(name: 'API_PROMOTION_JOB', defaultValue: 'KT-Template/KT-Template-API/main', description: 'API Jenkins job that accepts NapCat runtime override parameters.')
|
choice(name: 'API_PROMOTION_JOB', choices: ['KT-Template/KT-Template-API/main'], description: 'Whitelisted API Jenkins job that accepts NapCat runtime override parameters.')
|
||||||
|
string(name: 'CONFIRM_API_PROMOTION_TARGET', defaultValue: '', description: 'Type PROMOTE_KT_TEMPLATE_API_MAIN before triggering the production API promotion job.')
|
||||||
}
|
}
|
||||||
|
|
||||||
environment {
|
environment {
|
||||||
@ -29,7 +30,7 @@ pipeline {
|
|||||||
steps {
|
steps {
|
||||||
sh '''
|
sh '''
|
||||||
set -e
|
set -e
|
||||||
pnpm --dir /vol1/docker/kt-codex/workspace/KT/mcp/ktWorkflow run napcat-runtime-release-readiness -- \
|
pnpm --dir "$KT_WORKFLOW_DIR" run napcat-runtime-release-readiness -- \
|
||||||
--artifact-root "$KT_ARTIFACT_ROOT/napcat-runtime-release" \
|
--artifact-root "$KT_ARTIFACT_ROOT/napcat-runtime-release" \
|
||||||
--fork-branch "$NAPCAT_FORK_REF" \
|
--fork-branch "$NAPCAT_FORK_REF" \
|
||||||
--upstream-release-tag "$UPSTREAM_RELEASE_TAG" \
|
--upstream-release-tag "$UPSTREAM_RELEASE_TAG" \
|
||||||
@ -45,6 +46,9 @@ pipeline {
|
|||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
script {
|
script {
|
||||||
|
if (params.CONFIRM_API_PROMOTION_TARGET != 'PROMOTE_KT_TEMPLATE_API_MAIN') {
|
||||||
|
error('CONFIRM_API_PROMOTION_TARGET must equal PROMOTE_KT_TEMPLATE_API_MAIN before API promotion runs.')
|
||||||
|
}
|
||||||
requireRuntimeOverrides(
|
requireRuntimeOverrides(
|
||||||
params.QQBOT_NAPCAT_IMAGE_OVERRIDE,
|
params.QQBOT_NAPCAT_IMAGE_OVERRIDE,
|
||||||
params.QQBOT_NAPCAT_DESKTOP_PROFILE_VERSION_OVERRIDE
|
params.QQBOT_NAPCAT_DESKTOP_PROFILE_VERSION_OVERRIDE
|
||||||
|
|||||||
@ -7,15 +7,43 @@ pipeline {
|
|||||||
timeout(time: 30, unit: 'MINUTES')
|
timeout(time: 30, unit: 'MINUTES')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parameters {
|
||||||
|
booleanParam(name: 'RUN_CODEX_AUDIT', defaultValue: false, description: 'When true, allow the optional Codex workspace-write audit after the default dry-run audit.')
|
||||||
|
string(name: 'CONFIRM_CODEX_WORKSPACE_WRITE', defaultValue: '', description: 'Type RUN_CODEX_WORKSPACE_WRITE_AUDIT to confirm Codex may run with a workspace-write sandbox.')
|
||||||
|
}
|
||||||
|
|
||||||
|
environment {
|
||||||
|
KT_WORKFLOW_DIR = '/vol1/docker/kt-codex/workspace/KT/mcp/ktWorkflow'
|
||||||
|
KT_ARTIFACT_ROOT = '/vol1/docker/kt-codex/artifacts'
|
||||||
|
}
|
||||||
|
|
||||||
stages {
|
stages {
|
||||||
stage('ktWorkflow upstream audit') {
|
stage('ktWorkflow upstream audit dry-run') {
|
||||||
steps {
|
steps {
|
||||||
sh '''
|
sh '''
|
||||||
set -e
|
set -e
|
||||||
pnpm --dir /vol1/docker/kt-codex/workspace/KT/mcp/ktWorkflow run napcat-upstream-audit -- \
|
pnpm --dir "$KT_WORKFLOW_DIR" run napcat-upstream-audit -- \
|
||||||
|
--artifact-root "$KT_ARTIFACT_ROOT/napcat-upstream-sync/dry-run"
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('ktWorkflow upstream audit with Codex') {
|
||||||
|
when {
|
||||||
|
expression { return params.RUN_CODEX_AUDIT }
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
if (params.CONFIRM_CODEX_WORKSPACE_WRITE != 'RUN_CODEX_WORKSPACE_WRITE_AUDIT') {
|
||||||
|
error('CONFIRM_CODEX_WORKSPACE_WRITE must equal RUN_CODEX_WORKSPACE_WRITE_AUDIT before Codex workspace-write audit runs.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sh '''
|
||||||
|
set -e
|
||||||
|
pnpm --dir "$KT_WORKFLOW_DIR" run napcat-upstream-audit -- \
|
||||||
--execute \
|
--execute \
|
||||||
--use-codex \
|
--use-codex \
|
||||||
--artifact-root /vol1/docker/kt-codex/artifacts/napcat-upstream-sync
|
--artifact-root "$KT_ARTIFACT_ROOT/napcat-upstream-sync/codex"
|
||||||
'''
|
'''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -449,6 +449,81 @@ export async function runSelfTest(): Promise<void> {
|
|||||||
if (codexCommand.includes("git push") || codexCommand.includes("kubectl")) {
|
if (codexCommand.includes("git push") || codexCommand.includes("kubectl")) {
|
||||||
throw new Error("NapCat Codex runner mutation guard self-check failed");
|
throw new Error("NapCat Codex runner mutation guard self-check failed");
|
||||||
}
|
}
|
||||||
|
const upstreamSyncJenkinsTemplate = readFileSync(
|
||||||
|
resolveInsideRoot(
|
||||||
|
"mcp/ktWorkflow/ci/jenkins/KT-NapCatQQ-Upstream-Sync.Jenkinsfile",
|
||||||
|
),
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
const runtimeReleaseJenkinsTemplate = readFileSync(
|
||||||
|
resolveInsideRoot(
|
||||||
|
"mcp/ktWorkflow/ci/jenkins/KT-NapCatQQ-Runtime-Release.Jenkinsfile",
|
||||||
|
),
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
for (const expected of [
|
||||||
|
"RUN_CODEX_AUDIT",
|
||||||
|
"CONFIRM_CODEX_WORKSPACE_WRITE",
|
||||||
|
"RUN_CODEX_WORKSPACE_WRITE_AUDIT",
|
||||||
|
]) {
|
||||||
|
if (!upstreamSyncJenkinsTemplate.includes(expected)) {
|
||||||
|
throw new Error(`NapCat upstream Jenkins template missing: ${expected}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const defaultUpstreamAuditIndex = upstreamSyncJenkinsTemplate.indexOf(
|
||||||
|
"run napcat-upstream-audit --",
|
||||||
|
);
|
||||||
|
const firstUpstreamExecuteIndex =
|
||||||
|
upstreamSyncJenkinsTemplate.indexOf("--execute");
|
||||||
|
if (
|
||||||
|
defaultUpstreamAuditIndex === -1 ||
|
||||||
|
(firstUpstreamExecuteIndex !== -1 &&
|
||||||
|
defaultUpstreamAuditIndex > firstUpstreamExecuteIndex)
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
"NapCat upstream Jenkins template must run dry-run audit before execute",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const defaultUpstreamAuditCall = upstreamSyncJenkinsTemplate.slice(
|
||||||
|
defaultUpstreamAuditIndex,
|
||||||
|
firstUpstreamExecuteIndex === -1
|
||||||
|
? upstreamSyncJenkinsTemplate.length
|
||||||
|
: firstUpstreamExecuteIndex,
|
||||||
|
);
|
||||||
|
if (defaultUpstreamAuditCall.includes("--use-codex")) {
|
||||||
|
throw new Error(
|
||||||
|
"NapCat upstream Jenkins template default audit must not use Codex",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (const forbidden of ["git merge", "git push", "docker build", "kubectl"]) {
|
||||||
|
if (upstreamSyncJenkinsTemplate.includes(forbidden)) {
|
||||||
|
throw new Error(
|
||||||
|
`NapCat upstream Jenkins template forbidden command leaked: ${forbidden}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const expected of [
|
||||||
|
"choice(name: 'RUNTIME_BUILD_JOB'",
|
||||||
|
"choice(name: 'API_PROMOTION_JOB'",
|
||||||
|
"CONFIRM_API_PROMOTION_TARGET",
|
||||||
|
"PROMOTE_KT_TEMPLATE_API_MAIN",
|
||||||
|
]) {
|
||||||
|
if (!runtimeReleaseJenkinsTemplate.includes(expected)) {
|
||||||
|
throw new Error(`NapCat runtime Jenkins template missing: ${expected}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const forbidden of [
|
||||||
|
"string(name: 'RUNTIME_BUILD_JOB'",
|
||||||
|
"string(name: 'API_PROMOTION_JOB'",
|
||||||
|
"git merge",
|
||||||
|
"git push",
|
||||||
|
]) {
|
||||||
|
if (runtimeReleaseJenkinsTemplate.includes(forbidden)) {
|
||||||
|
throw new Error(
|
||||||
|
`NapCat runtime 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