mirror of
https://github.com/github/codeql-action.git
synced 2026-05-09 15:20:28 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 25599b3fc8 | |||
| 639dddc728 | |||
| 0b27c71731 | |||
| 6679b049d5 | |||
| 13e47f85a5 | |||
| 5f152be4c2 | |||
| f4b6bd982f |
@@ -0,0 +1,30 @@
|
||||
export const ACTION_VERSIONS = {
|
||||
"actions/checkout": {
|
||||
"version": "v6"
|
||||
},
|
||||
"actions/setup-go": {
|
||||
"version": "v6"
|
||||
},
|
||||
"actions/setup-dotnet": {
|
||||
"version": "v5"
|
||||
},
|
||||
"actions/upload-artifact": {
|
||||
"version": "v7"
|
||||
},
|
||||
"actions/github-script": {
|
||||
"version": "v8"
|
||||
},
|
||||
"actions/setup-python": {
|
||||
"version": "v6"
|
||||
},
|
||||
"actions/setup-java": {
|
||||
"version": "v5"
|
||||
},
|
||||
"actions/setup-node": {
|
||||
"version": "v6"
|
||||
},
|
||||
"ruby/setup-ruby": {
|
||||
"version": "09a7688d3b55cf0e976497ff046b70949eeaccfd",
|
||||
"comment": " v1.288.0"
|
||||
}
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
name: "Autobuild direct tracing (custom working directory)"
|
||||
description: >
|
||||
description: |
|
||||
An end-to-end integration test of a Java repository built using 'build-mode: autobuild',
|
||||
with direct tracing enabled and a custom working directory specified as the input to the
|
||||
autobuild Action.
|
||||
|
||||
@@ -4,12 +4,11 @@
|
||||
# basic mechanics of multi-registry auth is working.
|
||||
name: "Packaging: Download using registries"
|
||||
description: "Checks that specifying a registries block and associated auth works as expected"
|
||||
versions: [
|
||||
# This feature is not compatible with older CLIs
|
||||
"default",
|
||||
"linked",
|
||||
"nightly-latest",
|
||||
]
|
||||
versions:
|
||||
# This feature is not compatible with older CLIs
|
||||
- "default"
|
||||
- "linked"
|
||||
- "nightly-latest"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -24,9 +23,9 @@ steps:
|
||||
config-file: ./.github/codeql/codeql-config-registries.yml
|
||||
languages: javascript
|
||||
registries: |
|
||||
- url: "https://ghcr.io/v2/"
|
||||
packages: "*/*"
|
||||
token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
- url: "https://ghcr.io/v2/"
|
||||
packages: "*/*"
|
||||
token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
- name: Verify packages installed
|
||||
run: |
|
||||
|
||||
@@ -4,7 +4,7 @@ versions: ["linked", "nightly-latest"]
|
||||
steps:
|
||||
- uses: ./../action/init
|
||||
with:
|
||||
languages: actions # Any language without overlay support will do
|
||||
languages: actions # Any language without overlay support will do
|
||||
tools: ${{ steps.prepare-test.outputs.tools-url }}
|
||||
env:
|
||||
CODEQL_OVERLAY_DATABASE_MODE: overlay-base
|
||||
|
||||
+17
-6
@@ -5,6 +5,8 @@ import * as path from "path";
|
||||
|
||||
import * as yaml from "yaml";
|
||||
|
||||
import { ACTION_VERSIONS } from "./action-versions";
|
||||
|
||||
/** Known workflow input names. */
|
||||
enum KnownInputName {
|
||||
GoVersion = "go-version",
|
||||
@@ -13,6 +15,9 @@ enum KnownInputName {
|
||||
DotnetVersion = "dotnet-version",
|
||||
}
|
||||
|
||||
/** Known Action names that we have version information for. */
|
||||
type KnownAction = keyof typeof ACTION_VERSIONS;
|
||||
|
||||
/**
|
||||
* Represents workflow input definitions.
|
||||
*/
|
||||
@@ -94,6 +99,12 @@ const THIS_DIR = __dirname;
|
||||
const CHECKS_DIR = path.join(THIS_DIR, "checks");
|
||||
const OUTPUT_DIR = path.join(THIS_DIR, "..", ".github", "workflows");
|
||||
|
||||
/** Gets an `actionName@ref` string for `actionName`. */
|
||||
function getActionRef(actionName: KnownAction): string {
|
||||
const versionInfo = ACTION_VERSIONS[actionName];
|
||||
return `${actionName}@${versionInfo.version}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads and parses a YAML file.
|
||||
*/
|
||||
@@ -216,7 +227,7 @@ function main(): void {
|
||||
const steps: any[] = [
|
||||
{
|
||||
name: "Check out repository",
|
||||
uses: "actions/checkout@v6",
|
||||
uses: getActionRef("actions/checkout"),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -226,7 +237,7 @@ function main(): void {
|
||||
steps.push(
|
||||
{
|
||||
name: "Install Node.js",
|
||||
uses: "actions/setup-node@v6",
|
||||
uses: getActionRef("actions/setup-node"),
|
||||
with: {
|
||||
"node-version": "20.x",
|
||||
cache: "npm",
|
||||
@@ -265,7 +276,7 @@ function main(): void {
|
||||
|
||||
steps.push({
|
||||
name: "Install Go",
|
||||
uses: "actions/setup-go@v6",
|
||||
uses: getActionRef("actions/setup-go"),
|
||||
with: {
|
||||
"go-version":
|
||||
"${{ inputs.go-version || '" + baseGoVersionExpr + "' }}",
|
||||
@@ -289,7 +300,7 @@ function main(): void {
|
||||
|
||||
steps.push({
|
||||
name: "Install Java",
|
||||
uses: "actions/setup-java@v5",
|
||||
uses: getActionRef("actions/setup-java"),
|
||||
with: {
|
||||
"java-version":
|
||||
"${{ inputs.java-version || '" + baseJavaVersionExpr + "' }}",
|
||||
@@ -312,7 +323,7 @@ function main(): void {
|
||||
steps.push({
|
||||
name: "Install Python",
|
||||
if: "matrix.version != 'nightly-latest'",
|
||||
uses: "actions/setup-python@v6",
|
||||
uses: getActionRef("actions/setup-python"),
|
||||
with: {
|
||||
"python-version":
|
||||
"${{ inputs.python-version || '" + basePythonVersionExpr + "' }}",
|
||||
@@ -333,7 +344,7 @@ function main(): void {
|
||||
|
||||
steps.push({
|
||||
name: "Install .NET",
|
||||
uses: "actions/setup-dotnet@v5",
|
||||
uses: getActionRef("actions/setup-dotnet"),
|
||||
with: {
|
||||
"dotnet-version":
|
||||
"${{ inputs.dotnet-version || '" + baseDotNetVersionExpr + "' }}",
|
||||
|
||||
+95
-80
@@ -11,15 +11,18 @@ import * as path from "node:path";
|
||||
import { afterEach, beforeEach, describe, it } from "node:test";
|
||||
|
||||
import {
|
||||
Options,
|
||||
scanGeneratedWorkflows,
|
||||
updateSyncTs,
|
||||
updateActionVersions,
|
||||
updateTemplateFiles,
|
||||
} from "./sync_back";
|
||||
|
||||
let testDir: string;
|
||||
let workflowDir: string;
|
||||
let checksDir: string;
|
||||
let syncTsPath: string;
|
||||
let actionVersionsTsPath: string;
|
||||
|
||||
const defaultOptions: Options = { verbose: false, force: false };
|
||||
|
||||
beforeEach(() => {
|
||||
/** Set up temporary directories and files for testing */
|
||||
@@ -29,8 +32,8 @@ beforeEach(() => {
|
||||
fs.mkdirSync(workflowDir, { recursive: true });
|
||||
fs.mkdirSync(checksDir, { recursive: true });
|
||||
|
||||
// Create sync.ts file path
|
||||
syncTsPath = path.join(testDir, "pr-checks", "sync.ts");
|
||||
// Create action-versions.ts file path
|
||||
actionVersionsTsPath = path.join(testDir, "pr-checks", "action-versions.ts");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -56,9 +59,18 @@ jobs:
|
||||
|
||||
const result = scanGeneratedWorkflows(workflowDir);
|
||||
|
||||
assert.equal(result["actions/checkout"], "v4");
|
||||
assert.equal(result["actions/setup-node"], "v5");
|
||||
assert.equal(result["actions/setup-go"], "v6");
|
||||
assert.deepEqual(result["actions/checkout"], {
|
||||
version: "v4",
|
||||
comment: undefined,
|
||||
});
|
||||
assert.deepEqual(result["actions/setup-node"], {
|
||||
version: "v5",
|
||||
comment: undefined,
|
||||
});
|
||||
assert.deepEqual(result["actions/setup-go"], {
|
||||
version: "v6",
|
||||
comment: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it("scanning workflows with version comments", () => {
|
||||
@@ -78,12 +90,18 @@ jobs:
|
||||
|
||||
const result = scanGeneratedWorkflows(workflowDir);
|
||||
|
||||
assert.equal(result["actions/checkout"], "v4");
|
||||
assert.equal(
|
||||
result["ruby/setup-ruby"],
|
||||
"44511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0",
|
||||
);
|
||||
assert.equal(result["actions/setup-python"], "v6 # Latest Python");
|
||||
assert.deepEqual(result["actions/checkout"], {
|
||||
version: "v4",
|
||||
comment: undefined,
|
||||
});
|
||||
assert.deepEqual(result["ruby/setup-ruby"], {
|
||||
version: "44511735964dcb71245e7e55f72539531f7bc0eb",
|
||||
comment: " v1.257.0",
|
||||
});
|
||||
assert.deepEqual(result["actions/setup-python"], {
|
||||
version: "v6",
|
||||
comment: " Latest Python",
|
||||
});
|
||||
});
|
||||
|
||||
it("ignores local actions", () => {
|
||||
@@ -103,89 +121,76 @@ jobs:
|
||||
|
||||
const result = scanGeneratedWorkflows(workflowDir);
|
||||
|
||||
assert.equal(result["actions/checkout"], "v4");
|
||||
assert.deepEqual(result["actions/checkout"], {
|
||||
version: "v4",
|
||||
comment: undefined,
|
||||
});
|
||||
assert.equal("./.github/actions/local-action" in result, false);
|
||||
assert.equal("./another-local-action" in result, false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("updateSyncTs", () => {
|
||||
it("updates sync.ts file", () => {
|
||||
/** Test updating sync.ts file */
|
||||
const syncTsContent = `
|
||||
const steps = [
|
||||
{
|
||||
uses: "actions/setup-node@v4",
|
||||
with: { "node-version": "16" },
|
||||
describe("updateActionVersions", () => {
|
||||
it("updates action-versions.ts file", () => {
|
||||
/** Test updating action-versions.ts file */
|
||||
const actionVersionsTsContent = `
|
||||
export const ACTION_VERSIONS = {
|
||||
"actions/setup-node": {
|
||||
"version": "v4"
|
||||
},
|
||||
{
|
||||
uses: "actions/setup-go@v5",
|
||||
with: { "go-version": "1.19" },
|
||||
},
|
||||
];
|
||||
`;
|
||||
"actions/setup-go": {
|
||||
"version": "v5"
|
||||
}
|
||||
};
|
||||
`.trim();
|
||||
|
||||
fs.writeFileSync(syncTsPath, syncTsContent);
|
||||
fs.writeFileSync(actionVersionsTsPath, actionVersionsTsContent);
|
||||
|
||||
const actionVersions = {
|
||||
"actions/setup-node": "v5",
|
||||
"actions/setup-go": "v6",
|
||||
"actions/setup-node": { version: "v5" },
|
||||
"actions/setup-go": { version: "v6" },
|
||||
};
|
||||
|
||||
const result = updateSyncTs(syncTsPath, actionVersions);
|
||||
const result = updateActionVersions(
|
||||
defaultOptions,
|
||||
actionVersionsTsPath,
|
||||
actionVersions,
|
||||
);
|
||||
assert.equal(result, true);
|
||||
|
||||
const updatedContent = fs.readFileSync(syncTsPath, "utf8");
|
||||
const updatedContent = fs.readFileSync(actionVersionsTsPath, "utf8");
|
||||
|
||||
assert.ok(updatedContent.includes('uses: "actions/setup-node@v5"'));
|
||||
assert.ok(updatedContent.includes('uses: "actions/setup-go@v6"'));
|
||||
});
|
||||
|
||||
it("strips comments from versions", () => {
|
||||
/** Test updating sync.ts file when versions have comments */
|
||||
const syncTsContent = `
|
||||
const steps = [
|
||||
{
|
||||
uses: "actions/setup-node@v4",
|
||||
with: { "node-version": "16" },
|
||||
},
|
||||
];
|
||||
`;
|
||||
|
||||
fs.writeFileSync(syncTsPath, syncTsContent);
|
||||
|
||||
const actionVersions = {
|
||||
"actions/setup-node": "v5 # Latest version",
|
||||
};
|
||||
|
||||
const result = updateSyncTs(syncTsPath, actionVersions);
|
||||
assert.equal(result, true);
|
||||
|
||||
const updatedContent = fs.readFileSync(syncTsPath, "utf8");
|
||||
|
||||
// sync.ts should get the version without comment
|
||||
assert.ok(updatedContent.includes('uses: "actions/setup-node@v5"'));
|
||||
assert.ok(!updatedContent.includes("# Latest version"));
|
||||
assert.ok(
|
||||
updatedContent.includes('"actions/setup-node": {\n "version": "v5"'),
|
||||
);
|
||||
assert.ok(
|
||||
updatedContent.includes('"actions/setup-go": {\n "version": "v6"'),
|
||||
);
|
||||
});
|
||||
|
||||
it("returns false when no changes are needed", () => {
|
||||
/** Test that updateSyncTs returns false when no changes are needed */
|
||||
const syncTsContent = `
|
||||
const steps = [
|
||||
{
|
||||
uses: "actions/setup-node@v5",
|
||||
with: { "node-version": "16" },
|
||||
},
|
||||
];
|
||||
`;
|
||||
/** Test that updateActionVersions returns false when no changes are needed */
|
||||
const actionVersionsTsContent = `
|
||||
export const ACTION_VERSIONS = {
|
||||
"actions/setup-node": {
|
||||
"version": "v5"
|
||||
}
|
||||
};
|
||||
`.trimStart();
|
||||
|
||||
fs.writeFileSync(syncTsPath, syncTsContent);
|
||||
fs.writeFileSync(actionVersionsTsPath, actionVersionsTsContent);
|
||||
|
||||
const actionVersions = {
|
||||
"actions/setup-node": "v5",
|
||||
"actions/setup-node": { version: "v5" },
|
||||
};
|
||||
|
||||
const result = updateSyncTs(syncTsPath, actionVersions);
|
||||
const result = updateActionVersions(
|
||||
defaultOptions,
|
||||
actionVersionsTsPath,
|
||||
actionVersions,
|
||||
);
|
||||
const updatedContent = fs.readFileSync(actionVersionsTsPath, "utf8");
|
||||
assert.equal(updatedContent, actionVersionsTsContent);
|
||||
assert.equal(result, false);
|
||||
});
|
||||
});
|
||||
@@ -206,11 +211,15 @@ steps:
|
||||
fs.writeFileSync(templatePath, templateContent);
|
||||
|
||||
const actionVersions = {
|
||||
"actions/checkout": "v4",
|
||||
"actions/setup-node": "v5 # Latest",
|
||||
"actions/checkout": { version: "v4" },
|
||||
"actions/setup-node": { version: "v5", comment: " Latest" },
|
||||
};
|
||||
|
||||
const result = updateTemplateFiles(checksDir, actionVersions);
|
||||
const result = updateTemplateFiles(
|
||||
defaultOptions,
|
||||
checksDir,
|
||||
actionVersions,
|
||||
);
|
||||
assert.equal(result.length, 1);
|
||||
assert.ok(result.includes(templatePath));
|
||||
|
||||
@@ -232,11 +241,17 @@ steps:
|
||||
fs.writeFileSync(templatePath, templateContent);
|
||||
|
||||
const actionVersions = {
|
||||
"ruby/setup-ruby":
|
||||
"55511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0",
|
||||
"ruby/setup-ruby": {
|
||||
version: "55511735964dcb71245e7e55f72539531f7bc0eb",
|
||||
comment: " v1.257.0",
|
||||
},
|
||||
};
|
||||
|
||||
const result = updateTemplateFiles(checksDir, actionVersions);
|
||||
const result = updateTemplateFiles(
|
||||
defaultOptions,
|
||||
checksDir,
|
||||
actionVersions,
|
||||
);
|
||||
assert.equal(result.length, 1);
|
||||
|
||||
const updatedContent = fs.readFileSync(templatePath, "utf8");
|
||||
|
||||
+139
-76
@@ -1,5 +1,7 @@
|
||||
#!/usr/bin/env npx tsx
|
||||
|
||||
import * as yaml from "yaml";
|
||||
|
||||
/*
|
||||
Sync-back script to automatically update action versions in source templates
|
||||
from the generated workflow files after Dependabot updates.
|
||||
@@ -25,7 +27,71 @@ import * as path from "path";
|
||||
const THIS_DIR = __dirname;
|
||||
const CHECKS_DIR = path.join(THIS_DIR, "checks");
|
||||
const WORKFLOW_DIR = path.join(THIS_DIR, "..", ".github", "workflows");
|
||||
const SYNC_TS_PATH = path.join(THIS_DIR, "sync.ts");
|
||||
const ACTION_VERSIONS_PATH = path.join(THIS_DIR, "action-versions.ts");
|
||||
|
||||
/** Command-line options for this program. */
|
||||
export type Options = {
|
||||
verbose: boolean;
|
||||
force: boolean;
|
||||
};
|
||||
|
||||
/** Records information about the version of an Action with an optional comment. */
|
||||
type ActionVersion = { version: string; comment?: string };
|
||||
|
||||
/** Converts `info` to a string that includes the version and comment. */
|
||||
function versionWithCommentStr(info: ActionVersion): string {
|
||||
const comment = info.comment ? ` #${info.comment}` : "";
|
||||
return `${info.version}${comment}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a `yaml.visitor` which calls `fn` for `yaml.Pair` nodes where the key is "uses" and
|
||||
* the value is a `yaml.Scalar`.
|
||||
*/
|
||||
function usesVisitor(
|
||||
fn: (
|
||||
pair: yaml.Pair<yaml.Scalar, yaml.Scalar>,
|
||||
actionName: string,
|
||||
actionVersion: ActionVersion,
|
||||
) => void,
|
||||
): yaml.visitor {
|
||||
return {
|
||||
Pair(_, pair) {
|
||||
if (
|
||||
yaml.isScalar(pair.key) &&
|
||||
yaml.isScalar(pair.value) &&
|
||||
pair.key.value === "uses" &&
|
||||
typeof pair.value.value === "string"
|
||||
) {
|
||||
const usesValue = pair.value.value;
|
||||
|
||||
// Only track non-local actions (those with / but not starting with ./)
|
||||
if (!usesValue.startsWith("./")) {
|
||||
const parts = (pair.value.value as string).split("@");
|
||||
|
||||
if (parts.length !== 2) {
|
||||
throw new Error(`Unexpected 'uses' value: ${usesValue}`);
|
||||
}
|
||||
|
||||
const actionName = parts[0];
|
||||
const actionVersion = parts[1].trimEnd();
|
||||
const comment = pair.value.comment?.trimEnd();
|
||||
|
||||
fn(pair as yaml.Pair<yaml.Scalar, yaml.Scalar>, actionName, {
|
||||
version: actionVersion,
|
||||
comment,
|
||||
});
|
||||
}
|
||||
|
||||
// Do not visit the children of this node.
|
||||
return yaml.visit.SKIP;
|
||||
}
|
||||
|
||||
// Do nothing and continue.
|
||||
return undefined;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan generated workflow files to extract the latest action versions.
|
||||
@@ -33,8 +99,10 @@ const SYNC_TS_PATH = path.join(THIS_DIR, "sync.ts");
|
||||
* @param workflowDir - Path to .github/workflows directory
|
||||
* @returns Map from action names to their latest versions (including comments)
|
||||
*/
|
||||
export function scanGeneratedWorkflows(workflowDir: string): Record<string, string> {
|
||||
const actionVersions: Record<string, string> = {};
|
||||
export function scanGeneratedWorkflows(
|
||||
workflowDir: string,
|
||||
): Record<string, ActionVersion> {
|
||||
const actionVersions: Record<string, ActionVersion> = {};
|
||||
|
||||
const generatedFiles = fs
|
||||
.readdirSync(workflowDir)
|
||||
@@ -43,86 +111,63 @@ export function scanGeneratedWorkflows(workflowDir: string): Record<string, stri
|
||||
|
||||
for (const filePath of generatedFiles) {
|
||||
const content = fs.readFileSync(filePath, "utf8");
|
||||
const doc = yaml.parseDocument(content);
|
||||
|
||||
// Find all action uses in the file, including potential comments
|
||||
// This pattern captures: action_name@version_with_possible_comment
|
||||
const pattern = /uses:\s+([^/\s]+\/[^@\s]+)@([^@\n]+)/g;
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
while ((match = pattern.exec(content)) !== null) {
|
||||
const actionName = match[1];
|
||||
const versionWithComment = match[2].trimEnd();
|
||||
|
||||
// Only track non-local actions (those with / but not starting with ./)
|
||||
if (!actionName.startsWith("./")) {
|
||||
yaml.visit(
|
||||
doc,
|
||||
usesVisitor((_node, actionName, actionVersion) => {
|
||||
// Assume that version numbers are consistent (this should be the case on a Dependabot update PR)
|
||||
actionVersions[actionName] = versionWithComment;
|
||||
}
|
||||
}
|
||||
actionVersions[actionName] = actionVersion;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return actionVersions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update hardcoded action versions in pr-checks/sync.ts
|
||||
* Update hardcoded action versions in pr-checks/action-versions.ts
|
||||
*
|
||||
* @param syncTsPath - Path to sync.ts file
|
||||
* @param options - The command-line options.
|
||||
* @param actionVersionsTsPath - Path to action-versions.ts file
|
||||
* @param actionVersions - Map of action names to versions (may include comments)
|
||||
* @returns True if the file was modified, false otherwise
|
||||
*/
|
||||
export function updateSyncTs(
|
||||
syncTsPath: string,
|
||||
actionVersions: Record<string, string>,
|
||||
export function updateActionVersions(
|
||||
options: Options,
|
||||
actionVersionsTsPath: string,
|
||||
actionVersions: Record<string, ActionVersion>,
|
||||
): boolean {
|
||||
if (!fs.existsSync(syncTsPath)) {
|
||||
throw new Error(`Could not find ${syncTsPath}`);
|
||||
}
|
||||
// Build content for the file.
|
||||
let newContent: string = `export const ACTION_VERSIONS = ${JSON.stringify(actionVersions, null, 2)};\n`;
|
||||
|
||||
let content = fs.readFileSync(syncTsPath, "utf8");
|
||||
const originalContent = content;
|
||||
if (fs.existsSync(actionVersionsTsPath)) {
|
||||
const content = fs.readFileSync(actionVersionsTsPath, "utf8");
|
||||
|
||||
if (content === newContent && !options.force) {
|
||||
console.info(`No changes needed in ${actionVersionsTsPath}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Update hardcoded action versions
|
||||
for (const [actionName, versionWithComment] of Object.entries(
|
||||
actionVersions,
|
||||
)) {
|
||||
// Extract just the version part (before any comment) for sync.ts
|
||||
const version = versionWithComment.includes("#")
|
||||
? versionWithComment.split("#")[0].trim()
|
||||
: versionWithComment.trim();
|
||||
|
||||
// Look for patterns like uses: "actions/setup-node@v4"
|
||||
// Note that this will break if we store an Action uses reference in a
|
||||
// variable - that's a risk we're happy to take since in that case the
|
||||
// PR checks will just fail.
|
||||
const escaped = actionName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
const pattern = new RegExp(
|
||||
`(uses:\\s*")${escaped}@(?:[^"]+)(")`,
|
||||
"g",
|
||||
);
|
||||
content = content.replace(pattern, `$1${actionName}@${version}$2`);
|
||||
}
|
||||
|
||||
if (content !== originalContent) {
|
||||
fs.writeFileSync(syncTsPath, content, "utf8");
|
||||
console.info(`Updated ${syncTsPath}`);
|
||||
return true;
|
||||
} else {
|
||||
console.info(`No changes needed in ${syncTsPath}`);
|
||||
return false;
|
||||
}
|
||||
fs.writeFileSync(actionVersionsTsPath, newContent, "utf8");
|
||||
console.info(`Updated ${actionVersionsTsPath}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update action versions in template files in pr-checks/checks/
|
||||
*
|
||||
* @param options - The command-line options.
|
||||
* @param checksDir - Path to pr-checks/checks directory
|
||||
* @param actionVersions - Map of action names to versions (may include comments)
|
||||
* @returns List of files that were modified
|
||||
*/
|
||||
export function updateTemplateFiles(
|
||||
options: Options,
|
||||
checksDir: string,
|
||||
actionVersions: Record<string, string>,
|
||||
actionVersions: Record<string, ActionVersion>,
|
||||
): string[] {
|
||||
const modifiedFiles: string[] = [];
|
||||
|
||||
@@ -132,24 +177,33 @@ export function updateTemplateFiles(
|
||||
.map((f) => path.join(checksDir, f));
|
||||
|
||||
for (const filePath of templateFiles) {
|
||||
let content = fs.readFileSync(filePath, "utf8");
|
||||
const originalContent = content;
|
||||
const content = fs.readFileSync(filePath, "utf8");
|
||||
const doc = yaml.parseDocument(content, { keepSourceTokens: true });
|
||||
let modified: boolean = false;
|
||||
|
||||
// Update action versions
|
||||
for (const [actionName, versionWithComment] of Object.entries(
|
||||
actionVersions,
|
||||
)) {
|
||||
// Look for patterns like 'uses: actions/setup-node@v4' or 'uses: actions/setup-node@sha # comment'
|
||||
const escaped = actionName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
const pattern = new RegExp(
|
||||
`(uses:\\s+${escaped})@(?:[^@\n]+)`,
|
||||
"g",
|
||||
yaml.visit(
|
||||
doc,
|
||||
usesVisitor((pair, actionName, actionVersion) => {
|
||||
// Try to look up version information for this action.
|
||||
const versionInfo = actionVersions[actionName];
|
||||
|
||||
// If we found version information, and the version is different from that in the template,
|
||||
// then update the pair node accordingly.
|
||||
if (versionInfo && versionInfo.version !== actionVersion.version) {
|
||||
pair.value.value = `${actionName}@${versionInfo.version}`;
|
||||
pair.value.comment = versionInfo.comment;
|
||||
modified = true;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
// Write the YAML document back to the file if we made changes.
|
||||
if (modified || options.force) {
|
||||
fs.writeFileSync(
|
||||
filePath,
|
||||
doc.toString({ lineWidth: 0, flowCollectionPadding: false }),
|
||||
"utf8",
|
||||
);
|
||||
content = content.replace(pattern, `$1@${versionWithComment}`);
|
||||
}
|
||||
|
||||
if (content !== originalContent) {
|
||||
fs.writeFileSync(filePath, content, "utf8");
|
||||
modifiedFiles.push(filePath);
|
||||
console.info(`Updated ${filePath}`);
|
||||
}
|
||||
@@ -166,6 +220,11 @@ function main(): number {
|
||||
short: "v",
|
||||
default: false,
|
||||
},
|
||||
force: {
|
||||
type: "boolean",
|
||||
short: "f",
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
strict: true,
|
||||
});
|
||||
@@ -178,7 +237,7 @@ function main(): number {
|
||||
if (verbose) {
|
||||
console.info("Found action versions:");
|
||||
for (const [action, version] of Object.entries(actionVersions)) {
|
||||
console.info(` ${action}@${version}`);
|
||||
console.info(` ${action}@${versionWithCommentStr(version)}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,12 +251,16 @@ function main(): number {
|
||||
const modifiedFiles: string[] = [];
|
||||
|
||||
// Update sync.ts
|
||||
if (updateSyncTs(SYNC_TS_PATH, actionVersions)) {
|
||||
modifiedFiles.push(SYNC_TS_PATH);
|
||||
if (updateActionVersions(values, ACTION_VERSIONS_PATH, actionVersions)) {
|
||||
modifiedFiles.push(ACTION_VERSIONS_PATH);
|
||||
}
|
||||
|
||||
// Update template files
|
||||
const templateModified = updateTemplateFiles(CHECKS_DIR, actionVersions);
|
||||
const templateModified = updateTemplateFiles(
|
||||
values,
|
||||
CHECKS_DIR,
|
||||
actionVersions,
|
||||
);
|
||||
modifiedFiles.push(...templateModified);
|
||||
|
||||
if (modifiedFiles.length > 0) {
|
||||
|
||||
Reference in New Issue
Block a user