Files
vac-optimizer/scripts/ci-image.test.mjs
T
nessarandClaude Opus 5.5 ac69a41fc8 ci: run development, quality checks and CI on Bun 1.4.3
Pin Bun 1.4.3 with packageManager, and force every bun run script onto
the Bun runtime through bunfig so ng, ESLint and Prettier never fall
back to an installed Node. Type checking uses bun check for the
application and test projects, next to the Angular build.

CI jobs install the pinned release from a checksum-verified download
instead of npm and run project steps in a shell without Node on PATH,
which stays available only for the checkout and upload actions. The
image test's fake docker runs on Bun.

Refs #290, #293

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 14:25:46 +02:00

138 lines
4.9 KiB
JavaScript

import assert from 'node:assert/strict';
import { execFileSync, spawnSync } from 'node:child_process';
import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
const directory = mkdtempSync(join(tmpdir(), 'ci-image-'));
const script = fileURLToPath(new URL('./ci-image.mjs', import.meta.url));
const git = (...args) => execFileSync('git', args, { cwd: directory, encoding: 'utf8' }).trim();
const image = 'gitea.nessar.fr/nessar/vac-optimizer';
const commandsPath = join(directory, 'docker-commands.jsonl');
const bin = join(directory, 'bin');
const run = (variables = {}, expected = 0) => {
const commit = git('rev-parse', 'HEAD');
const eventName = variables.eventName ?? 'push';
const ref = variables.tag
? `refs/tags/${variables.tag}`
: eventName === 'pull_request'
? 'refs/pull/42/head'
: 'refs/heads/feature/calendar';
const event = { ref };
if (eventName === 'pull_request') {
event.pull_request = {
title: 'feat: calendar',
head: {
sha: commit,
ref: 'feature/calendar',
repo: { full_name: variables.headRepository ?? 'nessar/vac-optimizer' },
},
base: { ref: 'develop' },
};
}
writeFileSync(join(directory, 'event.json'), JSON.stringify(event));
writeFileSync(commandsPath, '');
const result = spawnSync(process.execPath, [script], {
cwd: directory,
encoding: 'utf8',
env: {
PATH: `${bin}:${process.env.PATH}`,
GITHUB_EVENT_NAME: eventName,
GITHUB_EVENT_PATH: join(directory, 'event.json'),
GITHUB_SHA: commit,
GITHUB_REF: ref,
GITHUB_REPOSITORY: 'nessar/vac-optimizer',
GITHUB_SERVER_URL: 'https://gitea.nessar.fr',
REGISTRY_USERNAME: 'publisher',
REGISTRY_PASSWORD: variables.registryPassword ?? 'test-token',
DOCKER_COMMANDS: commandsPath,
MISSING_IMAGE: variables.missingImage ? 'true' : '',
},
});
assert.equal(result.status, expected, result.stdout + result.stderr);
return readFileSync(commandsPath, 'utf8').trim().split('\n').filter(Boolean).map(JSON.parse);
};
try {
git('init', '--initial-branch=main');
git('config', 'user.name', 'CI image test');
git('config', 'user.email', 'ci-image@example.invalid');
git('remote', 'add', 'origin', directory);
writeFileSync(join(directory, 'package.json'), '{"version":"1.5.0"}\n');
git('add', 'package.json');
git('commit', '-m', 'chore: initial release');
git('branch', 'develop');
git('switch', '-c', 'feature/calendar');
git('commit', '--allow-empty', '-m', 'feat: calendar');
mkdirSync(bin);
const docker = join(bin, 'docker');
writeFileSync(
docker,
`#!/usr/bin/env bun
import { appendFileSync, readFileSync } from 'node:fs';
const args = process.argv.slice(2);
const input = args[0] === 'login' ? readFileSync(0, 'utf8') : '';
appendFileSync(process.env.DOCKER_COMMANDS, JSON.stringify({ args, input }) + '\\n');
if (args[0] === 'pull' && process.env.MISSING_IMAGE === 'true') process.exit(1);
`,
);
chmodSync(docker, 0o755);
const commit = git('rev-parse', 'HEAD');
const commands = run();
assert.deepEqual(commands, [
{
args: ['login', 'gitea.nessar.fr', '--username', 'publisher', '--password-stdin'],
input: 'test-token',
},
{
args: [
'build',
'-t',
`${image}:sha-${commit}`,
'-t',
`${image}:branch-feature-calendar`,
'.',
],
input: '',
},
{ args: ['push', `${image}:sha-${commit}`], input: '' },
{ args: ['push', `${image}:branch-feature-calendar`], input: '' },
]);
assert.deepEqual(run({ eventName: 'pull_request' }), commands);
assert.deepEqual(
run({ eventName: 'pull_request', headRepository: 'contributor/vac-optimizer' }, 1),
[],
);
assert.deepEqual(run({ eventName: 'workflow_dispatch' }, 1), []);
assert.deepEqual(run({ registryPassword: '' }, 1), []);
assert.deepEqual(run({ tag: 'v1.5.0' }, 1), []);
git('switch', 'main');
git('tag', '-a', 'v1.5.0', '-m', 'Release 1.5.0');
const releaseCommit = git('rev-parse', 'HEAD');
assert.deepEqual(run({ tag: 'v1.5.0' }), [
{
args: ['login', 'gitea.nessar.fr', '--username', 'publisher', '--password-stdin'],
input: 'test-token',
},
{ args: ['pull', `${image}:sha-${releaseCommit}`], input: '' },
{ args: ['tag', `${image}:sha-${releaseCommit}`, `${image}:v1.5.0`], input: '' },
{ args: ['push', `${image}:v1.5.0`], input: '' },
{ args: ['tag', `${image}:sha-${releaseCommit}`, `${image}:latest`], input: '' },
{ args: ['push', `${image}:latest`], input: '' },
]);
assert.deepEqual(run({ tag: 'v1.5.0', missingImage: true }, 1), [
{
args: ['login', 'gitea.nessar.fr', '--username', 'publisher', '--password-stdin'],
input: 'test-token',
},
{ args: ['pull', `${image}:sha-${releaseCommit}`], input: '' },
]);
assert.deepEqual(run({ tag: 'v1.5.0-rc.1' }, 1), []);
assert.deepEqual(run({ tag: 'v1.6.0' }, 1), []);
console.log('CI image publication checks passed.');
} finally {
rmSync(directory, { recursive: true, force: true });
}