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>
138 lines
4.9 KiB
JavaScript
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 });
|
|
}
|