tangler 0.4.0

Extracts code blocks from Markdown documents
Documentation
const execSync = require("child_process").execSync;
const fs = require("fs");

const jobs = {
    variables: {
        FF_TIMESTAMPS: true,
        CACHE_COMPRESSION_FORMAT: 'tarzstd',
        RUST_BACKTRACE: 'full',
        CARGO_INCREMENTAL: 0,
    },
    default: {
        interruptible: true,
    },
    workflow: {
        rules: [
          { if: '$CI_PIPELINE_SOURCE == "merge_request_event"' },
          { if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' },
          { if: '$CI_COMMIT_TAG =~ /^v\\d/' }
        ]
    }
};

const getMetadata = (attributes, key) => attributes
    .filter(attribute => 'metadata' in attribute)
    .flatMap(attribute => {
        let meta = attribute.metadata;
        let metaKey = meta[0];
        if (metaKey === key) {
            return meta.slice(1);
        } else {
            return [];
        }
    });

const getRecipePackages = (recipes, name) => {
    const recipe = recipes[name];
    const direct = getMetadata(recipe.attributes, 'pacman');
    const indirectDeps = recipe.body
        .filter(item => String(item[0]).startsWith('just '))
        .flatMap(item => item[0].split(' '))
        .filter(item => item !== 'just' && !item.startsWith('--'));
    direct.push(...indirectDeps.flatMap(dep => getRecipePackages(recipes, dep)));
    return direct;
}

for (const file of fs
    .readdirSync(".")
    .filter((file) => file === ".justfile" || file.endsWith(".just"))) {
    const justfile = JSON.parse(
        execSync(`just --justfile ${file} --dump --dump-format=json`, {
            encoding: "utf-8",
        }),
    );

    Object.values(justfile.recipes)
        .filter((recipe) =>
            recipe.attributes.some((attribute) => attribute.group == "ci"),
        )
        .forEach((recipe) => {
            let pacman = getRecipePackages(justfile.recipes, recipe.name);
            const packages = ['just'];
            if (pacman) {
                packages.push(...pacman);
            }

            const job = {
                image: 'archlinux',
                stage: 'test',
                script: [
                    'echo -e "\\e[0Ksection_start:`date +%s`:deps[collapsed=true]\\r\\e[0KInstalling job dependencies"',
                    'pacman-key --init',
                    'pacman -Sy --needed --noconfirm archlinux-keyring',
                    `pacman -Syu --needed --noconfirm ${packages.join(' ')}`,
                    'echo -e "\\e[0Ksection_end:`date +%s`:deps\\r\\e[0K"',
                    `just --justfile ${file} ${recipe.name}`
                ],
            };

            const gitlabci = getMetadata(recipe.attributes, "gitlabci-job")[0];
            if (gitlabci) {
                Object.assign(job, JSON.parse(gitlabci));
            }

            jobs[recipe.doc] = job;
        });
}

console.log('# Automatically generated by `scripts/ci/created-gitlab-checks.js` due to the following issue:');
console.log('# CHECK https://gitlab.com/groups/gitlab-org/-/epics/8205');
console.log(JSON.stringify(jobs, null, 2));