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 node 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 }); }