feat: add backend container delivery
Backend security / security (push) Failing after 5s
Backend verify / verify (push) Successful in 2m19s
Backend security / security (pull_request) Failing after 5s
Backend verify / verify (pull_request) Successful in 2m17s
Conventional commits / commits (pull_request) Successful in 25s
Backend security / security (push) Failing after 5s
Backend verify / verify (push) Successful in 2m19s
Backend security / security (pull_request) Failing after 5s
Backend verify / verify (pull_request) Successful in 2m17s
Conventional commits / commits (pull_request) Successful in 25s
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
// Real image boundary: catches missing native modules, proto assets and migrations.
|
||||
const assert = require('node:assert/strict');
|
||||
const { execFileSync } = require('node:child_process');
|
||||
const { randomUUID } = require('node:crypto');
|
||||
|
||||
const image = process.argv[2];
|
||||
assert.ok(image, 'Pass a built backend image reference');
|
||||
const name = `wealthledger-smoke-${randomUUID()}`;
|
||||
const docker = (...args) => execFileSync('docker', args, { encoding: 'utf8' }).trim();
|
||||
const environment = [
|
||||
'-e', 'DATABASE_URL=file:/data/wealthledger.db',
|
||||
'-e', 'FRONTEND_ORIGIN=http://localhost',
|
||||
'-e', 'JWT_SECRET=disposable-image-smoke-secret-at-least-32-characters',
|
||||
'-e', 'TOKEN_ENCRYPTION_KEY=MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY=',
|
||||
'-e', 'LOG_LEVEL=error',
|
||||
];
|
||||
|
||||
async function ready() {
|
||||
for (let attempt = 0; attempt < 90; attempt++) {
|
||||
const state = JSON.parse(docker('inspect', name, '--format', '{{json .State}}'));
|
||||
assert.equal(state.Running, true, 'Image exited before readiness');
|
||||
if (state.Health.Status === 'healthy') return;
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
}
|
||||
throw new Error('Image readiness timed out');
|
||||
}
|
||||
|
||||
(async () => {
|
||||
docker('volume', 'create', name);
|
||||
try {
|
||||
assert.throws(() => docker('run', '--rm', '--read-only', image),
|
||||
'Missing runtime configuration must prevent startup');
|
||||
const migrate = () => docker('run', '--rm', '--read-only', '-v', `${name}:/data`,
|
||||
...environment, image, 'node', 'node_modules/prisma/build/index.js', 'migrate', 'deploy');
|
||||
migrate();
|
||||
docker('run', '-d', '--name', name, '--read-only', '-v', `${name}:/data`, ...environment, image);
|
||||
await ready();
|
||||
assert.deepEqual(JSON.parse(docker('inspect', name, '--format', '{{json .HostConfig.PortBindings}}')), {});
|
||||
docker('exec', name, 'node', '-e', `
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
assert.notEqual(process.getuid(), 0);
|
||||
assert.equal(fs.existsSync('src'), false);
|
||||
assert.equal(fs.existsSync('.env'), false);
|
||||
assert.equal(fs.existsSync('node_modules/typescript'), false);
|
||||
assert.equal(fs.existsSync('node_modules/@nestjs/cli'), false);
|
||||
fs.accessSync('dist/src/integrations/tbank/proto/contracts/users.proto');
|
||||
fetch('http://localhost:3000/api/v1/auth/register', {
|
||||
method: 'POST', headers: {'content-type':'application/json'},
|
||||
body: JSON.stringify({email:'image-smoke@example.test',password:'fictional-image-smoke-password'})
|
||||
}).then(async r => {assert.equal(r.status, 200); await r.json()}).catch(e => {console.error(e); process.exitCode=1});
|
||||
`);
|
||||
docker('stop', name);
|
||||
migrate();
|
||||
docker('start', name);
|
||||
await ready();
|
||||
docker('exec', name, 'node', '-e', `
|
||||
const assert = require('node:assert/strict');
|
||||
const db = require('better-sqlite3')('/data/wealthledger.db');
|
||||
assert.equal(db.prepare('SELECT count(*) AS n FROM User').get().n, 1);
|
||||
assert.equal(db.prepare('SELECT count(*) AS n FROM Session').get().n, 1);
|
||||
assert.equal(db.pragma('integrity_check', {simple:true}), 'ok');
|
||||
db.close();
|
||||
`);
|
||||
console.log('PASS image: fresh/existing migration, non-root read-only runtime, auth, persistence, proto assets');
|
||||
} finally {
|
||||
try { docker('rm', '-f', name); } finally { docker('volume', 'rm', name); }
|
||||
}
|
||||
})().catch((error) => { console.error(error); process.exitCode = 1; });
|
||||
Reference in New Issue
Block a user