diff --git a/package.json b/package.json index 2fe06d4..d59a751 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "devDependencies": { "eslint": "^8.57.0", "eslint-config-prettier": "^10.1.8", - "js-yaml": "^4.1.0", + "js-yaml": "^5.5.0", "prettier": "^3.9.10" } } diff --git a/scripts/generate-automation.js b/scripts/generate-automation.js index eb23290..8fd9074 100644 --- a/scripts/generate-automation.js +++ b/scripts/generate-automation.js @@ -29,7 +29,7 @@ const GENERATED_HEADER = # ${description} `; -const DUMP_OPTS = { indent: 2, lineWidth: -1, noRefs: true, quotingType: '"' }; +const DUMP_OPTS = { indent: 2, lineWidth: -1, noRefs: true, quoteStyle: 'double' }; const DEFAULT_PERMISSIONS = { contents: 'read' }; const PERMISSION_RANK = { none: 0, read: 1, write: 2 }; @@ -44,14 +44,21 @@ function unquoteOnKey(content, suffix) { return content.replace(/^"on":/m, suffix ? `on: ${suffix}` : 'on:'); } -function loadRegistry() { - const raw = yaml.load(fs.readFileSync(REGISTRY_PATH, 'utf8')); +// js-yaml 5's default schema no longer resolves `<<` merge keys. +const LOAD_OPTS = { schema: yaml.CORE_SCHEMA.withTags(yaml.mergeTag) }; + +function parseRegistry(source) { + const raw = yaml.load(source, LOAD_OPTS); return { automations: raw.automations.filter(a => a.enabled), retiredSecrets: raw.retired_secrets || [], }; } +function loadRegistry() { + return parseRegistry(fs.readFileSync(REGISTRY_PATH, 'utf8')); +} + function titleCase(name) { return name .split('-') @@ -326,4 +333,4 @@ function main() { if (require.main === module) main(); -module.exports = { buildReusableWorkflows }; +module.exports = { buildReusableWorkflows, parseRegistry }; diff --git a/scripts/generate-automation.test.js b/scripts/generate-automation.test.js index eee234d..0e4faaf 100644 --- a/scripts/generate-automation.test.js +++ b/scripts/generate-automation.test.js @@ -2,7 +2,7 @@ const test = require('node:test'); const assert = require('node:assert/strict'); const yaml = require('js-yaml'); -const { buildReusableWorkflows } = require('./generate-automation'); +const { buildReusableWorkflows, parseRegistry } = require('./generate-automation'); const WORKFLOWS = 'learningequality/.github/.github/workflows'; const GROUP_FILE = 'automation-group-pull-request-target.yml'; @@ -87,3 +87,20 @@ test('a group whose job id collides with an automation name is rejected', () => const clash = { ...sweep, name: 'pull-request-target' }; assert.throws(() => buildReusableWorkflows([prLabel, prOpened, clash]), /pull-request-target/); }); + +test('registry merge keys expand into the mapping that uses them', () => { + const { automations } = parseRegistry(` +x-bot: &bot + LE_BOT_APP_ID: required + LE_BOT_PRIVATE_KEY: required +automations: + - name: review-requested + enabled: true + secrets: + <<: *bot +`); + assert.deepEqual(automations[0].secrets, { + LE_BOT_APP_ID: 'required', + LE_BOT_PRIVATE_KEY: 'required', + }); +}); diff --git a/yarn.lock b/yarn.lock index 7e3b1ae..935cda4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -121,6 +121,11 @@ argparse@^2.0.1: resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +argparse@^3.0.3: + version "3.0.4" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-3.0.4.tgz#ba802dd75fdc3d5bdcb4339b713d62c6c5d4565d" + integrity sha512-fwWK3Zq22l4XJJjx5mcBHGikIk+hJqO+32dPm0Jur7+rtG5x1scOjSfdT9ZKeWHPpsya/l0HHK4tDImBkTrtvA== + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" @@ -652,6 +657,13 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" +js-yaml@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-5.5.0.tgz#ec843f09ffb2a54b6758a41eee109a1815d16442" + integrity sha512-paNr4LFIFPjeeSmwNYdrb8FafLLG/Cr2Af3GjbsHJ5CxIjH4r0c2emlmRdAtpPAieP1gRVzk6buBbVP5gBMGAg== + dependencies: + argparse "^3.0.3" + json-bigint@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz"