fix(ci): bound Docker image build resources
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { basename, dirname, join, resolve } from 'node:path'
|
||||
import { spawnSync } from 'node:child_process'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import test from 'node:test'
|
||||
|
||||
const root = fileURLToPath(new URL('../', import.meta.url))
|
||||
const wrapper = join(root, 'scripts/build-image.sh')
|
||||
const bash = process.platform === 'win32' ? 'C:/Program Files/Git/bin/bash.exe' : 'bash'
|
||||
const shellAvailable = process.platform !== 'win32' || existsSync(bash)
|
||||
|
||||
// 使用替身验证参数、内存预检和退出码,不启动 Docker 或访问部署环境。
|
||||
function runBuild(availableMemory, childStatus = '0') {
|
||||
const directory = mkdtempSync(join(tmpdir(), 'frontend-build-resource-'))
|
||||
try {
|
||||
writeFileSync(join(directory, 'awk'), '#!/bin/sh\nprintf "%s\\n" "$TEST_AVAILABLE_MEMORY"\n', { mode: 0o755 })
|
||||
writeFileSync(join(directory, 'timeout'), '#!/bin/sh\nprintf "%s\\n" watchdog "$@"\nshift 3\nexec "$@"\n', { mode: 0o755 })
|
||||
writeFileSync(join(directory, 'docker'), '#!/bin/sh\nprintf "%s\\n" "builder=$DOCKER_BUILDKIT" "$@"\nexit "$TEST_CHILD_STATUS"\n', { mode: 0o755 })
|
||||
return spawnSync(bash, ['-c',
|
||||
'testbin=$(cd "$1" && pwd); shift; PATH="$testbin:$PATH"; export PATH; exec sh "$@"',
|
||||
'fixture', directory.replaceAll('\\', '/'), wrapper.replaceAll('\\', '/'),
|
||||
'--build-arg', 'VALUE=with space', '-t', 'web:test', '.'], {
|
||||
encoding: 'utf8', timeout: 10000,
|
||||
env: { ...process.env, DOCKER_BUILDKIT: '1', TEST_AVAILABLE_MEMORY: availableMemory, TEST_CHILD_STATUS: childStatus }
|
||||
})
|
||||
} finally {
|
||||
assert.equal(dirname(resolve(directory)), resolve(tmpdir()))
|
||||
rmSync(directory, { recursive: true })
|
||||
}
|
||||
}
|
||||
|
||||
test('TEST and gray builds share the resource gate and deployment cannot rebuild implicitly', () => {
|
||||
const pipeline = readFileSync(join(root, 'Jenkinsfile'), 'utf8')
|
||||
const production = readFileSync(join(root, 'scripts/deploy-production.sh'), 'utf8')
|
||||
const dockerfile = readFileSync(join(root, 'Dockerfile'), 'utf8')
|
||||
for (const source of [pipeline, production]) {
|
||||
assert.match(source, /sh scripts\/build-image\.sh/)
|
||||
assert.doesNotMatch(source, /docker (?:compose[^\n]* build|build\s)/)
|
||||
}
|
||||
assert.match(pipeline, /timeout\(time: 20, unit: 'MINUTES'\)/)
|
||||
assert.match(pipeline, /up -d --no-build --pull never --wait/)
|
||||
assert.ok(pipeline.includes(`-t "${basename(root)}-test:$IMAGE_TAG"`))
|
||||
assert.match(dockerfile, /^ENV NODE_OPTIONS=--max-old-space-size=2048$/m)
|
||||
})
|
||||
|
||||
test('build forwards quoted arguments, enforces container budgets and propagates failure', { skip: !shellAvailable }, () => {
|
||||
const result = runBuild('4194304', '23')
|
||||
assert.equal(result.status, 23, result.stderr)
|
||||
assert.match(result.stdout, /watchdog\r?\n--signal=TERM\r?\n--kill-after=30s\r?\n15m\r?\ndocker\r?\nbuild/)
|
||||
assert.match(result.stdout, /builder=0\r?\nbuild/)
|
||||
for (const argument of ['--memory=3g', '--memory-swap=3g', '--cpu-period=100000', '--cpu-quota=200000', '--force-rm']) {
|
||||
assert.ok(result.stdout.split(/\r?\n/).includes(argument), argument)
|
||||
}
|
||||
assert.match(result.stdout, /--build-arg\r?\nVALUE=with space\r?\n-t\r?\nweb:test\r?\n\./)
|
||||
assert.equal(runBuild('8388608').status, 0)
|
||||
assert.equal(runBuild('8388608', '124').status, 124)
|
||||
})
|
||||
|
||||
test('low or unreadable host memory refuses to start Docker', { skip: !shellAvailable }, () => {
|
||||
for (const [memory, status] of [['4194303', 75], ['', 69], ['unknown', 69]]) {
|
||||
const result = runBuild(memory)
|
||||
assert.equal(result.status, status, result.stderr)
|
||||
assert.equal(result.stdout, '')
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user