Compare commits

...
1 Commits
Author SHA1 Message Date
ouyangxq f4362f6dfd fix(ci): reject TEST revisions without memory protection 2026-11-06 13:59:34 +08:00
2 changed files with 67 additions and 1 deletions
+8
View File
@@ -125,6 +125,14 @@ pipeline {
git cat-file -e "${RESOLVED_SOURCE_COMMIT}^{commit}"
git checkout --detach "${RESOLVED_SOURCE_COMMIT}"
test "$(git rev-parse HEAD)" = "$(git rev-parse "${RESOLVED_SOURCE_COMMIT}^{commit}")"
# TEST 重发旧提交也须包含限额文件及部署入口,避免重建时恢复无限制容器。
if [ "$RESOLVED_ENVIRONMENT" = "test" ]; then
if ! git cat-file -e "${RESOLVED_SOURCE_COMMIT}:deploy/docker-compose.test-limits.yml" 2>/dev/null ||
! git grep -q -F 'docker-compose.test-limits.yml' "$RESOLVED_SOURCE_COMMIT" -- deploy/scripts/deploy-built-service.sh; then
echo "目标 TEST 提交缺少完整内存保护,请选择包含限额修复的 test 提交" >&2
exit 1
fi
fi
if [ "$RESOLVED_ENVIRONMENT" = "production" ]; then
git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main
git merge-base --is-ancestor "$RESOLVED_SOURCE_COMMIT" origin/main || {
@@ -1,5 +1,9 @@
import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
import { existsSync } from 'node:fs'
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
import { spawnSync } from 'node:child_process'
import { tmpdir } from 'node:os'
import { dirname, join, resolve } from 'node:path'
import test from 'node:test'
const pipeline = (await readFile(new URL('../Jenkinsfile.service', import.meta.url), 'utf8'))
@@ -15,6 +19,60 @@ const serviceDockerfile = (await readFile(new URL('../deploy/docker/Dockerfile',
const serviceCompose = (await readFile(new URL('../deploy/docker-compose.yml', import.meta.url), 'utf8'))
.replaceAll('\r\n', '\n')
test('TEST rejects old source commits even when the workspace contains newer limits files', {
skip: process.platform === 'win32' && !existsSync('C:/Program Files/Git/bin/bash.exe')
}, async () => {
const directory = await mkdtemp(join(tmpdir(), 'test-memory-source-'))
const bash = process.platform === 'win32' ? 'C:/Program Files/Git/bin/bash.exe' : 'bash'
const guardStart = pipeline.indexOf('if [ "$RESOLVED_ENVIRONMENT" = "test" ]; then')
const guardEnd = pipeline.indexOf('if [ "$RESOLVED_ENVIRONMENT" = "production" ]; then', guardStart)
assert.ok(guardStart >= 0 && guardEnd > guardStart)
assert.ok(guardStart < pipeline.indexOf("stage('Maven 测试与打包')"))
const guard = pipeline.slice(guardStart, guardEnd)
// 在微型 Git 夹具中验证目标提交本身,避免工作区残留文件误放行旧版本。
const git = (...args) => {
const result = spawnSync('git', args, { cwd: directory, encoding: 'utf8' })
assert.equal(result.status, 0, result.stderr)
return result.stdout.trim()
}
// 构建提交沿用正常身份设置,但禁止夹具触发全局签名及提交钩子。
const commit = (message) => {
git('add', '.')
git('-c', 'user.name=Resource Contract', '-c', 'user.email=resource@example.invalid',
'-c', 'core.hooksPath=/dev/null', 'commit', '--no-gpg-sign', '-m', message)
return git('rev-parse', 'HEAD')
}
try {
git('init')
await writeFile(join(directory, 'README.md'), 'Source guard fixture\n')
const oldCommit = commit('old source')
await mkdir(join(directory, 'deploy/scripts'), { recursive: true })
await writeFile(join(directory, 'deploy/docker-compose.test-limits.yml'), 'services: {}\n')
await writeFile(join(directory, 'deploy/scripts/deploy-built-service.sh'), '#!/bin/sh\nexit 0\n')
const incompleteCommit = commit('limits file without deployment integration')
await writeFile(join(directory, 'deploy/scripts/deploy-built-service.sh'), directDeployScript)
const protectedCommit = commit('complete memory protection')
git('checkout', '--detach', oldCommit)
await mkdir(join(directory, 'deploy/scripts'), { recursive: true })
await writeFile(join(directory, 'deploy/docker-compose.test-limits.yml'), 'services: {}\n')
await writeFile(join(directory, 'deploy/scripts/deploy-built-service.sh'), directDeployScript)
for (const [environment, sourceCommit, status] of [
['test', oldCommit, 1], ['test', incompleteCommit, 1],
['test', protectedCommit, 0], ['production', oldCommit, 0]
]) {
const result = spawnSync(bash, ['-c', guard], {
cwd: directory, encoding: 'utf8', timeout: 10000,
env: { ...process.env, RESOLVED_ENVIRONMENT: environment, RESOLVED_SOURCE_COMMIT: sourceCommit }
})
assert.equal(result.status, status, result.stderr)
if (status !== 0) assert.match(result.stderr, /目标 TEST 提交缺少完整内存保护/)
}
} finally {
assert.equal(dirname(resolve(directory)), resolve(tmpdir()))
await rm(directory, { recursive: true, force: true })
}
})
test('both pipelines reject the placeholder registry from failed build 662 before Maven or Docker', () => {
for (const source of [coordinatorPipeline, pipeline]) {
assert.match(source, /string\(name: 'IMAGE_REPOSITORY', defaultValue: params\.IMAGE_REPOSITORY \?: ''/,