Merge branch 'main' into dependabot/github_actions/dot-github/workflows/actions/setup-java-6.0.0

This commit is contained in:
Michael B. Gale
2026-09-02 14:36:24 +01:00
committed by GitHub
19 changed files with 1905 additions and 361 deletions

View File

@@ -0,0 +1,16 @@
---
applyTo: "CHANGELOG.md,src/defaults.json,lib/defaults.json,src/api-compatibility.json"
---
# Merging release, mergeback, and backport PRs
The release process creates a cascade of PRs (`main` → `releases/vN`, then
`releases/vN` → `main` mergeback, then `releases/vN` → `releases/v(N-1)`
backport). These PRs reliably touch `CHANGELOG.md`, `src/defaults.json` /
`lib/defaults.json` (bundle/CLI version bump), and `src/api-compatibility.json`.
Such PRs **must be merged with a merge commit**. Never squash or rebase, as
that breaks the branch linkage the release automation relies on.
When arming auto-merge on these PRs, use `--merge` (e.g. `gh pr merge --merge`),
not `--squash` or `--rebase`.

View File

@@ -72,6 +72,33 @@ jobs:
sarif_file: eslint.sarif
category: eslint
changetool-tests:
name: changetool unit tests
permissions:
contents: read
runs-on: ubuntu-slim
timeout-minutes: 10
concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
group: pr-checks-changetool-tests-${{ github.ref }}-${{ github.event_name }}
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run changetool unit tests
run: npm --workspace changetool test
# These checks do not need to be run as part of the same matrix that we use for the `unit-tests`
# job.
other-checks:

View File

@@ -60,10 +60,13 @@ Here are a few things you can do that will increase the likelihood of your pull
This workflow goes through the pull requests that have been merged to `main` since the last release, creates a changelog, then opens a pull request to merge the changes since the last release into the `releases/v3` release branch.
You can start a release by triggering this workflow via [workflow dispatch](https://github.com/github/codeql-action/actions/workflows/update-release-branch.yml).
1. The workflow run will open a pull request titled "Merge main into releases/v3". Follow the steps on the checklist in the pull request. Once you've checked off all but the last two of these, approve the PR and automerge it.
1. The workflow run will open a pull request titled "Merge main into releases/v3". Follow the steps on the checklist in the pull request. Once you've checked off all but the last two of these, approve the PR and automerge it **with a merge commit** (`gh pr merge --merge`).
1. When the "Merge main into releases/v3" pull request is merged into the `releases/v3` branch, a mergeback pull request to `main` will be automatically created. This mergeback pull request incorporates the changelog updates into `main`, tags the release using the merge commit of the "Merge main into releases/v3" pull request, and bumps the patch version of the CodeQL Action.
1. If a backport to an older major version is required, a pull request targeting that version's branch will also be automatically created.
1. Approve the mergeback and backport pull request (if applicable) and automerge them.
1. Approve the mergeback and backport pull request (if applicable) and automerge them **with a merge commit** (`gh pr merge --merge`).
> [!NOTE]
> The release, mergeback, and backport pull requests must always be merged with a merge commit — **never squash or rebase**. The mergeback tags the release using the merge commit of the "Merge main into releases/v3" pull request, so squashing or rebasing breaks tagging and the branch linkage the release automation relies on.
Once the mergeback and backport pull request have been merged, the release is complete.

View File

@@ -209,4 +209,18 @@ export default [
],
},
},
{
files: ["scripts/changetool/**/*.ts"],
languageOptions: {
parserOptions: {
project: "./scripts/changetool/tsconfig.json",
},
},
rules: {
"no-console": "off",
"import/extensions": "off",
},
},
];

462
lib/entry-points.js generated
View File

