conductor

CI task system
git clone git://git.finwo.net/app/conductor
Log | Files | Refs | README | LICENSE

self-pipeline.test.js (3385B)


      1 // test/self-pipeline.test.js - the pipeline this project jobs on itself
      2 //
      3 // .conductor.yml is not covered by examples.test.js, and it is the one
      4 // pipeline whose breakage stops the project from building at all. It also
      5 // encodes decisions that are easy to undo by accident: which tasks may run
      6 // on any worker, and which need a docker socket.
      7 
      8 import test from 'node:test';
      9 import assert from 'node:assert/strict';
     10 import fs from 'node:fs/promises';
     11 import path from 'node:path';
     12 import { fileURLToPath } from 'node:url';
     13 
     14 import { compilePipeline } from '../src/lib/pipeline/index.js';
     15 
     16 const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
     17 
     18 async function selfPipeline(arches = ['x86_64'], ref = 'refs/heads/main') {
     19   const text = await fs.readFile(path.join(ROOT, '.conductor.yml'), 'utf8');
     20   return compilePipeline(text, { source: '.conductor.yml', arches, ref });
     21 }
     22 
     23 test('the pipeline this project jobs on itself compiles', async () => {
     24   const { tasks } = await selfPipeline();
     25   assert.ok(tasks.length > 0, '.conductor.yml produced no tasks');
     26 });
     27 
     28 test('its tasks declare the features they actually need', async () => {
     29   const { tasks } = await selfPipeline();
     30   const byName = new Map(tasks.map((task) => [task.name, task]));
     31 
     32   for (const name of ['style', 'test', 'integration', 'images', 'smoke', 'publish']) {
     33     assert.ok(byName.has(name), `expected a ${name} task`);
     34   }
     35 
     36   // The fast task has to actually skip the container-backed tests, or it
     37   // fails on any worker without a docker socket.
     38   assert.equal(byName.get('test').env.CONDUCTOR_TEST_NO_DOCKER, '1');
     39 
     40   // Anything starting containers has to say so, otherwise it gets handed
     41   // to a worker that cannot job it.
     42   for (const name of ['integration', 'images', 'smoke', 'publish']) {
     43     assert.ok(
     44       byName.get(name).requires.includes('docker'),
     45       `${name} starts containers and must require the docker feature`,
     46     );
     47   }
     48 
     49   // Conversely, the tasks meant to run anywhere must not demand features.
     50   for (const name of ['style', 'test']) {
     51     assert.deepEqual(byName.get(name).requires, [], `${name} should run on any worker`);
     52   }
     53 });
     54 
     55 test('publishing is restricted to main and release tags', async () => {
     56   // The cost of getting this wrong is a branch build overwriting :latest
     57   // on Docker Hub, which is not something a test should leave to trust.
     58   const onMain = await selfPipeline();
     59   assert.ok(onMain.tasks.some((j) => j.name === 'publish'));
     60 
     61   for (const ref of ['refs/heads/feature', 'refs/heads/main-2', 'refs/pull/7/head', '']) {
     62     const { tasks } = compilePipeline(
     63       await fs.readFile(path.join(ROOT, '.conductor.yml'), 'utf8'),
     64       { source: '.conductor.yml', arches: ['x86_64'], ref },
     65     );
     66     assert.ok(
     67       !tasks.some((j) => j.name === 'publish'),
     68       `publish must not run for ${JSON.stringify(ref)}`,
     69     );
     70     // The rest of the pipeline still has to run on a branch.
     71     assert.ok(tasks.some((j) => j.name === 'test'), `test should still run for ${JSON.stringify(ref)}`);
     72   }
     73 
     74   for (const ref of ['refs/heads/main', 'refs/tags/v1.2.0']) {
     75     const { tasks } = compilePipeline(
     76       await fs.readFile(path.join(ROOT, '.conductor.yml'), 'utf8'),
     77       { source: '.conductor.yml', arches: ['x86_64'], ref },
     78     );
     79     assert.ok(tasks.some((j) => j.name === 'publish'), `publish should run for ${ref}`);
     80   }
     81 });