mirror of
https://github.com/github/codeql-action.git
synced 2026-11-06 13:09:45 +00:00
This avoids failures on PRs where `sync-back.ts` is run as part of the "Rebuild" job if there are already version inconsistencies on the base branch.
310 lines
8.2 KiB
TypeScript
Executable File
310 lines
8.2 KiB
TypeScript
Executable File
#!/usr/bin/env npx tsx
|
|
|
|
/*
|
|
Tests for the sync-back.ts script
|
|
*/
|
|
|
|
import * as assert from "node:assert/strict";
|
|
import * as fs from "node:fs";
|
|
import * as os from "node:os";
|
|
import * as path from "node:path";
|
|
import { afterEach, beforeEach, describe, it } from "node:test";
|
|
|
|
import {
|
|
scanGeneratedWorkflows,
|
|
updateSyncTs,
|
|
updateTemplateFiles,
|
|
} from "./sync-back";
|
|
|
|
let testDir: string;
|
|
let workflowDir: string;
|
|
let checksDir: string;
|
|
let syncTsPath: string;
|
|
|
|
beforeEach(() => {
|
|
/** Set up temporary directories and files for testing */
|
|
testDir = fs.mkdtempSync(path.join(os.tmpdir(), "sync-back-test-"));
|
|
workflowDir = path.join(testDir, ".github", "workflows");
|
|
checksDir = path.join(testDir, "pr-checks", "checks");
|
|
fs.mkdirSync(workflowDir, { recursive: true });
|
|
fs.mkdirSync(checksDir, { recursive: true });
|
|
|
|
// Create sync.ts file path
|
|
syncTsPath = path.join(testDir, "pr-checks", "sync.ts");
|
|
});
|
|
|
|
afterEach(() => {
|
|
/** Clean up temporary directories */
|
|
fs.rmSync(testDir, { recursive: true, force: true });
|
|
});
|
|
|
|
describe("scanGeneratedWorkflows", async () => {
|
|
await it("basic workflow scanning", () => {
|
|
/** Test basic workflow scanning functionality */
|
|
const workflowContent = `
|
|
name: Test Workflow
|
|
jobs:
|
|
test:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/setup-node@v5
|
|
- uses: actions/setup-go@v6
|
|
`;
|
|
|
|
fs.writeFileSync(path.join(workflowDir, "__test.yml"), workflowContent);
|
|
|
|
const result = scanGeneratedWorkflows(workflowDir);
|
|
|
|
assert.deepEqual(
|
|
result.actionVersions["actions/checkout"],
|
|
new Set(["v4"]),
|
|
);
|
|
assert.deepEqual(
|
|
result.actionVersions["actions/setup-node"],
|
|
new Set(["v5"]),
|
|
);
|
|
assert.deepEqual(
|
|
result.actionVersions["actions/setup-go"],
|
|
new Set(["v6"]),
|
|
);
|
|
});
|
|
|
|
await it("scanning workflows with version comments", () => {
|
|
/** Test scanning workflows with version comments */
|
|
const workflowContent = `
|
|
name: Test Workflow
|
|
jobs:
|
|
test:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: ruby/setup-ruby@44511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0
|
|
- uses: actions/setup-python@v6 # Latest Python
|
|
`;
|
|
|
|
fs.writeFileSync(path.join(workflowDir, "__test.yml"), workflowContent);
|
|
|
|
const result = scanGeneratedWorkflows(workflowDir);
|
|
|
|
assert.deepEqual(
|
|
result.actionVersions["actions/checkout"],
|
|
new Set(["v4"]),
|
|
);
|
|
assert.deepEqual(
|
|
result.actionVersions["ruby/setup-ruby"],
|
|
new Set(["44511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0"]),
|
|
);
|
|
assert.deepEqual(
|
|
result.actionVersions["actions/setup-python"],
|
|
new Set(["v6 # Latest Python"]),
|
|
);
|
|
});
|
|
|
|
await it("ignores local actions", () => {
|
|
/** Test that local actions (starting with ./) are ignored */
|
|
const workflowContent = `
|
|
name: Test Workflow
|
|
jobs:
|
|
test:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: ./.github/actions/local-action
|
|
- uses: ./another-local-action@v1
|
|
`;
|
|
|
|
fs.writeFileSync(path.join(workflowDir, "__test.yml"), workflowContent);
|
|
|
|
const result = scanGeneratedWorkflows(workflowDir);
|
|
|
|
assert.deepEqual(
|
|
result.actionVersions["actions/checkout"],
|
|
new Set(["v4"]),
|
|
);
|
|
assert.equal(
|
|
"./.github/actions/local-action" in result.actionVersions,
|
|
false,
|
|
);
|
|
assert.equal("./another-local-action" in result.actionVersions, false);
|
|
});
|
|
});
|
|
|
|
describe("updateSyncTs", async () => {
|
|
await it("updates sync.ts file", () => {
|
|
/** Test updating sync.ts file */
|
|
const syncTsContent = `
|
|
const steps = [
|
|
{
|
|
uses: "actions/setup-node@v4",
|
|
with: { "node-version": "16" },
|
|
},
|
|
{
|
|
uses: "actions/setup-go@v5",
|
|
with: { "go-version": "1.19" },
|
|
},
|
|
];
|
|
`;
|
|
|
|
fs.writeFileSync(syncTsPath, syncTsContent);
|
|
|
|
const actionVersions = {
|
|
"actions/setup-node": new Set(["v5"]),
|
|
"actions/setup-go": new Set(["v6"]),
|
|
};
|
|
|
|
const result = updateSyncTs(syncTsPath, actionVersions);
|
|
assert.equal(result, true);
|
|
|
|
const updatedContent = fs.readFileSync(syncTsPath, "utf8");
|
|
|
|
assert.ok(updatedContent.includes('uses: "actions/setup-node@v5"'));
|
|
assert.ok(updatedContent.includes('uses: "actions/setup-go@v6"'));
|
|
});
|
|
|
|
await 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": new Set(["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"));
|
|
});
|
|
|
|
await 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" },
|
|
},
|
|
];
|
|
`;
|
|
|
|
fs.writeFileSync(syncTsPath, syncTsContent);
|
|
|
|
const actionVersions = {
|
|
"actions/setup-node": new Set(["v5"]),
|
|
};
|
|
|
|
const result = updateSyncTs(syncTsPath, actionVersions);
|
|
assert.equal(result, false);
|
|
});
|
|
|
|
await it("updates SHA-pinned pinnedUses references", () => {
|
|
/** Test updating `pinnedUses(...)` references with new SHA and version */
|
|
const syncTsContent = `
|
|
const steps = [
|
|
{
|
|
uses: pinnedUses(
|
|
"actions/setup-node",
|
|
"0000000000000000000000000000000000000000",
|
|
"v6.0.0",
|
|
),
|
|
},
|
|
];
|
|
`;
|
|
|
|
fs.writeFileSync(syncTsPath, syncTsContent);
|
|
|
|
const actionVersions = {
|
|
"actions/setup-node": new Set([
|
|
"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0",
|
|
]),
|
|
};
|
|
|
|
const result = updateSyncTs(syncTsPath, actionVersions);
|
|
assert.equal(result, true);
|
|
|
|
const updatedContent = fs.readFileSync(syncTsPath, "utf8");
|
|
|
|
assert.ok(
|
|
updatedContent.includes('"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e"'),
|
|
);
|
|
assert.ok(updatedContent.includes('"v6.4.0"'));
|
|
assert.ok(
|
|
!updatedContent.includes("0000000000000000000000000000000000000000"),
|
|
);
|
|
assert.ok(!updatedContent.includes('"v6.0.0"'));
|
|
});
|
|
});
|
|
|
|
describe("updateTemplateFiles", async () => {
|
|
await it("updates template files", () => {
|
|
/** Test updating template files */
|
|
const templateContent = `
|
|
name: Test Template
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 16
|
|
`;
|
|
|
|
const templatePath = path.join(checksDir, "test.yml");
|
|
fs.writeFileSync(templatePath, templateContent);
|
|
|
|
const actionVersions = {
|
|
"actions/checkout": "v4",
|
|
"actions/setup-node": "v5 # Latest",
|
|
};
|
|
const byWorkflow = { ["test.yml"]: actionVersions };
|
|
|
|
const result = updateTemplateFiles(checksDir, byWorkflow);
|
|
assert.equal(result.length, 1);
|
|
assert.ok(result.includes(templatePath));
|
|
|
|
const updatedContent = fs.readFileSync(templatePath, "utf8");
|
|
|
|
assert.ok(updatedContent.includes("uses: actions/checkout@v4"));
|
|
assert.ok(updatedContent.includes("uses: actions/setup-node@v5 # Latest"));
|
|
});
|
|
|
|
await it("preserves version comments", () => {
|
|
/** Test that updating template files preserves version comments */
|
|
const templateContent = `
|
|
name: Test Template
|
|
steps:
|
|
- uses: ruby/setup-ruby@44511735964dcb71245e7e55f72539531f7bc0eb # v1.256.0
|
|
`;
|
|
|
|
const templatePath = path.join(checksDir, "test.yml");
|
|
fs.writeFileSync(templatePath, templateContent);
|
|
|
|
const actionVersions = {
|
|
"ruby/setup-ruby": "55511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0",
|
|
};
|
|
const byWorkflow = { ["test.yml"]: actionVersions };
|
|
|
|
const result = updateTemplateFiles(checksDir, byWorkflow);
|
|
assert.equal(result.length, 1);
|
|
|
|
const updatedContent = fs.readFileSync(templatePath, "utf8");
|
|
|
|
assert.ok(
|
|
updatedContent.includes(
|
|
"uses: ruby/setup-ruby@55511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0",
|
|
),
|
|
);
|
|
});
|
|
});
|