mirror of
https://github.com/github/codeql-action.git
synced 2026-10-03 09:14:58 +00:00
Merge branch 'main' into dependabot/github_actions/dot-github/workflows/actions/setup-java-6.0.0
This commit is contained in:
16
.github/instructions/release-merge.instructions.md
vendored
Normal file
16
.github/instructions/release-merge.instructions.md
vendored
Normal 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`.
|
||||
27
.github/workflows/pr-checks.yml
vendored
27
.github/workflows/pr-checks.yml
vendored
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
462
lib/entry-points.js
generated
@@ -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
1170
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
200
scripts/changetool/cli/validate.test.ts
Normal file
200
scripts/changetool/cli/validate.test.ts
Normal 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);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
121
scripts/changetool/cli/validate.ts
Normal file
121
scripts/changetool/cli/validate.ts
Normal 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;
|
||||
}
|
||||
51
scripts/changetool/index.ts
Normal file
51
scripts/changetool/index.ts
Normal 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;
|
||||
}
|
||||
21
scripts/changetool/package.json
Normal file
21
scripts/changetool/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
11
scripts/changetool/tsconfig.json
Normal file
11
scripts/changetool/tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"module": "preserve",
|
||||
"allowImportingTsExtensions": true,
|
||||
"rootDir": ".",
|
||||
"sourceMap": false
|
||||
},
|
||||
"include": ["./**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@@ -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,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 };
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user