feat: add production blue-green frontend deployment

This commit is contained in:
2026-09-20 21:44:56 +08:00
parent 97d0f82256
commit 5887837407
4 changed files with 118 additions and 31 deletions
@@ -0,0 +1,24 @@
import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
import test from 'node:test'
const script = (await readFile(new URL('../../scripts/deploy-production.sh', import.meta.url), 'utf8'))
.replaceAll('\r\n', '\n')
const compose = (await readFile(new URL('../../docker-compose.production.yml', import.meta.url), 'utf8'))
.replaceAll('\r\n', '\n')
test('client production deployment switches healthy blue-green slots atomically', () => {
const start = script.indexOf('compose run -d --no-deps --name "$candidate"')
const health = script.indexOf('docker exec "$candidate" wget')
const nginx = script.indexOf('nginx -t || ! nginx -s reload')
const stop = script.indexOf('docker stop --time 30 "$active_container"')
assert.ok(start >= 0 && start < health && health < nginx && nginx < stop)
assert.match(script, /target_port=\$\(\(base_port \+ 10000\)\)/)
assert.match(script, /cp -f "\$backup" "\$nginx_config"/)
})
test('client uses stable active gateway aliases on the routing network', () => {
assert.match(compose, /API_UPSTREAM: "http:\/\/gateway-active:9000"/)
assert.match(compose, /OVERSEAS_API_UPSTREAM: "http:\/\/gateway-overseas-active:9300"/)
assert.match(compose, /name: firstleg-production-routing/)
})
+3 -1
View File
@@ -49,7 +49,9 @@ test('keeps local environments on target nodes and uses credential-managed SSH f
assert.match(jenkinsfile, /credentialsId: 'firstleg-production-ssh'/)
assert.match(jenkinsfile, /git rev-parse origin\/main/)
assert.match(productionCompose, /127\.0\.0\.1:\$\{HOST_PORT:-9002\}:80/)
assert.match(productionCompose, /API_UPSTREAM: "http:\/\/gateway:9000"/)
assert.match(productionCompose, /API_UPSTREAM: "http:\/\/gateway-active:9000"/)
assert.match(productionCompose, /OVERSEAS_API_UPSTREAM: "http:\/\/gateway-overseas-active:9300"/)
assert.match(productionCompose, /name: firstleg-production-routing/)
assert.match(productionCompose, /name: firstleg-prod-infra-network/)
})