@@ -25972,7 +25972,7 @@ var init_dist_src3 = __esm({
}
});
// node_modules/@octokit/plugin-paginate-rest/dist-bundle/index.js
// node_modules/@actions/github/node_modules/@octokit/plugin-paginate-rest/dist-bundle/index.js
var dist_bundle_exports = {};
__export(dist_bundle_exports, {
composePaginateRest: () => composePaginateRest,
@@ -26098,7 +26098,7 @@ function paginateRest(octokit) {
}
var VERSION6, composePaginateRest, paginatingEndpoints;
var init_dist_bundle5 = __esm({
"node_modules/@octokit/plugin-paginate-rest/dist-bundle/index.js"() {
"node_modules/@actions/github/node_modules/@octokit/plugin-paginate-rest/dist-bundle/index.js"() {
VERSION6 = "0.0.0-development";
composePaginateRest = Object.assign(paginate, {
iterator
@@ -142283,7 +142283,6 @@ async function core(rootItemPath, options = {}, returnType = {}) {
// node_modules/js-yaml/dist/js-yaml.mjs
var NOT_RESOLVED = /* @__PURE__ */ Symbol("NOT_RESOLVED");
var MERGE_KEY = /* @__PURE__ */ Symbol("MERGE_KEY");
function defineScalarTag(tagName, options) {
return {
tagName,
@@ -142292,9 +142291,9 @@ function defineScalarTag(tagName, options) {
matchByTagPrefix: options.matchByTagPrefix ?? false,
implicitFirstChars: options.implicitFirstChars ?? null,
resolve: options.resolve,
identify: options.identify ?? null,
identify: options.identify,
represent: options.represent ?? ((data) => String(data)),
representTagName: options.representTagName ?? null
representTagName: options.representTagName ?? (() => tagName)
};
}
function defineSequenceTag(tagName, options) {
@@ -142308,9 +142307,9 @@ function defineSequenceTag(tagName, options) {
addItem: options.addItem,
finalize: options.finalize ?? ((carrier) => carrier),
carrierIsResult,
identify: options.identify ?? null,
identify: options.identify,
represent: options.represent ?? ((data) => data),
representTagName: options.representTagName ?? null
representTagName: options.representTagName ?? (() => tagName)
};
}
function defineMappingTag(tagName, options) {
@@ -142327,9 +142326,9 @@ function defineMappingTag(tagName, options) {
get: options.get,
finalize: options.finalize ?? ((carrier) => carrier),
carrierIsResult,
identify: options.identify ?? null,
identify: options.identify,
represent: options.represent ?? ((data) => data),
representTagName: options.representTagName ?? null
representTagName: options.representTagName ?? (() => tagName)
};
}
var strTag = defineScalarTag("tag:yaml.org,2002:str", {
@@ -142678,9 +142677,10 @@ var mergeTag = defineScalarTag("tag:yaml.org,2002:merge", {
implicit: true,
implicitFirstChars: ["<"],
resolve: (source, isExplicit) => {
if (source === "<<" || isExplicit && source === "") return MERGE_KEY;
if (source === "<<" || isExplicit && source === "") return "<<";
return NOT_RESOLVED;
}
},
identify: () => false
});
var BASE64_PATTERN = /^[A-Za-z0-9+/]*={0,2}$/;
function resolveYamlBinary(source) {
@@ -142785,7 +142785,8 @@ var omapTag = defineSequenceTag("tag:yaml.org,2002:omap", {
carrier.list.push(item);
return "";
},
finalize: (carrier) => carrier.list
finalize: (carrier) => carrier.list,
identify: () => false
});
var pairsTag = defineSequenceTag("tag:yaml.org,2002:pairs", {
create: () => [],
@@ -142801,7 +142802,8 @@ var pairsTag = defineSequenceTag("tag:yaml.org,2002:pairs", {
if (keys.length !== 1) return "cannot resolve a pairs item";
container.push([keys[0], object2[keys[0]]]);
return "";
}
},
identify: () => false
});
var mapTag = defineMappingTag("tag:yaml.org,2002:map", {
create: () => ({}),
@@ -142882,11 +142884,35 @@ function compileTags(tags) {
}
var Schema = class Schema2 {
tags;
/** @internal */
implicitScalarTags;
/**
* Dispatch implicit scalar resolvers by `source.charAt(0)`. Each bucket holds
* the resolvers that may match that key, in schema order; a key absent from
* the map uses
* {@link Schema.implicitScalarAnyFirstChar}
* (resolvers that declared no first-char constraint, so they apply to any
* first character).
*/
implicitScalarByFirstChar;
implicitScalarAnyFirstChar;
/**
* The default scalar tag (`!!str`), resolved once so the composer's fallback
* for unresolved plain scalars avoids a keyed lookup per scalar.
*
* @internal
*/
defaultScalarTag;
/**
* The default container tags (`!!seq` / `!!map`), used by the dumper: when a
* value is identified by its default tag, the tag is implicit and not
* printed. Undefined if the schema does not define them (then such values
* can't be dumped).
*
* @internal
*/
defaultSequenceTag;
/** @internal */
defaultMappingTag;
exact;
prefix;
@@ -142932,6 +142958,52 @@ var Schema = class Schema2 {
this.exact = exact;
this.prefix = prefix;
}
/** @internal */
lookupScalarTag(tagName) {
const exactTag = this.exact.scalar[tagName];
if (exactTag) return exactTag;
for (const tag of this.prefix.scalar) if (tagName.startsWith(tag.tagName)) return tag;
}
/** @internal */
lookupSequenceTag(tagName) {
const exactTag = this.exact.sequence[tagName];
if (exactTag) return exactTag;
for (const tag of this.prefix.sequence) if (tagName.startsWith(tag.tagName)) return tag;
}
/** @internal */
lookupMappingTag(tagName) {
const exactTag = this.exact.mapping[tagName];
if (exactTag) return exactTag;
for (const tag of this.prefix.mapping) if (tagName.startsWith(tag.tagName)) return tag;
}
/** @internal */
resolveImplicitScalarTag(source) {
const candidates = this.implicitScalarByFirstChar.get(source.charAt(0)) ?? this.implicitScalarAnyFirstChar;
for (const tag2 of candidates) {
const value = tag2.resolve(source, false, tag2.tagName);
if (value !== NOT_RESOLVED) return {
value,
tag: tag2
};
}
const tag = this.defaultScalarTag;
return {
value: tag.resolve(source, false, tag.tagName),
tag
};
}
/**
* Creates a new schema with the specified tags added. If a tag already
* exists, it is replaced by the specified tag.
*
* @example
*
* ```javascript
* import { CORE_SCHEMA, mergeTag, realMapTag } from 'js-yaml'
*
* const schema = CORE_SCHEMA.withTags(mergeTag, realMapTag)
* ```
*/
withTags(...tags) {
let flatTags = [];
for (const tag of tags) flatTags = flatTags.concat(tag);
@@ -142970,6 +143042,19 @@ var YAML11_SCHEMA = new Schema([
pairsTag,
setTag
]);
var DUMP_SCHEMA = YAML11_SCHEMA.withTags({
...intYaml11Tag,
resolve: (source, isExplicit, tagName) => {
const result = intYaml11Tag.resolve(source, isExplicit, tagName);
return result === NOT_RESOLVED ? intCoreTag.resolve(source, isExplicit, tagName) : result;
}
}, {
...floatYaml11Tag,
resolve: (source, isExplicit, tagName) => {
const result = floatYaml11Tag.resolve(source, isExplicit, tagName);
return result === NOT_RESOLVED ? floatCoreTag.resolve(source, isExplicit, tagName) : result;
}
});
var realMapTag = defineMappingTag("tag:yaml.org,2002:map", {
create: () => /* @__PURE__ */ new Map(),
addPair: (container, key, value) => {
@@ -143106,9 +143191,13 @@ function formatError(exception, compact) {
${exception.mark.snippet}`;
return `${exception.reason} ${where}`;
}
var YAMLException = class extends Error {
var YAMLException = class YAMLException2 extends Error {
reason;
mark;
/**
* Optional `mark` contains source snippet data. Usually, use
* {@link YAMLException.throwAt} instead of passing it directly.
*/
constructor(reason, mark) {
super();
this.name = "YAMLException";
@@ -143117,34 +143206,65 @@ var YAMLException = class extends Error {
this.message = formatError(this, false);
if (Error.captureStackTrace) Error.captureStackTrace(this, this.constructor);
}
/**
* Returns the formatted error, omitting the source snippet in compact mode.
*/
toString(compact) {
return `${this.name}: ${formatError(this, compact)}`;
}
};
function throwErrorAt(source, position, message, filename = "") {
let line = 0;
let lineStart = 0;
for (let index2 = 0; index2 < position; index2++) {
const ch = source.charCodeAt(index2);
if (ch === 10) {
line++;
lineStart = index2 + 1;
} else if (ch === 13) {
line++;
if (source.charCodeAt(index2 + 1) === 10) index2++;
lineStart = index2 + 1;
/**
* Builds a YAMLException with a source snippet and throws it. `source` is
* the raw input text; `position` is an offset into it.
*/
static throwAt(source, position, message, filename = "") {
let line = 0;
let lineStart = 0;
for (let index2 = 0; index2 < position; index2++) {
const ch = source.charCodeAt(index2);
if (ch === 10) {
line++;
lineStart = index2 + 1;
} else if (ch === 13) {
line++;
if (source.charCodeAt(index2 + 1) === 10) index2++;
lineStart = index2 + 1;
}
}
const mark = {
name: filename,
buffer: source,
position,
line,
column: position - lineStart
};
mark.snippet = makeSnippet(mark);
throw new YAMLException2(message, mark);
}
const mark = {
name: filename,
buffer: source,
position,
line,
column: position - lineStart
};
mark.snippet = makeSnippet(mark);
throw new YAMLException(message, mark);
}
};
var EVENT_ID = {
DOCUMENT: 1,
SEQUENCE: 2,
MAPPING: 3,
SCALAR: 4,
ALIAS: 5,
POP: 6
};
var SCALAR_STYLE = {
PLAIN: 1,
SINGLE_QUOTED: 2,
DOUBLE_QUOTED: 3,
LITERAL_BLOCK: 4,
FOLDED_BLOCK: 5
};
var COLLECTION_STYLE = {
BLOCK: 1,
FLOW: 2
};
var CHOMPING_MODE = {
CLIP: 1,
STRIP: 2,
KEEP: 3
};
var NO_RANGE$3 = -1;
function simpleEscapeSequence(c) {
switch (c) {
@@ -143342,8 +143462,8 @@ function getBlockValue(input, start, end, indent, chomping, folded) {
didReadContent = true;
emptyLines = 0;
}
if (chomping === 3) result += "\n".repeat(didReadContent ? 1 + emptyLines : emptyLines);
else if (chomping !== 2) {
if (chomping === CHOMPING_MODE.KEEP) result += "\n".repeat(didReadContent ? 1 + emptyLines : emptyLines);
else if (chomping !== CHOMPING_MODE.STRIP) {
if (didReadContent) result += "\n";
}
return result;
@@ -143353,13 +143473,13 @@ function getScalarValue(input, scalar) {
const { valueStart, valueEnd } = scalar;
if (scalar.fast) return input.slice(valueStart, valueEnd);
switch (scalar.style) {
case 2:
case SCALAR_STYLE.SINGLE_QUOTED:
return getSingleQuotedValue(input, valueStart, valueEnd);
case 3:
case SCALAR_STYLE.DOUBLE_QUOTED:
return getDoubleQuotedValue(input, valueStart, valueEnd);
case 4:
case SCALAR_STYLE.LITERAL_BLOCK:
return getBlockValue(input, valueStart, valueEnd, scalar.indent, scalar.chomping, false);
case 5:
case SCALAR_STYLE.FOLDED_BLOCK:
return getBlockValue(input, valueStart, valueEnd, scalar.indent, scalar.chomping, true);
default:
return getPlainValue(input, valueStart, valueEnd);
@@ -143389,6 +143509,7 @@ function tagNameShort(fullTag) {
return `!<${tagPercentEncode(tag)}>`;
}
var NO_RANGE$2 = -1;
var MERGE_TAG_NAME = "tag:yaml.org,2002:merge";
var DEFAULT_CONSTRUCTOR_OPTIONS = {
filename: "",
schema: CORE_SCHEMA,
@@ -143404,26 +143525,16 @@ function eventPosition$1(event) {
return 0;
}
function throwError$1(state, message) {
throwErrorAt(state.source, state.position, message, state.filename);
YAMLException.throwAt(state.source, state.position, message, state.filename);
}
function finalizeCollection(state, position, tag, carrier) {
try {
return tag.finalize(carrier);
} catch (error3) {
if (error3 instanceof YAMLException) throw error3;
throwErrorAt(state.source, position, error3 instanceof Error ? error3.message : String(error3), state.filename);
YAMLException.throwAt(state.source, position, error3 instanceof Error ? error3.message : String(error3), state.filename);
}
}
function lookupTag(exact, prefix, tagName) {
const exactTag = exact[tagName];
if (exactTag) return exactTag;
for (const tag of prefix) if (tagName.startsWith(tag.tagName)) return tag;
}
function findExplicitTag(state, exact, prefix, tagName, nodeKind) {
const tag = lookupTag(exact, prefix, tagName);
if (tag) return tag;
throwError$1(state, `unknown ${nodeKind} tag !<${tagName}>`);
}
function constructScalar(state, event) {
const source = getScalarValue(state.source, event);
const rawTag = event.tagStart === NO_RANGE$2 ? "" : state.source.slice(event.tagStart, event.tagEnd);
@@ -143434,7 +143545,7 @@ function constructScalar(state, event) {
tag: strTag2
};
const tagName = tagNameFull(rawTag, state.tagHandlers);
const scalarTag = lookupTag(state.schema.exact.scalar, state.schema.prefix.scalar, tagName);
const scalarTag = state.schema.lookupScalarTag(tagName);
if (scalarTag) {
const result = scalarTag.resolve(source, true, tagName);
if (result === NOT_RESOLVED) throwError$1(state, `cannot resolve a node with !<${tagName}> explicit tag`);
@@ -143443,7 +143554,7 @@ function constructScalar(state, event) {
tag: scalarTag
};
}
const collectionTagDef = lookupTag(state.schema.exact.mapping, state.schema.prefix.mapping, tagName) ?? lookupTag(state.schema.exact.sequence, state.schema.prefix.sequence, tagName);
const collectionTagDef = state.schema.lookupMappingTag(tagName) ?? state.schema.lookupSequenceTag(tagName);
if (collectionTagDef) {
if (source !== "") throwError$1(state, `cannot resolve a node with !<${tagName}> explicit tag`);
const carrier = collectionTagDef.create(tagName);
@@ -143454,28 +143565,15 @@ function constructScalar(state, event) {
}
throwError$1(state, `unknown scalar tag !<${tagName}>`);
}
if (event.style === 1) {
const candidates = state.schema.implicitScalarByFirstChar.get(source.charAt(0)) ?? state.schema.implicitScalarAnyFirstChar;
for (const tag of candidates) {
const result = tag.resolve(source, false, tag.tagName);
if (result !== NOT_RESOLVED) return {
value: result,
tag
};
}
}
if (event.style === SCALAR_STYLE.PLAIN) return state.schema.resolveImplicitScalarTag(source);
return {
value: strTag2.resolve(source, false, strTag2.tagName),
tag: strTag2
};
}
function collectionTag(state, event, exact, prefix, defaultTagName, nodeKind) {
function collectionTagName(state, event, defaultTagName) {
const rawTag = event.tagStart === NO_RANGE$2 ? "" : state.source.slice(event.tagStart, event.tagEnd);
const tagName = rawTag === "" || rawTag === "!" ? defaultTagName : tagNameFull(rawTag, state.tagHandlers);
return {
tagName,
tag: findExplicitTag(state, exact, prefix, tagName, nodeKind)
};
return rawTag === "" || rawTag === "!" ? defaultTagName : tagNameFull(rawTag, state.tagHandlers);
}
function isMappingTag(tag) {
return tag.nodeKind === "mapping";
@@ -143492,12 +143590,16 @@ function mergeKeys(state, frame, source, sourceTag) {
function mergeSource(state, frame, source, sourceTag) {
state.position = frame.keyPosition;
if (isMappingTag(sourceTag)) mergeKeys(state, frame, source, sourceTag);
else if (sourceTag.nodeKind === "sequence" && Array.isArray(source)) for (const element of source) mergeKeys(state, frame, element, frame.tag);
else if (sourceTag.nodeKind === "sequence" && Array.isArray(source)) for (const element of source) {
const elementTag = state.nodeTags.get(element);
if (!elementTag) throwError$1(state, "cannot merge mappings; the provided source object is unacceptable");
mergeKeys(state, frame, element, elementTag);
}
else throwError$1(state, "cannot merge mappings; the provided source object is unacceptable");
}
function addMappingValue(state, frame, key, value, tag) {
state.position = frame.keyPosition;
if (key === MERGE_KEY) {
if (frame.keyIsMerge) {
mergeSource(state, frame, value, tag);
return;
}
@@ -143512,9 +143614,7 @@ function addValue(state, value, tag) {
frame.value = value;
frame.hasValue = true;
} else if (frame.kind === "sequence") {
if (frame.merge) {
if (!isMappingTag(tag)) throwError$1(state, "cannot merge mappings; the provided source object is unacceptable");
}
if (isMappingTag(tag)) state.nodeTags.set(value, tag);
const err = frame.tag.addItem(frame.value, value, frame.index++);
if (err) throwError$1(state, err);
} else if (frame.hasKey) {
@@ -143526,6 +143626,7 @@ function addValue(state, value, tag) {
frame.key = value;
frame.keyPosition = state.position;
frame.hasKey = true;
frame.keyIsMerge = tag.tagName === MERGE_TAG_NAME;
}
}
function storeAnchor(state, event, value, tag, isValueFinal) {
@@ -143550,6 +143651,7 @@ function constructFromEvents(events, options) {
position: 0,
frames: [],
anchors: /* @__PURE__ */ new Map(),
nodeTags: /* @__PURE__ */ new Map(),
tagHandlers: /* @__PURE__ */ Object.create(null),
totalMergeKeys: 0,
aliasCount: 0
@@ -143558,8 +143660,9 @@ function constructFromEvents(events, options) {
const event = state.events[state.eventIndex++];
state.position = eventPosition$1(event);
switch (event.type) {
case 1:
case EVENT_ID.DOCUMENT:
state.anchors = /* @__PURE__ */ new Map();
state.nodeTags = /* @__PURE__ */ new Map();
state.aliasCount = 0;
state.tagHandlers = /* @__PURE__ */ Object.create(null);
for (const directive of event.directives) if (directive.kind === "tag") state.tagHandlers[directive.handle] = directive.prefix;
@@ -143570,47 +143673,49 @@ function constructFromEvents(events, options) {
hasValue: false
});
break;
case 4: {
case EVENT_ID.SCALAR: {
const { value, tag } = constructScalar(state, event);
storeAnchor(state, event, value, tag, true);
addValue(state, value, tag);
break;
}
case 2: {
const definition = collectionTag(state, event, state.schema.exact.sequence, state.schema.prefix.sequence, "tag:yaml.org,2002:seq", "sequence");
const value = definition.tag.create(definition.tagName);
const anchor = storeAnchor(state, event, value, definition.tag, definition.tag.carrierIsResult);
const parent = state.frames[state.frames.length - 1];
const merge2 = parent !== void 0 && parent.kind === "mapping" && parent.hasKey && parent.key === MERGE_KEY;
case EVENT_ID.SEQUENCE: {
const tagName = collectionTagName(state, event, "tag:yaml.org,2002:seq");
const tag = state.schema.lookupSequenceTag(tagName);
if (!tag) throwError$1(state, `unknown sequence tag !<${tagName}>`);
const value = tag.create(tagName);
const anchor = storeAnchor(state, event, value, tag, tag.carrierIsResult);
state.frames.push({
kind: "sequence",
position: state.position,
value,
tag: definition.tag,
tag,
anchor,
index: 0,
merge: merge2
index: 0
});
break;
}
case 3: {
const definition = collectionTag(state, event, state.schema.exact.mapping, state.schema.prefix.mapping, "tag:yaml.org,2002:map", "mapping");
const value = definition.tag.create(definition.tagName);
const anchor = storeAnchor(state, event, value, definition.tag, definition.tag.carrierIsResult);
case EVENT_ID.MAPPING: {
const tagName = collectionTagName(state, event, "tag:yaml.org,2002:map");
const tag = state.schema.lookupMappingTag(tagName);
if (!tag) throwError$1(state, `unknown mapping tag !<${tagName}>`);
const value = tag.create(tagName);
const anchor = storeAnchor(state, event, value, tag, tag.carrierIsResult);
state.frames.push({
kind: "mapping",
position: state.position,
value,
tag: definition.tag,
tag,
anchor,
key: void 0,
keyPosition: state.position,
hasKey: false,
keyIsMerge: false,
overridable: null
});
break;
}
case 5: {
case EVENT_ID.ALIAS: {
if (state.maxAliases !== -1 && ++state.aliasCount > state.maxAliases) throwError$1(state, `aliases exceeded maxAliases (${state.maxAliases})`);
const name = state.source.slice(event.anchorStart, event.anchorEnd);
const anchor = state.anchors.get(name);
@@ -143619,7 +143724,7 @@ function constructFromEvents(events, options) {
addValue(state, anchor.value, anchor.tag);
break;
}
case 6: {
case EVENT_ID.POP: {
const frame = state.frames.pop();
if (frame.kind === "mapping" && frame.hasKey) {
state.position = frame.keyPosition;
@@ -143660,7 +143765,7 @@ var DEFAULT_PARSER_OPTIONS = {
};
function addDocumentEvent(state, explicitStart, explicitEnd) {
state.events.push({
type: 1,
type: EVENT_ID.DOCUMENT,
explicitStart,
explicitEnd,
directives: state.directives
@@ -143668,7 +143773,7 @@ function addDocumentEvent(state, explicitStart, explicitEnd) {
}
function addSequenceEvent(state, start, anchorStart, anchorEnd, tagStart, tagEnd, style) {
state.events.push({
type: 2,
type: EVENT_ID.SEQUENCE,
start,
anchorStart,
anchorEnd,
@@ -143679,7 +143784,7 @@ function addSequenceEvent(state, start, anchorStart, anchorEnd, tagStart, tagEnd
}
function addMappingEvent(state, start, anchorStart, anchorEnd, tagStart, tagEnd, style) {
state.events.push({
type: 3,
type: EVENT_ID.MAPPING,
start,
anchorStart,
anchorEnd,
@@ -143690,18 +143795,18 @@ function addMappingEvent(state, start, anchorStart, anchorEnd, tagStart, tagEnd,
}
function insertFlowPairMappingEvent(state, snapshot) {
state.events.splice(snapshot.eventsLength, 0, {
type: 3,
type: EVENT_ID.MAPPING,
start: snapshot.position,
anchorStart: NO_RANGE$1,
anchorEnd: NO_RANGE$1,
tagStart: NO_RANGE$1,
tagEnd: NO_RANGE$1,
style: 2
style: COLLECTION_STYLE.FLOW
});
}
function addScalarEvent(state, valueStart, valueEnd, anchorStart, anchorEnd, tagStart, tagEnd, style, chomping = 1, indent = -1, fast = false) {
function addScalarEvent(state, valueStart, valueEnd, anchorStart, anchorEnd, tagStart, tagEnd, style, chomping = CHOMPING_MODE.CLIP, indent = -1, fast = false) {
state.events.push({
type: 4,
type: EVENT_ID.SCALAR,
valueStart,
valueEnd,
anchorStart,
@@ -143716,16 +143821,16 @@ function addScalarEvent(state, valueStart, valueEnd, anchorStart, anchorEnd, tag
}
function addAliasEvent(state, anchorStart, anchorEnd) {
state.events.push({
type: 5,
type: EVENT_ID.ALIAS,
anchorStart,
anchorEnd
});
}
function addPopEvent(state) {
state.events.push({ type: 6 });
state.events.push({ type: EVENT_ID.POP });
}
function addEmptyScalarEvent(state) {
addScalarEvent(state, NO_RANGE$1, NO_RANGE$1, NO_RANGE$1, NO_RANGE$1, NO_RANGE$1, NO_RANGE$1, 1);
addScalarEvent(state, NO_RANGE$1, NO_RANGE$1, NO_RANGE$1, NO_RANGE$1, NO_RANGE$1, NO_RANGE$1, SCALAR_STYLE.PLAIN);
}
function emptyProperties() {
return {
@@ -143754,7 +143859,7 @@ function restoreState(state, snapshot) {
state.events.length = snapshot.eventsLength;
}
function throwError(state, message) {
throwErrorAt(state.input.slice(0, state.length), state.position, message, state.filename);
YAMLException.throwAt(state.input.slice(0, state.length), state.position, message, state.filename);
}
function isEol(c) {
return c === 10 || c === 13;
@@ -143922,7 +144027,7 @@ function readSingleQuotedScalar(state, nodeIndent, props) {
}
const end = state.position;
state.position++;
addScalarEvent(state, start, end, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, 2, 1, -1, simple);
addScalarEvent(state, start, end, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, SCALAR_STYLE.SINGLE_QUOTED, CHOMPING_MODE.CLIP, -1, simple);
return true;
}
if (isEol(ch)) {
@@ -143944,7 +144049,7 @@ function readDoubleQuotedScalar(state, nodeIndent, props) {
if (ch === 34) {
const end = state.position;
state.position++;
addScalarEvent(state, start, end, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, 3, 1, -1, simple);
addScalarEvent(state, start, end, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, SCALAR_STYLE.DOUBLE_QUOTED, CHOMPING_MODE.CLIP, -1, simple);
return true;
}
if (ch === 92) {
@@ -143972,18 +144077,18 @@ function readDoubleQuotedScalar(state, nodeIndent, props) {
}
function readBlockScalar(state, parentIndent, props) {
const ch = state.input.charCodeAt(state.position);
let chomping = 1;
let chomping = CHOMPING_MODE.CLIP;
let indent = -1;
let detectedIndent = false;
if (ch !== 124 && ch !== 62) return false;
const style = ch === 124 ? 4 : 5;
const style = ch === 124 ? SCALAR_STYLE.LITERAL_BLOCK : SCALAR_STYLE.FOLDED_BLOCK;
state.position++;
while (state.input.charCodeAt(state.position) !== 0) {
const current = state.input.charCodeAt(state.position);
const digit = fromDecimalCode(current);
if (current === 43 || current === 45) {
if (chomping !== 1) throwError(state, "repeat of a chomping mode identifier");
chomping = current === 43 ? 3 : 2;
if (chomping !== CHOMPING_MODE.CLIP) throwError(state, "repeat of a chomping mode identifier");
chomping = current === 43 ? CHOMPING_MODE.KEEP : CHOMPING_MODE.STRIP;
state.position++;
} else if (digit >= 0) {
if (digit === 0) throwError(state, "bad explicit indentation width of a block scalar; it cannot be less than one");
@@ -144098,7 +144203,7 @@ function readPlainScalar(state, nodeIndent, nodeContext, props) {
}
if (end === start) return false;
checkPrintable(state, start, end);
addScalarEvent(state, start, end, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, 1, 1, -1, !multiline);
addScalarEvent(state, start, end, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, SCALAR_STYLE.PLAIN, CHOMPING_MODE.CLIP, -1, !multiline);
return true;
}
function skipFlowSeparationSpace(state, nodeIndent) {
@@ -144113,8 +144218,8 @@ function readFlowCollection(state, nodeIndent, props) {
let readNext = true;
if (ch !== 91 && ch !== 123) return false;
const terminator = isMapping ? 125 : 93;
if (isMapping) addMappingEvent(state, start, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, 2);
else addSequenceEvent(state, start, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, 2);
if (isMapping) addMappingEvent(state, start, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, COLLECTION_STYLE.FLOW);
else addSequenceEvent(state, start, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, COLLECTION_STYLE.FLOW);
state.position++;
while (state.input.charCodeAt(state.position) !== 0) {
skipFlowSeparationSpace(state, nodeIndent);
@@ -144168,7 +144273,7 @@ function readFlowCollection(state, nodeIndent, props) {
}
function readBlockSequence(state, nodeIndent, props) {
if (state.firstTabInLine !== -1 || state.input.charCodeAt(state.position) !== 45 || !isWsOrEolOrEnd(state.input.charCodeAt(state.position + 1))) return false;
addSequenceEvent(state, state.position, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, 1);
addSequenceEvent(state, state.position, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, COLLECTION_STYLE.BLOCK);
while (state.input.charCodeAt(state.position) === 45 && isWsOrEolOrEnd(state.input.charCodeAt(state.position + 1))) {
if (state.firstTabInLine !== -1) {
state.position = state.firstTabInLine;
@@ -144204,7 +144309,7 @@ function readBlockMapping(state, nodeIndent, flowIndent, props) {
const entryLine = state.line;
if ((ch === 63 || ch === 58) && isWsOrEolOrEnd(following)) {
if (!mappingOpened) {
addMappingEvent(state, state.position, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, 1);
addMappingEvent(state, state.position, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, COLLECTION_STYLE.BLOCK);
mappingOpened = true;
}
if (ch === 63) {
@@ -144234,7 +144339,7 @@ function readBlockMapping(state, nodeIndent, flowIndent, props) {
if (!isWsOrEolOrEnd(ch)) throwError(state, "a whitespace character is expected after the key-value separator within a block mapping");
if (!mappingOpened) {
restoreState(state, beforeKey);
addMappingEvent(state, beforeKey.position, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, 1);
addMappingEvent(state, beforeKey.position, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, COLLECTION_STYLE.BLOCK);
mappingOpened = true;
parseNode(state, flowIndent, CONTEXT_FLOW_OUT, false, true);
ch = state.input.charCodeAt(state.position);
@@ -144302,7 +144407,7 @@ function parseNode(state, parentIndent, nodeContext, allowToSeek, allowCompact,
if (atNewLine && allowBlockStyles && (props.tagStart !== NO_RANGE$1 || props.anchorStart !== NO_RANGE$1) && (ch === 33 || ch === 38)) {
const fallbackState = snapshotState(state);
const flowIndent = parentIndent + 1;
if (readBlockMapping(state, state.position - state.lineStart, flowIndent, props) && state.events[fallbackState.eventsLength]?.type === 3) {
if (readBlockMapping(state, state.position - state.lineStart, flowIndent, props) && state.events[fallbackState.eventsLength]?.type === EVENT_ID.MAPPING) {
state.depth--;
return true;
}
@@ -144330,7 +144435,7 @@ function parseNode(state, parentIndent, nodeContext, allowToSeek, allowCompact,
const fallbackState = snapshotState(state);
const propertyIndent = propertyStart.position - propertyStart.lineStart;
restoreState(state, propertyStart);
if (readBlockMapping(state, propertyIndent, flowIndent, emptyProperties()) && state.events[fallbackState.eventsLength]?.type === 3) hasContent = true;
if (readBlockMapping(state, propertyIndent, flowIndent, emptyProperties()) && state.events[fallbackState.eventsLength]?.type === EVENT_ID.MAPPING) hasContent = true;
else restoreState(state, fallbackState);
}
if (!hasContent && (allowBlockScalars && readBlockScalar(state, flowIndent, props) || readSingleQuotedScalar(state, flowIndent, props) || readDoubleQuotedScalar(state, flowIndent, props) || readAlias(state, props) || readPlainScalar(state, flowIndent, nodeContext, props))) hasContent = true;
@@ -144339,7 +144444,7 @@ function parseNode(state, parentIndent, nodeContext, allowToSeek, allowCompact,
}
allowBlockScalars = allowBlockScalars && !hasContent;
if (!hasContent && (props.anchorStart !== NO_RANGE$1 || props.tagStart !== NO_RANGE$1 || allowBlockScalars)) {
addScalarEvent(state, NO_RANGE$1, NO_RANGE$1, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, 1);
addScalarEvent(state, NO_RANGE$1, NO_RANGE$1, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, SCALAR_STYLE.PLAIN);
hasContent = true;
}
state.depth--;
@@ -144424,7 +144529,7 @@ function readDocument(state) {
}
}
const documentEvent = state.events[documentEventIndex];
if (documentEvent?.type === 1) documentEvent.explicitEnd = explicitEnd;
if (documentEvent?.type === EVENT_ID.DOCUMENT) documentEvent.explicitEnd = explicitEnd;
addPopEvent(state);
if (!explicitEnd && state.position < state.length && !(state.position === state.lineStart && testDocumentSeparator(state))) throwError(state, "end of the stream or a document separator is expected");
}
@@ -144446,7 +144551,7 @@ function parseEvents(input, options) {
events: []
};
const nullpos = input.indexOf("\0");
if (nullpos !== -1) throwErrorAt(input, nullpos, "null byte is not allowed in input", state.filename);
if (nullpos !== -1) YAMLException.throwAt(input, nullpos, "null byte is not allowed in input", state.filename);
if (state.input.charCodeAt(state.position) === 65279) state.position++;
while (state.position < state.length) {
skipSeparationSpace(state, true);
@@ -144482,6 +144587,7 @@ function load(input, options) {
throw new YAMLException("expected a single document in the stream, but found more");
}
var Style = class {
/** Whether to print the node's tag explicitly. */
tagged = false;
flow = false;
singleQuoted = false;
@@ -144517,9 +144623,9 @@ function buildRepresentTypes(schema) {
function matchTag(state, object2) {
for (let index2 = 0, length = state.representTypes.length; index2 < length; index2 += 1) {
const { tag, implicitTag } = state.representTypes[index2];
if (tag.identify && tag.identify(object2)) {
if (tag.identify(object2)) {
let tagName;
if (tag.matchByTagPrefix && tag.representTagName) tagName = tag.representTagName(object2);
if (tag.matchByTagPrefix) tagName = tag.representTagName(object2);
else tagName = tag.tagName;
return {
tag,
@@ -144719,8 +144825,7 @@ function createPresenterState(options) {
};
return {
...opts,
defaultScalarTagName: opts.schema.defaultScalarTag.tagName,
implicitResolvers: opts.schema.implicitScalarTags
defaultScalarTagName: opts.schema.defaultScalarTag.tagName
};
}
function encodeNonPrintable(character) {
@@ -144761,13 +144866,6 @@ function scalarLayout(state, level) {
lineWidth: state.lineWidth === -1 ? -1 : Math.max(Math.min(state.lineWidth, 40), state.lineWidth - indent)
};
}
function resolveImplicitTag(state, str) {
for (let index2 = 0, length = state.implicitResolvers.length; index2 < length; index2 += 1) {
const tagDefinition = state.implicitResolvers[index2];
if (tagDefinition.resolve(str, false, tagDefinition.tagName) !== NOT_RESOLVED) return tagDefinition.tagName;
}
return state.defaultScalarTagName;
}
function isWhitespace(c) {
return c === CHAR_SPACE || c === CHAR_TAB;
}
@@ -144884,11 +144982,11 @@ function resolveScalarStyle(state, node, layout, iskey, inblock) {
}
const string2 = node.value;
if (string2.length === 0) {
if (node.style.tagged || resolveImplicitTag(state, string2) === node.tag) return STYLE_PLAIN;
if (node.style.tagged || state.schema.resolveImplicitScalarTag(string2).tag.tagName === node.tag) return STYLE_PLAIN;
return state.quoteStyle === "double" ? STYLE_DOUBLE : STYLE_SINGLE;
}
const style = chooseScalarStyle(state, string2, layout, singleLineOnly, state.forceQuotes && !iskey, inblock);
if (style === STYLE_PLAIN && !node.style.tagged && resolveImplicitTag(state, string2) !== node.tag) return state.quoteStyle === "double" ? STYLE_DOUBLE : STYLE_SINGLE;
if (style === STYLE_PLAIN && !node.style.tagged && state.schema.resolveImplicitScalarTag(string2).tag.tagName !== node.tag) return state.quoteStyle === "double" ? STYLE_DOUBLE : STYLE_SINGLE;
return style;
}
function blockHeader(string2, indentPerLevel) {
@@ -145172,22 +145270,9 @@ function present(documents, options) {
}
return result;
}
var DEFAULT_DUMP_SCHEMA = YAML11_SCHEMA.withTags({
...intYaml11Tag,
resolve: (source, isExplicit, tagName) => {
const result = intYaml11Tag.resolve(source, isExplicit, tagName);
return result === NOT_RESOLVED ? intCoreTag.resolve(source, isExplicit, tagName) : result;
}
}, {
...floatYaml11Tag,
resolve: (source, isExplicit, tagName) => {
const result = floatYaml11Tag.resolve(source, isExplicit, tagName);
return result === NOT_RESOLVED ? floatCoreTag.resolve(source, isExplicit, tagName) : result;
}
});
var DEFAULT_DUMP_OPTIONS = {
...DEFAULT_PRESENTER_OPTIONS,
schema: DEFAULT_DUMP_SCHEMA,
schema: DUMP_SCHEMA,
skipInvalid: false,
noRefs: false,
flowLevel: -1,
@@ -145214,6 +145299,22 @@ function dump(input, options = {}) {
schema: opts.schema
});
}
var EVENT_DOCUMENT = EVENT_ID.DOCUMENT;
var EVENT_SEQUENCE = EVENT_ID.SEQUENCE;
var EVENT_MAPPING = EVENT_ID.MAPPING;
var EVENT_SCALAR = EVENT_ID.SCALAR;
var EVENT_ALIAS = EVENT_ID.ALIAS;
var EVENT_POP = EVENT_ID.POP;
var SCALAR_STYLE_PLAIN = SCALAR_STYLE.PLAIN;
var SCALAR_STYLE_SINGLE_QUOTED = SCALAR_STYLE.SINGLE_QUOTED;
var SCALAR_STYLE_DOUBLE_QUOTED = SCALAR_STYLE.DOUBLE_QUOTED;
var SCALAR_STYLE_LITERAL_BLOCK = SCALAR_STYLE.LITERAL_BLOCK;
var SCALAR_STYLE_FOLDED_BLOCK = SCALAR_STYLE.FOLDED_BLOCK;
var COLLECTION_STYLE_BLOCK = COLLECTION_STYLE.BLOCK;
var COLLECTION_STYLE_FLOW = COLLECTION_STYLE.FLOW;
var CHOMPING_CLIP = CHOMPING_MODE.CLIP;
var CHOMPING_STRIP = CHOMPING_MODE.STRIP;
var CHOMPING_KEEP = CHOMPING_MODE.KEEP;
// src/util.ts
var semver = __toESM(require_semver2());
@@ -146727,12 +146828,33 @@ function wrapApiConfigurationError(e) {
// src/cli/output-cache.ts
var fs3 = __toESM(require("fs"));
var import_path = __toESM(require("path"));
// src/cli/types.ts
var versionInfoBaseSchema = {
version: string,
features: optional(object({})),
/**
* The overlay version helps deal with backward incompatible changes for
* overlay analysis. When a precompiled query pack reports the same overlay
* version as the CodeQL CLI, we can use the CodeQL CLI to perform overlay
* analysis with that pack. Otherwise, if the overlay versions are different,
* or if either the pack or the CLI does not report an overlay version,
* we need to revert to non-overlay analysis.
*/
overlayVersion: optional(number)
};
// src/cli/output-cache.ts
var outputCacheSchema = {
cmd: string,
entries: object({})
};
var COMMAND_CACHE_FILENAME = "codeql-action-command-cache.json";
var cachedCodeQlVersion = void 0;
function getCommandCacheFilePath(env) {
return import_path.default.join(getTemporaryDirectory(env), COMMAND_CACHE_FILENAME);
}
function cacheCodeQlVersion(env, cmd, version) {
function cacheCodeQlVersion(cacheFilePath, cmd, version) {
if (cachedCodeQlVersion !== void 0) {
throw new Error("cacheCodeQlVersion() should be called only once");
}
@@ -146741,23 +146863,17 @@ function cacheCodeQlVersion(env, cmd, version) {
cmd,
entries: { version }
};
fs3.writeFileSync(
getCommandCacheFilePath(env),
JSON.stringify(outputCache),
"utf8"
);
fs3.writeFileSync(cacheFilePath, JSON.stringify(outputCache), "utf8");
}
function getCachedCodeQlVersion(logger, env, cmd) {
function getCachedCodeQlVersion(logger, cacheFilePath, cmd) {
if (cachedCodeQlVersion !== void 0) {
return cachedCodeQlVersion;
}
let serialized;
try {
serialized = fs3.readFileSync(getCommandCacheFilePath(env), "utf8");
serialized = fs3.readFileSync(cacheFilePath, "utf8");
} catch (e) {
logger.debug(
`Cannot read CLI-cache file ${getCommandCacheFilePath(env)}: ${e}`
);
logger.debug(`Cannot read CLI-cache file ${cacheFilePath}: ${e}`);
return void 0;
}
let persisted;
@@ -146774,12 +146890,10 @@ function getCachedCodeQlVersion(logger, env, cmd) {
return cachedCodeQlVersion;
}
function isVersionInfo(x) {
const candidate = x;
return typeof candidate === "object" && candidate !== null && typeof candidate.version === "string" && (candidate.features === void 0 || typeof candidate.features === "object" && candidate.features !== null) && (candidate.overlayVersion === void 0 || typeof candidate.overlayVersion === "number");
return isObject(x) && validateSchema(versionInfoBaseSchema, x);
}
function isOutputCache(x) {
const candidate = x;
return typeof candidate === "object" && candidate !== null && typeof candidate.cmd === "string" && candidate.entries !== void 0 && isVersionInfo(candidate.entries.version);
return isObject(x) && validateSchema(outputCacheSchema, x) && isObject(x.entries) && isVersionInfo(x.entries.version);
}
// src/config/pack-registries.ts
@@ -147323,7 +147437,10 @@ async function createStatusReportBase(actionName, status, actionStartedAt, confi
core7.exportVariable("CODEQL_WORKFLOW_STARTED_AT" /* WORKFLOW_STARTED_AT */, workflowStartedAt);
}
const runnerOs = getRequiredEnvParam("RUNNER_OS");
const codeQlCliVersion = getCachedCodeQlVersion(logger, getEnv());
const codeQlCliVersion = getCachedCodeQlVersion(
logger,
getCommandCacheFilePath(getEnv())
);
const actionRef = process.env["GITHUB_ACTION_REF"] || "";
const testingEnvironment = getTestingEnvironment();
if (testingEnvironment) {
@@ -152411,7 +152528,12 @@ async function getCodeQLForCmd(logger, cmd, checkVersion) {
return cmd;
},
async getVersion() {
let result = getCachedCodeQlVersion(logger, getEnv(), cmd);
const cacheFilePath = getCommandCacheFilePath(getEnv());
let result = getCachedCodeQlVersion(
logger,
cacheFilePath,
cmd
);
if (result === void 0) {
result = await runCliJson(
cmd,
@@ -152420,7 +152542,7 @@ async function getCodeQLForCmd(logger, cmd, checkVersion) {
noStreamStdout: true
}
);
cacheCodeQlVersion(getEnv(), cmd, result);
cacheCodeQlVersion(cacheFilePath, cmd, result);
}
return result;
},
@@ -163948,7 +164070,7 @@ tmp/lib/tmp.js:
*)
js-yaml/dist/js-yaml.mjs:
(*! js-yaml 5.2.3 https://github.com/nodeca/js-yaml @license MIT *)
(*! js-yaml 5.3.0 https://github.com/nodeca/js-yaml @license MIT *)
long/index.js:
(**

1170
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -17,7 +17,8 @@
},
"license": "MIT",
"workspaces": [
"pr-checks"
"pr-checks",
"scripts/changetool"
],
"dependencies": {
"@actions/artifact": "^5.0.3",
@@ -31,7 +32,7 @@
"@actions/io": "^2.0.0",
"@actions/tool-cache": "^3.0.1",
"@octokit/core": "^7.0.7",
"@octokit/plugin-paginate-rest": "^14.0.0",
"@octokit/plugin-paginate-rest": "^15.0.0",
"@octokit/plugin-rest-endpoint-methods": "^17.0.0",
"@octokit/plugin-retry": "^8.1.1",
"archiver": "^8.0.0",
@@ -39,19 +40,19 @@
"follow-redirects": "^1.16.0",
"get-folder-size": "^5.0.0",
"https-proxy-agent": "^7.0.6",
"js-yaml": "^5.2.3",
"js-yaml": "^5.3.0",
"jsonschema": "1.5.0",
"long": "^5.3.2",
"node-forge": "^1.4.0",
"semver": "^7.8.5",
"uuid": "^14.0.1",
"uuid": "^14.0.2",
"undici": "^6.28.0"
},
"devDependencies": {
"@ava/typescript": "6.0.0",
"@eslint/compat": "^2.1.0",
"@microsoft/eslint-formatter-sarif": "^3.1.0",
"@octokit/types": "^16.0.0",
"@octokit/types": "^17.0.0",
"@types/archiver": "^8.0.0",
"@types/follow-redirects": "^1.14.4",
"@types/js-yaml": "^4.0.9",

View File

@@ -5,7 +5,7 @@
"@actions/core": "^2.0.3",
"@actions/github": "^8.0.1",
"@octokit/core": "^7.0.7",
"@octokit/plugin-paginate-rest": ">=9.2.2",
"@octokit/plugin-paginate-rest": ">=15.0.0",
"@octokit/plugin-rest-endpoint-methods": "^17.0.0",
"semver": "^7.8.5",
"yaml": "^2.9.0"

View File

@@ -0,0 +1,200 @@
import assert from "node:assert/strict";
import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
import { describe, it } from "node:test";
import {
isValidChangenoteContent,
isValidChangenoteFile,
isValidChangenoteFilename,
hasValidChangenoteCategory,
VALID_CHANGE_NOTE_CATEGORIES,
} from "./validate.ts";
async function withTmpFile<T>(
baseFileName: string,
contents: string,
body: (filePath: string) => Promise<T>,
): Promise<T> {
const tmpDir = fs.mkdtempSync(
path.join(os.tmpdir(), "changetool-validate-test-"),
);
try {
const filePath = path.join(tmpDir, baseFileName);
fs.writeFileSync(filePath, contents);
return await body(filePath);
} finally {
fs.rmSync(tmpDir, { recursive: true, force: true });
}
}
await describe("isValidChangenoteContent", async () => {
await it("recognizes an unordered Markdown list", async () => {
const inputs = [
"- One changenote entry",
"- First item\n- Second item",
"\n\n\n\n- Fixed a bug\n- Added a feature",
];
for (const input of inputs) {
assert.equal(isValidChangenoteContent(input), true);
}
});
await it("does not recognize non-Markdown text", async () => {
const inputs = [
"This is not a list.",
'["this", "is", "JSON"]',
"---",
"***",
"___",
"paragraph",
];
for (const input of inputs) {
assert.equal(isValidChangenoteContent(input), false);
}
});
await it("does not recognize ordered Markdown lists", async () => {
const inputs = [
"1. First item\n2. Second item",
"\n\n\n1. First item\n1. Second item",
];
for (const input of inputs) {
assert.equal(isValidChangenoteContent(input), false);
}
});
await it("requires all list items to use a hyphen bullet", async () => {
const inputs = [
"* Fixed a bug\n* Added feature",
"+ Fixed a bug\n+ Added feature",
"- Fixed a bug\n* Added feature",
"- Fixed a bug\n+ Added feature",
"- Fixed a bug\n * Added feature\n + Updated docs",
"\n\n\n* Fixed a bug",
"\n\n\n+ Fixed a bug",
"---\n* Fixed a bug\n* Added feature",
] as const;
for (const input of inputs) {
assert.equal(isValidChangenoteContent(input), false);
}
});
await it("does not contain other Markdown elements", async () => {
const inputs = [
"- Fixed a bug\n\nParagraph of text",
"- Fixed a bug\n\n* Added a feature",
"# Header\n- Fixed a bug",
"- Fixed a bug\n## Subheader",
];
for (const input of inputs) {
assert.equal(isValidChangenoteContent(input), false);
}
});
});
await describe("isValidChangenoteFilename", async () => {
await it("accepts valid filenames", async () => {
const inputs = [
"2023-01-01-fix-bug.md",
"2023-12-31-add-feature.md",
"2023-06-15-update-docs.md",
];
for (const input of inputs) {
assert.equal(isValidChangenoteFilename(input), true);
}
});
await it("rejects invalid filenames", async () => {
const inputs = [
"missing-date-from-filename.md",
"2021-01-01.md",
"2026-12-19-wrong-file-name-extension.txt",
];
for (const input of inputs) {
assert.equal(isValidChangenoteFilename(input), false);
}
});
});
await describe("hasValidChangenoteCategory", async () => {
await it("accepts valid categories", async () => {
for (const category of Object.keys(VALID_CHANGE_NOTE_CATEGORIES)) {
const frontmatter = { category };
assert.equal(hasValidChangenoteCategory(frontmatter), true);
}
});
await it("rejects invalid categories", async () => {
const inputs = [
"",
"invalid-category",
"bug-fix",
"new-feature",
"security-patch",
"miscellaneous",
"documentation",
];
for (const category of inputs) {
const frontmatter = { category };
assert.equal(hasValidChangenoteCategory(frontmatter), false);
}
});
await it("reject missing category", async () => {
assert.equal(hasValidChangenoteCategory({}), false);
assert.equal(hasValidChangenoteCategory({ category: null }), false);
assert.equal(hasValidChangenoteCategory({ category: undefined }), false);
});
});
await describe("isValidChangenoteFile", async () => {
await it("accepts a valid change-note file", async () => {
await withTmpFile(
"2026-01-01-fix-bug.md",
"---\ncategory: fix\n---\n- Fixed a bug\n",
async (filePath) => {
assert.equal(isValidChangenoteFile(filePath), true);
},
);
});
await it("rejects invalid filename", async () => {
await withTmpFile(
"fix-bug.md",
"---\ncategory: fix\n---\n- Fixed a bug\n",
async (filePath) => {
assert.equal(isValidChangenoteFile(filePath), false);
},
);
});
await it("rejects missing frontmatter", async () => {
await withTmpFile(
"2026-01-01-fix-bug.md",
"- Fixed a bug\n",
async (filePath) => {
assert.equal(isValidChangenoteFile(filePath), false);
},
);
});
await it("rejects invalid Markdown", async () => {
await withTmpFile(
"2026-01-01-fix-bug.md",
"---\ncategory: fix\n---\n* Fixed a bug\n",
async (filePath) => {
assert.equal(isValidChangenoteFile(filePath), false);
},
);
});
});

View File

@@ -0,0 +1,121 @@
import * as fs from "node:fs";
import * as path from "node:path";
import { matter } from "lite-matter";
import type { List, ListItem } from "mdast";
import { fromMarkdown } from "mdast-util-from-markdown";
// Regex for filename: YYYY-MM-DD-id.md
const VALID_CHANGE_NOTE_FILENAME_PATTERN =
/^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])-([a-z0-9]+(?:-[a-z0-9]+)*)\.md$/;
export const VALID_CHANGE_NOTE_CATEGORIES = {
breaking: "Breaking Changes",
feature: "New Features",
improvement: "Improvements",
securityFix: "Security Fixes",
fix: "Bug Fixes",
unship: "Removed Features",
deprecation: "Deprecations",
knownIssue: "Known Issues",
misc: "Miscellaneous",
};
/**
* Validates that the given Markdown string meets the criteria for a change-note, which is:
* - A single unordered list
* - Each list item must start with a hyphen (-)
* - No other Markdown elements are allowed
* @param content The Markdown string to validate
* @returns True if the string is a valid change-note, false otherwise
*/
export function isValidChangenoteContent(content: string): boolean {
const ast = fromMarkdown(content);
const lines = content.split("\n");
function listHasHyphenBullets(node: List | ListItem): boolean {
if (node.type === "list") {
return node.children.every(listHasHyphenBullets);
}
const line = lines[node.position!.start.line - 1].trim();
return (
line.startsWith("-") &&
node.children.every(
(child) => child.type !== "list" || listHasHyphenBullets(child),
)
);
}
return (
ast.children.length === 1 &&
ast.children[0].type === "list" &&
ast.children[0].ordered === false &&
listHasHyphenBullets(ast.children[0])
);
}
/**
* Validates that the given filename meets the criteria for a change-note filename.
* @param filename The name of the change-note file to validate.
* @returns True if the filename is valid, false otherwise.
*/
export function isValidChangenoteFilename(filename: string): boolean {
return filename.match(VALID_CHANGE_NOTE_FILENAME_PATTERN) !== null;
}
/**
* Validates that the given frontmatter has a valid change-note category.
* @param frontmatter The frontmatter object to validate.
* @returns True if the frontmatter has a valid category, false otherwise.
*/
export function hasValidChangenoteCategory(
frontmatter: Record<string, unknown>,
): boolean {
const category = frontmatter["category"];
return (
typeof category === "string" &&
Object.hasOwn(VALID_CHANGE_NOTE_CATEGORIES, category)
);
}
/**
* Validates that the given change-note file meets all of the criteria for a change-note.
* @param filename The name of the change-note file to validate.
* @returns True if the file is a valid change-note, false otherwise.
*/
export function isValidChangenoteFile(filename: string): boolean {
let isValid: boolean = true;
let fileData: string | undefined;
try {
fileData = fs.readFileSync(filename, "utf8");
} catch (error) {
console.error(`${filename}: failed to read file`, error);
return false;
}
const { data: frontmatter, content } = matter(fileData);
if (!isValidChangenoteFilename(path.basename(filename))) {
isValid = false;
console.error(
`${filename}: invalid filename; must match pattern YYYY-MM-DD-id.md`,
);
}
if (!hasValidChangenoteCategory(frontmatter)) {
isValid = false;
const categories = Object.keys(VALID_CHANGE_NOTE_CATEGORIES).join(", ");
console.error(
`${filename}: invalid category; must be one of: ${categories}`,
);
}
if (!isValidChangenoteContent(content)) {
isValid = false;
console.error(
`${filename}: invalid Markdown; content must be a single unordered list with hyphen bullets and no other Markdown elements`,
);
}
return isValid;
}

View File

@@ -0,0 +1,51 @@
import { pathToFileURL } from "node:url";
import { parseArgs } from "node:util";
import { isValidChangenoteFile } from "./cli/validate.ts";
const entryPoint = process.argv[1];
if (entryPoint && import.meta.url === pathToFileURL(entryPoint).href) {
try {
process.exit(main());
} catch (error) {
console.error(error);
process.exit(1);
}
}
function main(): number {
const { positionals } = parseArgs({
allowPositionals: true,
strict: true,
});
const [command, ...paths] = positionals;
switch (command) {
case undefined:
case "help":
return usage();
case "validate":
return validate(paths);
default:
console.error(`Unknown command: ${command}`);
return 1;
}
}
function usage(): number {
console.log("Usage: changetool validate <path> [<path> ...]");
return 0;
}
function validate(paths: string[]): number {
let valid = true;
if (paths.length === 0) {
console.error("error: no paths provided (see 'help' command for usage)");
return 1;
}
for (const path of paths) {
if (!isValidChangenoteFile(path)) {
valid = false;
}
}
return valid ? 0 : 1;
}

View File

@@ -0,0 +1,21 @@
{
"name": "changetool",
"version": "1.0.0",
"private": true,
"description": "Validates change-notes and merges them into CHANGELOG.md",
"license": "MIT",
"type": "module",
"scripts": {
"start": "tsx index.ts",
"test": "node --test --experimental-strip-types cli/*.test.ts"
},
"devDependencies": {
"@types/node": "^26.2.0",
"tsx": "^4.23.12",
"typescript": "^7.0.2"
},
"dependencies": {
"lite-matter": "^0.1.2",
"mdast-util-from-markdown": "^2.0.3"
}
}

View File

@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"module": "preserve",
"allowImportingTsExtensions": true,
"rootDir": ".",
"sourceMap": false
},
"include": ["./**/*.ts"],
"exclude": ["node_modules"]
}

View File

@@ -3,12 +3,11 @@ import path from "path";
import test from "ava";
import { EnvVar } from "../environment";
import { getRunnerLogger } from "../logging";
import { getTestEnv, setupTests } from "../testing-utils";
import { setupTests } from "../testing-utils";
import * as util from "../util";
import * as outputCache from "./output-cache";
import { getCachedCodeQlVersion } from "./output-cache";
setupTests(test);
@@ -18,18 +17,18 @@ test.serial(
"getCachedCodeQlVersion reuses a version persisted by an earlier step",
async (t) => {
await util.withTmpDir(async (tmpDir: string) => {
const cacheFile = path.join(tmpDir, "codeql-action-command-cache.json");
const cacheFilePath = path.join(tmpDir, "cache.json");
fs.writeFileSync(
cacheFile,
cacheFilePath,
JSON.stringify({
cmd: "/path/to/codeql",
entries: { version: { version: "2.20.0" } },
}),
"utf8",
);
const env = getTestEnv({ [EnvVar.TEMP]: tmpDir });
t.deepEqual(
outputCache.getCachedCodeQlVersion(logger, env, "/path/to/codeql"),
getCachedCodeQlVersion(logger, cacheFilePath, "/path/to/codeql"),
{
version: "2.20.0",
},
@@ -42,18 +41,17 @@ test.serial(
"getCachedCodeQlVersion ignores a persisted version from a different CLI",
async (t) => {
await util.withTmpDir(async (tmpDir: string) => {
const cacheFile = path.join(tmpDir, "version.json");
const cacheFilePath = path.join(tmpDir, "cache.json");
fs.writeFileSync(
cacheFile,
cacheFilePath,
JSON.stringify({
cmd: "/path/to/other-codeql",
version: { version: "2.20.0" },
entries: { version: { version: "2.20.0" } },
}),
"utf8",
);
const env = getTestEnv({ [EnvVar.TEMP]: tmpDir });
t.is(
outputCache.getCachedCodeQlVersion(logger, env, "/path/to/codeql"),
getCachedCodeQlVersion(logger, cacheFilePath, "/path/to/codeql"),
undefined,
);
});
@@ -64,11 +62,10 @@ test.serial(
"getCachedCodeQlVersion ignores a malformed persisted value",
async (t) => {
await util.withTmpDir(async (tmpDir: string) => {
const cacheFile = path.join(tmpDir, "version.json");
fs.writeFileSync(cacheFile, "not valid json", "utf8");
const env = getTestEnv({ [EnvVar.TEMP]: tmpDir });
const cacheFilePath = path.join(tmpDir, "cache.json");
fs.writeFileSync(cacheFilePath, "not valid json", "utf8");
t.is(
outputCache.getCachedCodeQlVersion(logger, env, "/path/to/codeql"),
getCachedCodeQlVersion(logger, cacheFilePath, "/path/to/codeql"),
undefined,
);
});
@@ -79,9 +76,7 @@ test.serial(
"getCachedCodeQlVersion ignores a persisted value with the wrong structure",
async (t) => {
await util.withTmpDir(async (tmpDir: string) => {
const cacheFile = path.join(tmpDir, "version.json");
const env = getTestEnv({ [EnvVar.TEMP]: tmpDir });
const cacheFilePath = path.join(tmpDir, "cache.json");
const testValues = [
{ cmd: "/path/to/codeql" },
{ entries: { version: { version: "2.20.0" } } },
@@ -104,9 +99,9 @@ test.serial(
].map((v) => JSON.stringify(v));
for (const value of testValues) {
fs.writeFileSync(cacheFile, value, "utf8");
fs.writeFileSync(cacheFilePath, value, "utf8");
t.is(
outputCache.getCachedCodeQlVersion(logger, env, "/path/to/codeql"),
getCachedCodeQlVersion(logger, cacheFilePath, "/path/to/codeql"),
undefined,
value,
);
@@ -117,10 +112,10 @@ test.serial(
test.serial("getCachedCodeQlVersion ignores non-existent file", async (t) => {
await util.withTmpDir(async (tmpDir: string) => {
const env = getTestEnv({ [EnvVar.TEMP]: tmpDir });
const cacheFilePath = path.join(tmpDir, "cache.json");
t.notThrows(() => {
t.is(
outputCache.getCachedCodeQlVersion(logger, env, "/path/to/codeql"),
getCachedCodeQlVersion(logger, cacheFilePath, "/path/to/codeql"),
undefined,
);
});

View File

@@ -3,9 +3,10 @@ import path from "path";
import { getTemporaryDirectory } from "../actions-util";
import { Env } from "../environment";
import * as json from "../json";
import { Logger } from "../logging";
import type { VersionInfo } from "./types";
import { VersionInfo, versionInfoBaseSchema } from "./types";
/**
* The keys of the command cache. Each key corresponds to a command whose output we cache.
@@ -13,12 +14,19 @@ import type { VersionInfo } from "./types";
export type CommandCacheKey = string;
/**
* The type of the command cache that is persisted to disk.
* The JSON schema of the command cache that is persisted to disk.
*/
export interface OutputCache {
cmd: string;
entries: Record<CommandCacheKey, unknown>;
}
const outputCacheSchema = {
cmd: json.string,
entries: json.object({}),
} as const satisfies json.Schema;
/**
* The type that describes the command cache that is persisted to disk.
*/
export type OutputCache = json.FromSchema<typeof outputCacheSchema> & {
entries: { version: VersionInfo };
};
/**
* The name of the temporary file that backs the on-disk cache of
@@ -43,18 +51,18 @@ export function resetCachedCodeQlVersion(): void {
* Returns the path to the temporary file that backs the
* on-disk cache of CLI responses between workflow steps.
*/
function getCommandCacheFilePath(env: Env): string {
export function getCommandCacheFilePath(env: Env): string {
return path.join(getTemporaryDirectory(env), COMMAND_CACHE_FILENAME);
}
/**
* Caches the CodeQL CLI version both in-memory and on disk.
* @param env The environment variables to use.
* @param cacheFilePath The path to the cache file.
* @param cmd The path to the CodeQL CLI.
* @param version The version information to cache.
*/
export function cacheCodeQlVersion(
env: Env,
cacheFilePath: string,
cmd: string,
version: VersionInfo,
): void {
@@ -70,22 +78,18 @@ export function cacheCodeQlVersion(
// processes, can reuse it rather than invoking `codeql version` again. We
// record the CLI path so that a different step using a different CodeQL bundle
// doesn't pick up a stale version.
fs.writeFileSync(
getCommandCacheFilePath(env),
JSON.stringify(outputCache),
"utf8",
);
fs.writeFileSync(cacheFilePath, JSON.stringify(outputCache), "utf8");
}
/**
* Returns the cached CodeQL CLI version, if any.
* @param logger The logger to use for logging messages.
* @param env The environment variables to use.
* @param cacheFilePath The path to the cache file.
* @param cmd The path to the CodeQL CLI.
*/
export function getCachedCodeQlVersion(
logger: Logger,
env: Env,
cacheFilePath: string,
cmd?: string,
): undefined | VersionInfo {
if (cachedCodeQlVersion !== undefined) {
@@ -96,11 +100,9 @@ export function getCachedCodeQlVersion(
// invokes `codeql version` instead.
let serialized: string;
try {
serialized = fs.readFileSync(getCommandCacheFilePath(env), "utf8");
serialized = fs.readFileSync(cacheFilePath, "utf8");
} catch (e) {
logger.debug(
`Cannot read CLI-cache file ${getCommandCacheFilePath(env)}: ${e}`,
);
logger.debug(`Cannot read CLI-cache file ${cacheFilePath}: ${e}`);
return undefined;
}
let persisted: unknown;
@@ -127,17 +129,7 @@ export function getCachedCodeQlVersion(
* @param x The value to test
*/
function isVersionInfo(x: unknown): x is VersionInfo {
const candidate = x as Partial<VersionInfo> | null;
return (
typeof candidate === "object" &&
candidate !== null &&
typeof candidate.version === "string" &&
(candidate.features === undefined ||
(typeof candidate.features === "object" &&
candidate.features !== null)) &&
(candidate.overlayVersion === undefined ||
typeof candidate.overlayVersion === "number")
);
return json.isObject(x) && json.validateSchema(versionInfoBaseSchema, x);
}
/**
@@ -145,12 +137,10 @@ function isVersionInfo(x: unknown): x is VersionInfo {
* @param x The value to test
*/
function isOutputCache(x: unknown): x is OutputCache {
const candidate = x as Partial<OutputCache> | null;
return (
typeof candidate === "object" &&
candidate !== null &&
typeof candidate.cmd === "string" &&
candidate.entries !== undefined &&
isVersionInfo(candidate.entries.version)
json.isObject(x) &&
json.validateSchema(outputCacheSchema, x) &&
json.isObject<{ version: unknown }>(x.entries) &&
isVersionInfo(x.entries.version)
);
}

View File

@@ -1,6 +1,11 @@
export interface VersionInfo {
version: string;
features?: { [name: string]: boolean };
import * as json from "../json";
/**
* The JSON schema of the expected output of the `codeql version` command.
*/
export const versionInfoBaseSchema = {
version: json.string,
features: json.optional(json.object({})),
/**
* The overlay version helps deal with backward incompatible changes for
* overlay analysis. When a precompiled query pack reports the same overlay
@@ -9,5 +14,17 @@ export interface VersionInfo {
* or if either the pack or the CLI does not report an overlay version,
* we need to revert to non-overlay analysis.
*/
overlayVersion?: number;
}
overlayVersion: json.optional(json.number),
} as const satisfies json.Schema;
/**
* The base type that describes the expected output of the `codeql version` command.
*/
export type VersionInfoBase = json.FromSchema<typeof versionInfoBaseSchema>;
/**
* The full type that describes the expected output of the `codeql version` command.
*/
export type VersionInfo = Omit<VersionInfoBase, "features"> & {
features?: { [name: string]: boolean };
};

View File

@@ -510,7 +510,12 @@ async function getCodeQLForCmd(
return cmd;
},
async getVersion() {
let result = outputCache.getCachedCodeQlVersion(logger, getEnv(), cmd);
const cacheFilePath = outputCache.getCommandCacheFilePath(getEnv());
let result = outputCache.getCachedCodeQlVersion(
logger,
cacheFilePath,
cmd,
);
if (result === undefined) {
result = await runCliJson<VersionInfo>(
cmd,
@@ -519,7 +524,7 @@ async function getCodeQLForCmd(
noStreamStdout: true,
},
);
outputCache.cacheCodeQlVersion(getEnv(), cmd, result);
outputCache.cacheCodeQlVersion(cacheFilePath, cmd, result);
}
return result;
},

View File

@@ -14,7 +14,10 @@ import {
isSelfHostedRunner,
} from "./actions-util";
import { getAnalysisKey, getApiClient } from "./api-client";
import { getCachedCodeQlVersion } from "./cli/output-cache";
import {
getCachedCodeQlVersion,
getCommandCacheFilePath,
} from "./cli/output-cache";
import type { Config } from "./config/action-config";
import type { ComputedInput, InputName } from "./config/inputs";
import { parseRegistriesWithoutCredentials } from "./config/pack-registries";
@@ -376,7 +379,10 @@ export async function createStatusReportBase(
core.exportVariable(EnvVar.WORKFLOW_STARTED_AT, workflowStartedAt);
}
const runnerOs = getRequiredEnvParam("RUNNER_OS");
const codeQlCliVersion = getCachedCodeQlVersion(logger, getEnv());
const codeQlCliVersion = getCachedCodeQlVersion(
logger,
getCommandCacheFilePath(getEnv()),
);
const actionRef = process.env["GITHUB_ACTION_REF"] || "";
const testingEnvironment = getTestingEnvironment();
// re-export the testing environment variable so that it is available to subsequent steps,

View File

@@ -37,5 +37,5 @@
"@octokit/core/dist-types/types": ["./node_modules/@octokit/core/dist-types/types.d.ts"]
},
},
"exclude": ["node_modules", "pr-checks"]
"exclude": ["node_modules", "pr-checks", "scripts/changetool"]
}