Compare commits

..

40 Commits

Author SHA1 Message Date
Michael B. Gale
c5320e3a8a Merge pull request #4288 from github/dependabot/npm_and_yarn/node-forge-1.4.1
Bump node-forge from 1.4.0 to 1.4.1
2026-11-03 15:53:37 +00:00
Michael B. Gale
875ac0c36c Merge pull request #4287 from github/dependabot/npm_and_yarn/pr-checks/glob-14.0.3
Bump glob from 13.0.6 to 14.0.3 in /pr-checks
2026-11-03 15:49:12 +00:00
github-actions[bot]
d0468de4ad Rebuild 2026-11-03 15:38:38 +00:00
Michael B. Gale
6a39e2e9cd Merge branch 'main' into dependabot/npm_and_yarn/pr-checks/glob-14.0.3 2026-11-03 15:36:40 +00:00
Michael B. Gale
7c5ee8eaaf Merge branch 'main' into dependabot/npm_and_yarn/node-forge-1.4.1 2026-11-03 15:33:20 +00:00
Michael B. Gale
1f8cacc390 Merge pull request #4292 from github/mbg/ci/fix-sync-back
Maintain action versions by workflow in `sync-back.ts`
2026-11-03 14:27:13 +00:00
Michael B. Gale
8a05ce6067 Update JSDoc for scanGeneratedWorkflows 2026-11-03 13:41:11 +00:00
Michael B. Gale
de3ece651e Merge branch 'main' into mbg/ci/fix-sync-back 2026-11-03 12:58:27 +00:00
Michael B. Gale
cc88e02cc1 Update version of actions/download-artifact in unsupported-macos for consistency 2026-11-03 12:34:09 +00:00
Michael B. Gale
7b8e4ced67 Maintain action versions by workflow in sync-back.ts
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.
2026-11-03 12:34:09 +00:00
Henry Mercer
093c0af4c3 Merge pull request #4290 from github/mergeback/v4.39.2-to-main-1fc58d04
Mergeback v4.39.2 refs/heads/releases/v4 into main
2026-11-03 11:09:56 +00:00
github-actions[bot]
30626207b6 Rebuild 2026-11-03 10:46:49 +00:00
github-actions[bot]
eb1e5bd7ab Update changelog and version after v4.39.2 2026-11-03 10:46:39 +00:00
Henry Mercer
1fc58d04a4 Merge pull request #4286 from github/update-v4.39.2-c05042115
Merge main into releases/v4
2026-11-03 10:45:07 +00:00
dependabot[bot]
dc4b73f464 Bump node-forge from 1.4.0 to 1.4.1
Bumps [node-forge](https://github.com/digitalbazaar/forge) from 1.4.0 to 1.4.1.
- [Changelog](https://github.com/digitalbazaar/forge/blob/main/CHANGELOG.md)
- [Commits](https://github.com/digitalbazaar/forge/compare/v1.4.0...v1.4.1)

---
updated-dependencies:
- dependency-name: node-forge
  dependency-version: 1.4.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-11-03 10:27:23 +00:00
dependabot[bot]
3b22011a97 Bump glob from 13.0.6 to 14.0.3 in /pr-checks
Bumps [glob](https://github.com/isaacs/node-glob) from 13.0.6 to 14.0.3.
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v13.0.6...v14.0.3)

---
updated-dependencies:
- dependency-name: glob
  dependency-version: 14.0.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-11-03 10:27:18 +00:00
github-actions[bot]
d9984814e4 Update changelog for v4.39.2 2026-11-03 10:25:27 +00:00
Henry Mercer
0d5ac7a9b1 Merge pull request #4285 from github/henrymercer/automated-change-notes
Migrate bundle update workflow to new changenote tooling
2026-11-03 10:13:30 +00:00
Henry Mercer
39528452cf Remove unused updateChangelog from bundle-changelog.ts
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-11-02 17:40:43 +00:00
Henry Mercer
9bc74b13fb Create a changenote for bundle updates instead of editing the changelog
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-11-02 17:40:43 +00:00
Henry Mercer
c050421152 Merge pull request #4284 from github/update-bundle/codeql-bundle-v2.27.4
Update default bundle to 2.27.4
2026-11-02 16:43:58 +00:00
Henry Mercer
750fe8feea Merge branch 'main' into update-bundle/codeql-bundle-v2.27.4 2026-11-02 16:16:46 +00:00
github-actions[bot]
adfcd5ffb8 Add changelog note 2026-11-02 16:15:59 +00:00
github-actions[bot]
2ef3b62201 Update default bundle to codeql-bundle-v2.27.4 2026-11-02 16:15:52 +00:00
Michael B. Gale
b35c6c6aae Merge pull request #4251 from github/mbg/refactor-util/errors
Refactor a few things out of `util.ts`
2026-11-02 16:00:32 +00:00
Michael B. Gale
ac9f257611 Merge remote-tracking branch 'origin/main' into mbg/refactor-util/errors 2026-11-02 14:28:50 +00:00
Michael B. Gale
fb9d45340b Merge remote-tracking branch 'origin/main' into mbg/refactor-util/errors 2026-10-30 16:50:30 +00:00
Michael B. Gale
5c1005e0ae Merge branch 'main' into mbg/refactor-util/errors 2026-10-21 16:58:03 +01:00
Michael B. Gale
c29909a87f Improve typing of resolveFunction 2026-10-21 16:57:16 +01:00
Michael B. Gale
44f1c73c56 Fix resolveFunction call 2026-10-21 16:57:16 +01:00
Michael B. Gale
7def52843a Move initializeEnvironment out of util.ts 2026-10-21 16:57:16 +01:00
Michael B. Gale
774bc191bf Remove unused type parameters from Env classes 2026-10-21 16:57:15 +01:00
Michael B. Gale
dc2ed81cc7 Remove InstanceURL from util.ts 2026-10-16 22:04:05 +01:00
Michael B. Gale
02283e9542 Move more GitHub-related definitions from util.ts to github.ts 2026-10-16 21:54:02 +01:00
Michael B. Gale
86bc4448d3 Mark additional imports in util.ts as type-only 2026-10-16 21:52:19 +01:00
Michael B. Gale
9c72af5b2b Move codeQlVersionAtLeast out of util.ts and into CodeQL 2026-10-16 21:50:31 +01:00
Michael B. Gale
929f5c0db4 Move isDiskConfigurationError out of codeql.ts 2026-10-16 21:50:09 +01:00
Michael B. Gale
a76419a632 Move wrapError and getErrorMessage out of util.ts 2026-10-16 21:46:16 +01:00
Michael B. Gale
071544a67a Move HTTPError out of util.ts 2026-10-16 21:40:48 +01:00
Michael B. Gale
8bdea824e2 Move ConfigurationError out of util.ts 2026-10-16 21:39:54 +01:00
40 changed files with 825 additions and 532 deletions

View File

@@ -114,7 +114,7 @@ jobs:
- name: Install dependencies
run: npm ci
- name: Download workflow artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
pattern: test-mode-sarif-*
path: sarif

View File

@@ -113,11 +113,12 @@ jobs:
echo "CLI_VERSION=$cli_version" | tee -a "$GITHUB_ENV"
echo "PR_URL=$pr_url" | tee -a "$GITHUB_ENV"
- name: Create changelog note
- name: Create changenote
run: |
npx tsx pr-checks/bundle-changelog.ts
- name: Push changelog note
- name: Push changenote
run: |
git commit -am "Add changelog note"
git add unreleased-change-notes
git commit -m "Add changenote"
git push

View File

@@ -6,6 +6,13 @@ See the [releases page](https://github.com/github/codeql-action/releases) for th
No user facing changes.
## 4.39.2 - 03 Nov 2026
- Update default CodeQL bundle version to [2.27.4](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.27.4). [#4284](https://github.com/github/codeql-action/pull/4284)
- _Upcoming breaking change_: CodeQL version 2.22.3 and earlier were discontinued on 16 October 2026 alongside GitHub Enterprise Server 3.18, and will be unsupported by the next minor release of the CodeQL Action. Added a deprecation warning for customers using these versions of CodeQL. [#4266](https://github.com/github/codeql-action/pull/4266)
- Starting with macOS 27 and Xcode 27, traced analyses are unsupported. See [this blog post for more information](https://github.blog/changelog/2026-10-19-codeql-changes-for-macos-27-xcode-27-and-swift-6-4/). The CodeQL Action will now try to identify if a workflow is about to perform a traced analysis on version 27 or above of macOS or Xcode. If so, the CodeQL Action will emit a corresponding warning that will also be visible on the [Tool Status Page](https://docs.github.com/en/code-security/how-tos/find-and-fix-code-vulnerabilities/manage-your-configuration/use-the-tools-status-page-for-code-scanning). This check will be rolled out following this release of the CodeQL Action. [#4221](https://github.com/github/codeql-action/pull/4221)
- The undocumented TRAP cache cleanup feature that could be enabled using the `CODEQL_ACTION_CLEANUP_TRAP_CACHES` environment variable has been removed, as announced in the release notes for CodeQL Action 4.35.2. The `analyze` Action now warns if this environment variable is set. If you used TRAP cache cleanup to limit Actions cache usage, we recommend disabling TRAP caching by passing the `trap-caching: false` input to the `init` Action. [#4243](https://github.com/github/codeql-action/pull/4243)
## 4.39.1 - 21 Oct 2026
- Update default CodeQL bundle version to [2.27.3](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.27.3). [#4256](https://github.com/github/codeql-action/pull/4256)

View File

@@ -1,6 +1,6 @@
{
"bundleVersion": "codeql-bundle-v2.27.3",
"cliVersion": "2.27.3",
"priorBundleVersion": "codeql-bundle-v2.27.2",
"priorCliVersion": "2.27.2"
"bundleVersion": "codeql-bundle-v2.27.4",
"cliVersion": "2.27.4",
"priorBundleVersion": "codeql-bundle-v2.27.3",
"priorCliVersion": "2.27.3"
}

612
lib/entry-points.js generated

File diff suppressed because it is too large Load Diff

12
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "codeql",
"version": "4.39.2",
"version": "4.39.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "codeql",
"version": "4.39.2",
"version": "4.39.3",
"license": "MIT",
"workspaces": [
"pr-checks"
@@ -33,7 +33,7 @@
"js-yaml": "^5.5.0",
"jsonschema": "1.5.0",
"long": "^5.3.2",
"node-forge": "^1.4.0",
"node-forge": "^1.4.1",
"semver": "^7.8.5",
"undici": "^7.30.1",
"uuid": "^14.0.3"
@@ -8198,9 +8198,9 @@
}
},
"node_modules/node-forge": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz",
"integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.1.tgz",
"integrity": "sha512-w5/MfWaeMrpR/5ba6fZyAUTLSuE6aYz7H/3cYrqwZEmf5eQS3IaaLgJt4MBv7HglGFWmV22K8h4U18RJQmw/ow==",
"license": "(BSD-3-Clause OR GPL-2.0)",
"engines": {
"node": ">= 6.13.0"

View File

@@ -1,6 +1,6 @@
{
"name": "codeql",
"version": "4.39.2",
"version": "4.39.3",
"private": true,
"description": "CodeQL action",
"scripts": {
@@ -42,7 +42,7 @@
"js-yaml": "^5.5.0",
"jsonschema": "1.5.0",
"long": "^5.3.2",
"node-forge": "^1.4.0",
"node-forge": "^1.4.1",
"semver": "^7.8.5",
"undici": "^7.30.1",
"uuid": "^14.0.3"

View File

@@ -10,17 +10,13 @@ import { afterEach, beforeEach, describe, it } from "node:test";
import {
CLI_VERSION_ENV_VAR,
getChangenote,
getCLIVersion,
getPRNumber,
getPRUrl,
PR_URL_ENV_VAR,
updateChangelog,
} from "./bundle-changelog";
import {
EMPTY_CHANGELOG,
NO_CHANGES_STR,
UNRELEASED_PLACEHOLDER,
} from "./changelog";
import { isValidChangenoteContent } from "./changelog/validate";
let testDir: string;
@@ -85,58 +81,15 @@ describe("getPRNumber", async () => {
});
});
const testChangelog = `${EMPTY_CHANGELOG.trimEnd()}
## 4.23.7
- Other change
## 4.23.6
${NO_CHANGES_STR}`;
const expectedChangelog = `# CodeQL Action Changelog
## ${UNRELEASED_PLACEHOLDER}
- Update default CodeQL bundle version to
## 4.23.7
- Other change
## 4.23.6
${NO_CHANGES_STR}`;
describe("updateChangelog", async () => {
await it("removes `NO_CHANGES_STR` if present in [UNRELEASED] section", async () => {
const result = updateChangelog(EMPTY_CHANGELOG, "");
assert.ok(!result.includes(NO_CHANGES_STR));
});
await it("doesn't remove `NO_CHANGES_STR` if present in versioned section", async () => {
const result = updateChangelog(
EMPTY_CHANGELOG.replace(UNRELEASED_PLACEHOLDER, "1.2.3"),
"",
describe("getChangenote", async () => {
await it("returns a note linking to the bundle release and the PR", async () => {
assert.equal(
getChangenote("1.2.3", testPrUrl),
"- Update default CodeQL bundle version to [1.2.3](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v1.2.3). [#42](https://github.com/github/codeql-action/pulls/42)\n",
);
assert.ok(result.includes(NO_CHANGES_STR));
});
await it("throws if there are no sections", async () => {
assert.throws(() => {
updateChangelog(
"# CodeQL Action Changelog",
"- Update default CodeQL bundle version to",
);
});
});
await it("adds note at the end of the first section", async () => {
const result = updateChangelog(
testChangelog,
"- Update default CodeQL bundle version to",
);
assert.deepEqual(result, expectedChangelog);
await it("returns valid changenote content", async () => {
assert.ok(isValidChangenoteContent(getChangenote("1.2.3", testPrUrl)));
});
});

View File

@@ -1,17 +1,13 @@
#!/usr/bin/env npx tsx
/**
* Updates the changelog with a change note for an updated CodeQL CLI bundle.
* Creates a changenote file for an update to the default CodeQL CLI bundle.
*/
import * as fs from "node:fs";
import * as path from "node:path";
import {
parseChangelog,
renderChangelog,
UNRELEASED_PLACEHOLDER,
} from "./changelog";
import { CHANGELOG_FILE, CLI_BUNDLE_RELEASE_URL_PREFIX } from "./config";
import { CHANGENOTES_DIR, CLI_BUNDLE_RELEASE_URL_PREFIX } from "./config";
import { getErrorMessage } from "./util";
export const CLI_VERSION_ENV_VAR = "CLI_VERSION";
@@ -58,42 +54,19 @@ export function getPRNumber(prUrl: string) {
}
/**
* Updates `changelog` by adding `changelogNote` to the first section.
* Returns the contents of the changenote file for an update to the default
* CodeQL CLI bundle.
*
* @param contents The existing changelog contents.
* @param changelogNote The note to add to the first section.
* @param cliVersion The CodeQL CLI version of the new default bundle.
* @param prUrl The URL of the PR that updates the default bundle.
* @throws If the PR number can't be determined from `prUrl`.
*/
export function updateChangelog(contents: string, changelogNote: string) {
// If the "[UNRELEASED]" section starts with "no user facing changes", remove that line.
contents = contents.replace(
`## ${UNRELEASED_PLACEHOLDER}\n\nNo user facing changes.`,
`## ${UNRELEASED_PLACEHOLDER}\n`,
);
export function getChangenote(cliVersion: string, prUrl: string): string {
// The GitHub Release for the new bundle version.
const bundleReleaseUrl = `${CLI_BUNDLE_RELEASE_URL_PREFIX}${cliVersion}`;
const changelog = parseChangelog(contents);
if (changelog.sections.length === 0) {
throw new Error("The changelog contains no existing sections.");
}
// Add the changelog note to the bottom of the first section.
const firstSection = changelog.sections[0];
const lastLine = firstSection.bodyLines.pop();
if (lastLine !== undefined && lastLine.trim() !== "") {
// We expect the last line to be empty. If it isn't for some reason,
// add it back.
firstSection.bodyLines.push(lastLine);
}
firstSection.bodyLines.push(changelogNote);
// If the last line is empty as expected, then add it back in after the new note.
if (lastLine?.trim() === "") {
firstSection.bodyLines.push(lastLine);
}
return renderChangelog(changelog);
const prNumber = getPRNumber(prUrl);
return `- Update default CodeQL bundle version to [${cliVersion}](${bundleReleaseUrl}). [#${prNumber}](${prUrl})\n`;
}
function main() {
@@ -101,22 +74,19 @@ function main() {
const cliVersion = getCLIVersion();
const prUrl = getPRUrl();
// The GitHub Release for the new bundle version.
const bundleReleaseUrl = `${CLI_BUNDLE_RELEASE_URL_PREFIX}${cliVersion}`;
// Get the PR number from the PR URL.
const prNumber = getPRNumber(prUrl);
const changelogNote = `- Update default CodeQL bundle version to [${cliVersion}](${bundleReleaseUrl}). [#${prNumber}](${prUrl})`;
let changelog = fs.readFileSync(CHANGELOG_FILE, "utf-8");
changelog = updateChangelog(changelog, changelogNote);
fs.writeFileSync(CHANGELOG_FILE, changelog);
// Include the CLI version in the file name so that changenotes for
// different bundle updates don't conflict.
const changenotePath = path.join(
CHANGENOTES_DIR,
`update-bundle-${cliVersion}.md`,
);
fs.writeFileSync(changenotePath, getChangenote(cliVersion, prUrl));
return 0;
} catch (err) {
console.error(`Failed to bundle changelog: ${getErrorMessage(err)}`);
console.error(
`Failed to create changenote for bundle update: ${getErrorMessage(err)}`,
);
return -1;
}
}

View File

@@ -48,7 +48,7 @@ validationJobs:
installNode: true
steps:
- name: Download workflow artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
pattern: test-mode-sarif-*
path: sarif

View File

@@ -14,7 +14,7 @@
"@octokit/core": "^7.0.8",
"@octokit/plugin-paginate-rest": ">=16.0.0",
"@octokit/plugin-rest-endpoint-methods": "^18.0.0",
"glob": "^13.0.6",
"glob": "^14.0.3",
"lite-matter": "^0.1.2",
"mdast-util-from-markdown": "^2.1.0",
"semver": "^7.8.5",

View File

@@ -56,9 +56,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.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", () => {
@@ -78,12 +87,18 @@ jobs:
const result = scanGeneratedWorkflows(workflowDir);
assert.equal(result["actions/checkout"], "v4");
assert.equal(
result["ruby/setup-ruby"],
"44511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0",
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"]),
);
assert.equal(result["actions/setup-python"], "v6 # Latest Python");
});
await it("ignores local actions", () => {
@@ -103,9 +118,15 @@ jobs:
const result = scanGeneratedWorkflows(workflowDir);
assert.equal(result["actions/checkout"], "v4");
assert.equal("./.github/actions/local-action" in result, false);
assert.equal("./another-local-action" in result, false);
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);
});
});
@@ -128,8 +149,8 @@ const steps = [
fs.writeFileSync(syncTsPath, syncTsContent);
const actionVersions = {
"actions/setup-node": "v5",
"actions/setup-go": "v6",
"actions/setup-node": new Set(["v5"]),
"actions/setup-go": new Set(["v6"]),
};
const result = updateSyncTs(syncTsPath, actionVersions);
@@ -155,7 +176,7 @@ const steps = [
fs.writeFileSync(syncTsPath, syncTsContent);
const actionVersions = {
"actions/setup-node": "v5 # Latest version",
"actions/setup-node": new Set(["v5 # Latest version"]),
};
const result = updateSyncTs(syncTsPath, actionVersions);
@@ -182,7 +203,7 @@ const steps = [
fs.writeFileSync(syncTsPath, syncTsContent);
const actionVersions = {
"actions/setup-node": "v5",
"actions/setup-node": new Set(["v5"]),
};
const result = updateSyncTs(syncTsPath, actionVersions);
@@ -206,7 +227,9 @@ const steps = [
fs.writeFileSync(syncTsPath, syncTsContent);
const actionVersions = {
"actions/setup-node": "48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0",
"actions/setup-node": new Set([
"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0",
]),
};
const result = updateSyncTs(syncTsPath, actionVersions);
@@ -244,8 +267,9 @@ steps:
"actions/checkout": "v4",
"actions/setup-node": "v5 # Latest",
};
const byWorkflow = { ["test.yml"]: actionVersions };
const result = updateTemplateFiles(checksDir, actionVersions);
const result = updateTemplateFiles(checksDir, byWorkflow);
assert.equal(result.length, 1);
assert.ok(result.includes(templatePath));
@@ -269,8 +293,9 @@ steps:
const actionVersions = {
"ruby/setup-ruby": "55511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0",
};
const byWorkflow = { ["test.yml"]: actionVersions };
const result = updateTemplateFiles(checksDir, actionVersions);
const result = updateTemplateFiles(checksDir, byWorkflow);
assert.equal(result.length, 1);
const updatedContent = fs.readFileSync(templatePath, "utf8");

View File

@@ -27,16 +27,39 @@ const CHECKS_DIR = path.join(PR_CHECKS_DIR, "checks");
const WORKFLOW_DIR = path.join(REPO_ROOT, ".github", "workflows");
const SYNC_TS_PATH = path.join(PR_CHECKS_DIR, "sync.ts");
/** Type alias for action names. */
type ActionName = string;
/** Type alias for action versions. May include a comment. */
type ActionVersion = string;
/** Mappings of {@link ActionName}s to {@link ActionVersion}s. */
type ActionVersions = Record<ActionName, ActionVersion>;
/** Mappings of workflow names to {@link ActionVersions}. */
type ActionVersionsByWorkflow = Record<string, ActionVersions>;
/** The result of {@link scanGeneratedWorkflows}. */
type ScanWorkflowResult = {
/** A mapping of {@link ActionName}s to all encountered {@link ActionVersion}s. */
actionVersions: Record<ActionName, Set<ActionVersion>>;
/** A mapping of workflow names to {@link ActionVersions}. */
actionVersionsByWorkflow: ActionVersionsByWorkflow;
};
/**
* Scan generated workflow files to extract the latest action versions.
*
* @param workflowDir - Path to .github/workflows directory
* @returns Map from action names to their latest versions (including comments)
*
* @returns A {@link ScanWorkflowResult} object containing information about the extracted action versions.
*/
export function scanGeneratedWorkflows(
workflowDir: string,
): Record<string, string> {
const actionVersions: Record<string, string> = {};
): ScanWorkflowResult {
const actionVersionsByWorkflow: ActionVersionsByWorkflow = {};
const actionVersions: Record<ActionName, Set<ActionVersion>> = {};
const generatedFiles = fs
.readdirSync(workflowDir)
@@ -46,6 +69,10 @@ export function scanGeneratedWorkflows(
for (const filePath of generatedFiles) {
const content = fs.readFileSync(filePath, "utf8");
// Get the basename of the workflow and drop the leading `__` from it.
const workflowName = path.basename(filePath).slice(2);
actionVersionsByWorkflow[workflowName] = {};
// 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;
@@ -57,13 +84,26 @@ export function scanGeneratedWorkflows(
// Only track non-local actions (those with / but not starting with ./)
if (!actionName.startsWith("./")) {
// Assume that version numbers are consistent (this should be the case on a Dependabot update PR)
actionVersions[actionName] = versionWithComment;
// Record the version of the action for this workflow.
actionVersionsByWorkflow[workflowName][actionName] = versionWithComment;
// Record this version for the action so that we can write it to `sync.ts` if needed.
// Usually we'd expect there to be exactly one version per action across all workflows,
// but sometimes these can go out-of-sync if e.g. a workflow is added in a branch and a
// Dependabot PR is merged in the meantime that changes the version of an action that is
// used in the new workflow. Different versions of the same action across different
// workflows is not fundamentally a problem, but we may need to pick one for `sync.ts`
// so we warn there if we have multiple versions of an action.
if (actionVersions[actionName] === undefined) {
actionVersions[actionName] = new Set([versionWithComment]);
} else if (!actionVersions[actionName].has(versionWithComment)) {
actionVersions[actionName].add(versionWithComment);
}
}
}
}
return actionVersions;
return { actionVersions, actionVersionsByWorkflow };
}
/**
@@ -79,7 +119,7 @@ export function scanGeneratedWorkflows(
*/
export function updateSyncTs(
syncTsPath: string,
actionVersions: Record<string, string>,
actionVersions: Record<ActionName, Set<ActionVersion>>,
): boolean {
if (!fs.existsSync(syncTsPath)) {
throw new Error(`Could not find ${syncTsPath}`);
@@ -89,9 +129,18 @@ export function updateSyncTs(
const originalContent = content;
// Update hardcoded action versions
for (const [actionName, versionWithComment] of Object.entries(
for (const [actionName, versionsWithComments] of Object.entries(
actionVersions,
)) {
// We usually expect `versionsWithComments` to contain one version. If there are multiple,
// we arbitrarily pick one and warn about it.
const versionsWithCommentsArray = Array.from(versionsWithComments);
const versionWithComment = versionsWithCommentsArray[0];
if (versionsWithComments.size > 1) {
console.warn(
`Inconsistent versions for ${actionName}. Arbitrarily picking '${versionWithComment}'. All found: ${versionsWithCommentsArray.join(", ")}`,
);
}
// Split the scanned value into the ref (e.g. a commit SHA) and the optional
// trailing version comment (e.g. `v6.0.3`).
const ref = versionWithComment.includes("#")
@@ -138,12 +187,12 @@ export function updateSyncTs(
* Update action versions in template files in pr-checks/checks/
*
* @param checksDir - Path to pr-checks/checks directory
* @param actionVersions - Map of action names to versions (may include comments)
* @param actionVersionsByWorkflow - Map of workflows to maps of action names to versions (may include comments)
* @returns List of files that were modified
*/
export function updateTemplateFiles(
checksDir: string,
actionVersions: Record<string, string>,
actionVersionsByWorkflow: ActionVersionsByWorkflow,
): string[] {
const modifiedFiles: string[] = [];
@@ -156,6 +205,16 @@ export function updateTemplateFiles(
let content = fs.readFileSync(filePath, "utf8");
const originalContent = content;
const workflowName = path.basename(filePath);
const actionVersions = actionVersionsByWorkflow[workflowName];
if (actionVersions === undefined) {
console.info(
`Found no action versions for workflow template '${filePath}'.`,
);
continue;
}
// Update action versions
for (const [actionName, versionWithComment] of Object.entries(
actionVersions,
@@ -191,12 +250,15 @@ function main(): number {
const verbose = values.verbose ?? false;
console.info("Scanning generated workflows for latest action versions...");
const actionVersions = scanGeneratedWorkflows(WORKFLOW_DIR);
const { actionVersions, actionVersionsByWorkflow } =
scanGeneratedWorkflows(WORKFLOW_DIR);
if (verbose) {
console.info("Found action versions:");
for (const [action, version] of Object.entries(actionVersions)) {
console.info(` ${action}@${version}`);
for (const [action, versions] of Object.entries(actionVersions)) {
for (const version of versions) {
console.info(` ${action}@${version}`);
}
}
}
@@ -215,7 +277,10 @@ function main(): number {
}
// Update template files
const templateModified = updateTemplateFiles(CHECKS_DIR, actionVersions);
const templateModified = updateTemplateFiles(
CHECKS_DIR,
actionVersionsByWorkflow,
);
modifiedFiles.push(...templateModified);
if (modifiedFiles.length > 0) {

View File

@@ -11,8 +11,8 @@ import {
} from "./actions-util";
import { computeAutomationID } from "./api-client";
import { EnvVar } from "./environment";
import { RecordingLogger, setupTests } from "./testing-utils";
import { initializeEnvironment, withTmpDir } from "./util";
import { getTestEnv, RecordingLogger, setupTests } from "./testing-utils";
import { withTmpDir } from "./util";
setupTests(test);
@@ -227,8 +227,9 @@ test.serial(
);
test.serial("initializeEnvironment", (t) => {
initializeEnvironment("1.2.3");
t.deepEqual(process.env[EnvVar.VERSION], "1.2.3");
const env = getTestEnv();
env.initialise("1.2.3");
t.deepEqual(env.getOptional(EnvVar.VERSION), "1.2.3");
});
test.serial("fixCodeQualityCategory", (t) => {

View File

@@ -233,7 +233,7 @@ async function run(action: ActionState<["Base", "Logger", "Env", "Actions"]>) {
let databaseUploadResults: DatabaseUploadResult[] = [];
try {
util.initializeEnvironment(actionsUtil.getActionVersion());
action.env.initialise(actionsUtil.getActionVersion());
// Make inputs accessible in the `post` step, details at
// https://github.com/github/codeql-action/issues/2553

View File

@@ -7,7 +7,7 @@ import { ProxyAgent } from "undici";
import * as actionsUtil from "./actions-util";
import * as api from "./api-client";
import { DO_NOT_RETRY_STATUSES } from "./api-client";
import { ActionsEnvVars, RegistryProxyVars } from "./environment";
import { ActionsEnvVars, RegistryProxyVars, getEnv } from "./environment";
import { GITHUB_DOTCOM_API_URL, GITHUB_DOTCOM_URL } from "./github";
import {
callee,
@@ -21,7 +21,7 @@ import * as util from "./util";
setupTests(test);
test.beforeEach(() => {
util.initializeEnvironment(actionsUtil.getActionVersion());
getEnv().initialise(actionsUtil.getActionVersion());
});
test.serial("getApiClient", async (t) => {

View File

@@ -10,7 +10,7 @@ import { getGitHubVersion } from "./api-client";
import { determineAutobuildLanguages, runAutobuild } from "./autobuild";
import { getCodeQL } from "./codeql";
import { Config, getConfig } from "./config-utils";
import { EnvVar } from "./environment";
import { EnvVar, type Env } from "./environment";
import { Language } from "./languages";
import { Logger } from "./logging";
import {
@@ -26,7 +26,6 @@ import {
checkDiskUsage,
checkGitHubVersionInRange,
ConfigurationError,
initializeEnvironment,
wrapError,
} from "./util";
@@ -38,6 +37,7 @@ interface AutobuildStatusReport extends StatusReportBase {
}
async function sendCompletedStatusReport(
env: Env,
config: Config | undefined,
logger: Logger,
startedAt: Date,
@@ -45,7 +45,7 @@ async function sendCompletedStatusReport(
failingLanguage?: string,
cause?: Error,
) {
initializeEnvironment(getActionVersion());
env.initialise(getActionVersion());
const status = getActionsStatus(cause, failingLanguage);
const statusReportBase = await createStatusReportBase(
@@ -129,6 +129,7 @@ async function run({
`We were unable to automatically build your code. Please replace the call to the autobuild action with your custom build steps. ${error.message}`,
);
await sendCompletedStatusReport(
env,
config,
logger,
startedAt,
@@ -141,7 +142,13 @@ async function run({
env.export(EnvVar.AUTOBUILD_DID_COMPLETE_SUCCESSFULLY, "true");
await sendCompletedStatusReport(config, logger, startedAt, languages ?? []);
await sendCompletedStatusReport(
env,
config,
logger,
startedAt,
languages ?? [],
);
}
/** Defines the `autobuild` Action. */

View File

@@ -21,6 +21,7 @@ import {
import type { Config } from "./config-utils";
import * as defaults from "./defaults.json";
import { DocUrl } from "./doc-url";
import { getEnv } from "./environment";
import { BuiltInLanguage } from "./languages";
import { ToolsSource } from "./setup-codeql";
import {
@@ -38,14 +39,13 @@ import {
} from "./testing-utils";
import { ToolsDownloadStatusReport } from "./tools-download";
import * as util from "./util";
import { initializeEnvironment } from "./util";
setupTests(test);
let stubConfig: Config;
test.beforeEach(() => {
initializeEnvironment("1.2.3");
getEnv().initialise("1.2.3");
stubConfig = createTestConfig({
languages: [BuiltInLanguage.cpp],

View File

@@ -4,6 +4,7 @@ import * as path from "path";
import * as core from "@actions/core";
import * as toolrunner from "@actions/exec/lib/toolrunner";
import * as yaml from "js-yaml";
import * as semver from "semver";
import {
CommandInvocationError,
@@ -19,6 +20,7 @@ import type { ActionConfig } from "./config/action-config";
import { appendExtraQueryExclusions, type Config } from "./config-utils";
import { DocUrl } from "./doc-url";
import { EnvVar, getEnv, Env, ReadOnlyEnv } from "./environment";
import { getErrorMessage, isDiskConfigurationError } from "./errors";
import {
CodeQLDefaultVersionInfo,
Feature,
@@ -34,7 +36,9 @@ import { ToolsDownloadStatusReport } from "./tools-download";
import { ToolsFeature, isSupportedToolsFeature } from "./tools-features";
import { shouldEnableIndirectTracing } from "./tracer-config";
import * as util from "./util";
import { BuildMode, CleanupLevel, getErrorMessage } from "./util";
import { BuildMode, CleanupLevel } from "./util";
export { isDiskConfigurationError } from "./errors";
type Options = Array<string | number | boolean>;
@@ -70,6 +74,11 @@ export interface CodeQL {
* Get a string containing the semver version of the CodeQL executable.
*/
getVersion(): Promise<VersionInfo>;
/**
* Determines whether the CLI installation represented by this {@link CodeQL} instance
* has at least the {@link requiredVersion}.
*/
isVersionAtLeast(requiredVersion: string): Promise<boolean>;
/**
* Print version information about CodeQL.
*/
@@ -275,26 +284,6 @@ const GHES_MOST_RECENT_DEPRECATION_DATE = "2026-10-16";
/** The CLI verbosity level to use for extraction in debug mode. */
const EXTRACTION_DEBUG_MODE_VERBOSITY = "progress++";
/**
* Decides whether `e` is a disk-related error outside of our control
* that should be classified as a `ConfigurationError`.
*
* @param e The error to check.
* @returns True if the error should be treated as a `ConfigurationError` or false if not.
*/
export function isDiskConfigurationError(e: unknown): boolean {
if (!(e instanceof Error)) {
return false;
}
return (
// out of disk space
e.message.includes("ENOSPC") ||
// access denied
e.message.includes("EACCES")
);
}
/**
* Set up CodeQL CLI access.
*
@@ -403,9 +392,9 @@ export function setCodeQL(codeql: Partial<CodeQL>): void {
cachedCodeQL = createStubCodeQL(codeql);
}
function resolveFunction<T>(
function resolveFunction<K extends keyof CodeQL, T extends CodeQL[K]>(
partialCodeql: Partial<CodeQL>,
methodName: string,
methodName: K,
defaultImplementation?: T,
): T {
if (typeof partialCodeql[methodName] !== "function") {
@@ -415,7 +404,7 @@ function resolveFunction<T>(
const dummyMethod = () => {
throw new Error(`CodeQL ${methodName} method not correctly defined`);
};
return dummyMethod as T;
return dummyMethod as unknown as T;
}
return partialCodeql[methodName] as T;
}
@@ -432,6 +421,19 @@ export function createStubCodeQL(partialCodeql: Partial<CodeQL>): CodeQL {
getVersion: resolveFunction(partialCodeql, "getVersion", async () => ({
version: "1.0.0",
})),
isVersionAtLeast: resolveFunction(
partialCodeql,
"isVersionAtLeast",
async (requiredVersion) => {
return (
!!partialCodeql.getVersion &&
semver.gte(
(await partialCodeql.getVersion()).version,
requiredVersion,
)
);
},
),
printVersion: resolveFunction(partialCodeql, "printVersion"),
supportsFeature: resolveFunction(
partialCodeql,
@@ -525,6 +527,9 @@ export async function getCodeQLForCmd(
}
return result;
},
async isVersionAtLeast(requiredVersion: string) {
return semver.gte((await this.getVersion()).version, requiredVersion);
},
async printVersion() {
// Reuse the cached version information rather than invoking the CLI again.
core.info(JSON.stringify(await this.getVersion(), null, 2));
@@ -1062,7 +1067,7 @@ export async function getCodeQLForCmd(
// CodeQL object is created.
if (
checkVersion &&
!(await util.codeQlVersionAtLeast(codeql, CODEQL_MINIMUM_VERSION))
!(await codeql.isVersionAtLeast(CODEQL_MINIMUM_VERSION))
) {
throw new util.ConfigurationError(
`Expected a CodeQL CLI with version at least ${CODEQL_MINIMUM_VERSION} but got version ${
@@ -1072,7 +1077,7 @@ export async function getCodeQLForCmd(
} else if (
checkVersion &&
env.getOptional(EnvVar.SUPPRESS_DEPRECATED_SOON_WARNING) !== "true" &&
!(await util.codeQlVersionAtLeast(codeql, CODEQL_NEXT_MINIMUM_VERSION))
!(await codeql.isVersionAtLeast(CODEQL_NEXT_MINIMUM_VERSION))
) {
const result = await codeql.getVersion();
core.warning(

View File

@@ -82,7 +82,6 @@ import {
GitHubVersion,
ConfigurationError,
BuildMode,
codeQlVersionAtLeast,
cloneObject,
isDefined,
checkDiskUsage,
@@ -654,10 +653,7 @@ async function runnerHasSufficientMemory(
logger: Logger,
): Promise<boolean> {
if (
await codeQlVersionAtLeast(
codeql,
CODEQL_VERSION_REDUCED_OVERLAY_MEMORY_USAGE,
)
await codeql.isVersionAtLeast(CODEQL_VERSION_REDUCED_OVERLAY_MEMORY_USAGE)
) {
logger.debug(
`Skipping memory check for overlay analysis because CodeQL version is at least ${CODEQL_VERSION_REDUCED_OVERLAY_MEMORY_USAGE}.`,
@@ -901,7 +897,7 @@ async function validateOverlayDatabaseMode(
);
return new Failure(OverlayDisabledReason.IncompatibleBuildMode);
}
if (!(await codeQlVersionAtLeast(codeql, CODEQL_OVERLAY_MINIMUM_VERSION))) {
if (!(await codeql.isVersionAtLeast(CODEQL_OVERLAY_MINIMUM_VERSION))) {
logger.warning(
`Cannot build an ${overlayDatabaseMode} database because ` +
`the CodeQL CLI is older than ${CODEQL_OVERLAY_MINIMUM_VERSION}. ` +

View File

@@ -15,6 +15,7 @@ import {
cleanupAndUploadDatabases,
uploadBundledDatabase,
} from "./database-upload";
import { getEnv } from "./environment";
import { Feature } from "./feature-flags";
import * as gitUtils from "./git-utils";
import { BuiltInLanguage } from "./languages";
@@ -31,18 +32,12 @@ import {
setupActionsVars,
setupTests,
} from "./testing-utils";
import {
CleanupLevel,
GitHubVariant,
HTTPError,
initializeEnvironment,
withTmpDir,
} from "./util";
import { CleanupLevel, GitHubVariant, HTTPError, withTmpDir } from "./util";
setupTests(test);
test.beforeEach(() => {
initializeEnvironment("1.2.3");
getEnv().initialise("1.2.3");
});
const testRepoName: RepositoryNwo = { owner: "github", repo: "example" };

View File

@@ -1,6 +1,6 @@
{
"bundleVersion": "codeql-bundle-v2.27.3",
"cliVersion": "2.27.3",
"priorBundleVersion": "codeql-bundle-v2.27.2",
"priorCliVersion": "2.27.2"
"bundleVersion": "codeql-bundle-v2.27.4",
"cliVersion": "2.27.4",
"priorBundleVersion": "codeql-bundle-v2.27.3",
"priorCliVersion": "2.27.3"
}

View File

@@ -397,6 +397,18 @@ export class Env extends ReadOnlyEnv {
}
}
/**
* Set some initial environment variables that we can set even without
* knowing what version of CodeQL we're running.
*/
public initialise(version: string) {
this.export(EnvVar.FEATURE_MULTI_LANGUAGE, "false");
this.export(EnvVar.FEATURE_SANDWICH, "false");
this.export(EnvVar.FEATURE_SARIF_COMBINE, "true");
this.export(EnvVar.FEATURE_WILL_UPLOAD, "true");
this.export(EnvVar.VERSION, version);
}
/** Gets a value indicating whether `set` was called at least once. */
public hasChanged(): boolean {
return this.changed;

88
src/errors/index.ts Normal file
View File

@@ -0,0 +1,88 @@
import * as json from "../json";
/**
* If {@link error} is not already an instance of {@link Error}, then this function constructs
* an {@link Error} with a string representation of {@link error} as the message.
*/
export function wrapError(error: unknown): Error {
return error instanceof Error ? error : new Error(String(error));
}
/**
* Returns an appropriate message for the error.
*
* If the error is an {@link Error} instance, this returns the error message without
* an `Error: ` prefix.
*/
export function getErrorMessage(error: unknown): string {
return error instanceof Error ? error.message : String(error);
}
/**
* Approximates whether {@link error} is like a {@link NodeJS.ErrnoException}.
* We only typically care about the `code` property, so that's what we check for here.
*/
export function isErrnoException(
error: unknown,
): error is NodeJS.ErrnoException {
return (
typeof error === "object" &&
error !== null &&
"code" in error &&
typeof error.code === "string"
);
}
/**
* Decides whether `e` is a disk-related error outside of our control
* that should be classified as a `ConfigurationError`.
*
* @param e The error to check.
* @returns True if the error should be treated as a `ConfigurationError` or false if not.
*/
export function isDiskConfigurationError(e: unknown): boolean {
if (!(e instanceof Error)) {
return false;
}
return (
// out of disk space
e.message.includes("ENOSPC") ||
// access denied
e.message.includes("EACCES")
);
}
/**
* An Error class that indicates an error that occurred due to
* a misconfiguration of the action or the CodeQL CLI.
*/
export class ConfigurationError extends Error {}
/** Minimal representation of errors resulting from HTTP requests. */
export class HTTPError extends Error {
public status: number;
constructor(message: string, status: number) {
super(message);
this.status = status;
}
}
/**
* Tries to convert {@link arg} to a {@link HTTPError}, but returns `undefined` if
* {@link arg} is not like a {@link HTTPError}.
*/
export function asHTTPError(arg: any): HTTPError | undefined {
if (!json.isObject<any>(arg) || !json.isString(arg.message)) {
return undefined;
}
if (Number.isInteger(arg.status)) {
return new HTTPError(arg.message, arg.status as number);
}
// See https://github.com/actions/toolkit/blob/acb230b99a46ed33a3f04a758cd68b47b9a82908/packages/tool-cache/src/tool-cache.ts#L19
if (Number.isInteger(arg.httpStatusCode)) {
return new HTTPError(arg.message, arg.httpStatusCode as number);
}
return undefined;
}

View File

@@ -4,6 +4,7 @@ import * as path from "path";
import test from "ava";
import * as defaults from "./defaults.json";
import { getEnv } from "./environment";
import {
Feature,
featureConfig,
@@ -26,12 +27,12 @@ import {
setupTests,
stubFeatureFlagApiEndpoint,
} from "./testing-utils";
import { GitHubVariant, initializeEnvironment, withTmpDir } from "./util";
import { GitHubVariant, withTmpDir } from "./util";
setupTests(test);
test.beforeEach(() => {
initializeEnvironment("1.2.3");
getEnv().initialise("1.2.3");
});
test.serial(

View File

@@ -560,7 +560,7 @@ class OfflineFeatures implements FeatureEnablement {
// Never use this feature if the CLI version explicitly can't support it.
const minimumVersion = config.minimumVersion;
if (codeql && minimumVersion) {
if (!(await util.codeQlVersionAtLeast(codeql, minimumVersion))) {
if (!(await codeql.isVersionAtLeast(minimumVersion))) {
this.logger.debug(
`Feature ${feature} is disabled because the CodeQL CLI version is older than the minimum ` +
`version ${minimumVersion}.`,

View File

@@ -8,7 +8,7 @@ import {
LoggedMessage,
setupTests,
} from "../testing-utils";
import { GitHubVariant, initializeEnvironment, withTmpDir } from "../util";
import { getEnv, GitHubVariant, withTmpDir } from "../util";
import {
assertAllFeaturesHaveDefaultValues,
@@ -18,7 +18,7 @@ import {
setupTests(test);
test.beforeEach(() => {
initializeEnvironment("1.2.3");
getEnv().initialise("1.2.3");
});
test("OfflineFeatures makes no API requests", async (t) => {

View File

@@ -1,5 +1,19 @@
import { ActionsEnvVars, type ReadOnlyEnv } from "./environment";
import { ConfigurationError } from "./util";
import { ConfigurationError } from "./errors";
export enum GitHubVariant {
/** [GitHub.com](https://github.com) */
DOTCOM = "GitHub.com",
/** [GitHub Enterprise Server](https://docs.github.com/en/enterprise-server@latest/admin/overview/about-github-enterprise-server) */
GHES = "GitHub Enterprise Server",
/** [GitHub Enterprise Cloud with data residency](https://docs.github.com/en/enterprise-cloud@latest/admin/data-residency/about-github-enterprise-cloud-with-data-residency) */
GHEC_DR = "GitHub Enterprise Cloud with data residency",
}
export type GitHubVersion =
| { type: GitHubVariant.DOTCOM }
| { type: GitHubVariant.GHEC_DR }
| { type: GitHubVariant.GHES; version: string };
/** A string representing a GitHub instance address. */
export type InstanceURL = string;

View File

@@ -89,12 +89,10 @@ import {
checkDiskUsage,
checkForTimeout,
checkGitHubVersionInRange,
codeQlVersionAtLeast,
DEFAULT_DEBUG_ARTIFACT_NAME,
DEFAULT_DEBUG_DATABASE_NAME,
getCodeQLMemoryLimit,
getThreadsFlagValue,
initializeEnvironment,
ConfigurationError,
wrapError,
checkActionVersion,
@@ -227,7 +225,7 @@ async function run(
let toolsVersion: string;
try {
initializeEnvironment(getActionVersion());
actionState.env.initialise(getActionVersion());
// Make inputs accessible in the `post` step.
persistInputs();
@@ -685,7 +683,7 @@ async function run(
`${EnvVar.JAVA_EXTRACTOR_MINIMIZE_DEPENDENCY_JARS} is already set to '${process.env[EnvVar.JAVA_EXTRACTOR_MINIMIZE_DEPENDENCY_JARS]}', so the Action will not override it.`,
);
} else if (
(await codeQlVersionAtLeast(codeql, CODEQL_VERSION_JAR_MINIMIZATION)) &&
(await codeql.isVersionAtLeast(CODEQL_VERSION_JAR_MINIMIZATION)) &&
config.dependencyCachingEnabled &&
config.buildMode === BuildMode.None &&
config.languages.includes(BuiltInLanguage.java)

View File

@@ -38,7 +38,6 @@ import {
checkDiskUsage,
checkForTimeout,
checkGitHubVersionInRange,
initializeEnvironment,
ConfigurationError,
wrapError,
checkActionVersion,
@@ -108,7 +107,7 @@ async function run(
let toolsVersion: string;
try {
initializeEnvironment(getActionVersion());
actionState.env.initialise(getActionVersion());
const apiDetails = {
auth: getRequiredInput("token"),

View File

@@ -11,7 +11,13 @@ import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
import * as api from "./api-client";
import * as diagnostics from "./diagnostics";
import { ActionsEnvVars, Env, EnvVar, ReadOnlyEnv } from "./environment";
import {
ActionsEnvVars,
Env,
EnvVar,
getEnv,
ReadOnlyEnv,
} from "./environment";
import { Feature } from "./feature-flags";
import { BuiltInLanguage } from "./languages";
import { getCacheRestoreKeyPrefix } from "./overlay/caching";
@@ -37,18 +43,12 @@ import {
setupTests,
} from "./testing-utils";
import * as toolsDownload from "./tools-download";
import {
getErrorMessage,
GitHubVariant,
HTTPError,
initializeEnvironment,
withTmpDir,
} from "./util";
import { getErrorMessage, GitHubVariant, HTTPError, withTmpDir } from "./util";
setupTests(test);
test.beforeEach(() => {
initializeEnvironment("1.2.3");
getEnv().initialise("1.2.3");
});
function stubDownloadAndExtract() {

View File

@@ -6,7 +6,6 @@ import * as sinon from "sinon";
import * as actionsUtil from "../actions-util";
import { ActionsEnvVars } from "../environment";
import { callee, getTestEnv, setupTests } from "../testing-utils";
import { initializeEnvironment } from "../util";
import { getCodeQLActionRepository } from "./repository";
@@ -16,7 +15,7 @@ test.serial("getCodeQLActionRepository", async (t) => {
const initialEnv = getTestEnv({
[ActionsEnvVars.RUNNER_TEMP]: path.dirname(__dirname),
});
initializeEnvironment("1.2.3", initialEnv);
initialEnv.initialise("1.2.3");
const target = callee(getCodeQLActionRepository)
.withArgs()

View File

@@ -2,13 +2,12 @@ import { ActionState } from "../action-common";
import { isRunningLocalAction } from "../actions-util";
import * as api from "../api-client";
import { ActionsEnvVars } from "../environment";
import { GITHUB_DOTCOM_URL } from "../github";
import { GITHUB_DOTCOM_URL, type InstanceURL } from "../github";
import {
isRepositoryNwoStr,
parseRepositoryNwo,
type RepositoryNwoStr,
} from "../repository";
import { type InstanceURL } from "../util";
/** The NWO of the standard CodeQL Action repository. */
export const CODEQL_DEFAULT_ACTION_REPOSITORY = "github/codeql-action";

View File

@@ -10,6 +10,7 @@ import { https } from "follow-redirects";
import nock from "nock";
import * as sinon from "sinon";
import * as errors from "./errors";
import * as tar from "./tar";
import { RecordingLogger, setupTests } from "./testing-utils";
import { downloadAndExtract } from "./tools-download";
@@ -163,7 +164,7 @@ test.serial(
test.serial(
"downloadAndExtract handles an unknown status as a non-HTTP error",
async (t) => {
const asHTTPError = sinon.spy(util, "asHTTPError");
const asHTTPError = sinon.spy(errors, "asHTTPError");
await util.withTmpDir(async (tmpDir) => {
sinon.stub(process, "platform").value("linux");
const archivePath = path.join(tmpDir, "codeql-bundle.tar.zst");

View File

@@ -10,17 +10,18 @@ import * as analyses from "./analyses";
import { AnalysisKind, CodeQuality, CodeScanning } from "./analyses";
import * as api from "./api-client";
import * as diffUtils from "./diff-informed-analysis-utils";
import { getEnv } from "./environment";
import { Logger } from "./logging";
import * as sarif from "./sarif";
import { RecordingLogger, setupTests } from "./testing-utils";
import * as uploadLib from "./upload-lib";
import { UploadPayload } from "./upload-lib/types";
import { GitHubVariant, initializeEnvironment, withTmpDir } from "./util";
import { GitHubVariant, withTmpDir } from "./util";
setupTests(test);
test.beforeEach(() => {
initializeEnvironment("1.2.3");
getEnv().initialise("1.2.3");
});
test.serial("validateSarifFileSchema - valid", (t) => {

View File

@@ -24,7 +24,6 @@ import {
ConfigurationError,
checkActionVersion,
checkDiskUsage,
initializeEnvironment,
shouldSkipSarifUpload,
wrapError,
} from "./util";
@@ -55,11 +54,13 @@ async function sendSuccessStatusReport(
}
}
async function run({ startedAt, logger }: ActionState<["Base", "Logger"]>) {
async function run(action: ActionState<["Base", "Env", "Logger"]>) {
// To capture errors appropriately, keep as much code within the try-catch as
// possible, and only use safe functions outside.
const logger = action.logger;
try {
initializeEnvironment(getActionVersion());
action.env.initialise(getActionVersion());
const gitHubVersion = await getGitHubVersion();
checkActionVersion(getActionVersion(), gitHubVersion);
@@ -79,7 +80,7 @@ async function run({ startedAt, logger }: ActionState<["Base", "Logger"]>) {
const startingStatusReportBase = await createStatusReportBase(
ActionName.UploadSarif,
"starting",
startedAt,
action.startedAt,
undefined,
await checkDiskUsage(logger),
logger,
@@ -137,7 +138,7 @@ async function run({ startedAt, logger }: ActionState<["Base", "Logger"]>) {
// so we can't wait for that here.
}
await sendSuccessStatusReport(
startedAt,
action.startedAt,
codeScanningResult?.statusReport || {},
logger,
);
@@ -153,7 +154,7 @@ async function run({ startedAt, logger }: ActionState<["Base", "Logger"]>) {
const errorStatusReportBase = await createStatusReportBase(
ActionName.UploadSarif,
getActionsStatus(error),
startedAt,
action.startedAt,
undefined,
await checkDiskUsage(logger),
logger,

View File

@@ -20,20 +20,22 @@ import {
getRequiredEnvParam,
exportEnvVar,
getEnv,
type Env,
type ReadOnlyEnv,
} from "./environment";
import * as json from "./json";
import { Language } from "./languages";
import { Logger } from "./logging";
import { ConfigurationError, getErrorMessage, wrapError } from "./errors";
import { GitHubVariant, type GitHubVersion } from "./github";
import type { Language } from "./languages";
import type { Logger } from "./logging";
// Re-export for backwards compatibility to avoid updating a lot of imports elsewhere.
export * from "./errors";
export {
getRequiredEnvParam,
getOptionalEnvVar,
getEnv,
isInTestMode,
} from "./environment";
export { GitHubVariant, type GitHubVersion } from "./github";
/**
* The name of the file containing the base database OIDs, as stored in the
@@ -47,9 +49,6 @@ const BASE_DATABASE_OIDS_FILE_NAME = "base-database-oids.json";
*/
const BROKEN_VERSIONS = ["0.0.0-20211207"];
/** A string representing a GitHub instance. */
export type InstanceURL = string;
/**
* Default name of the debugging artifact.
*/
@@ -470,20 +469,6 @@ const CODEQL_ACTION_WARNED_ABOUT_VERSION_ENV_VAR =
let hasBeenWarnedAboutVersion = false;
export enum GitHubVariant {
/** [GitHub.com](https://github.com) */
DOTCOM = "GitHub.com",
/** [GitHub Enterprise Server](https://docs.github.com/en/enterprise-server@latest/admin/overview/about-github-enterprise-server) */
GHES = "GitHub Enterprise Server",
/** [GitHub Enterprise Cloud with data residency](https://docs.github.com/en/enterprise-cloud@latest/admin/data-residency/about-github-enterprise-cloud-with-data-residency) */
GHEC_DR = "GitHub Enterprise Cloud with data residency",
}
export type GitHubVersion =
| { type: GitHubVariant.DOTCOM }
| { type: GitHubVariant.GHEC_DR }
| { type: GitHubVariant.GHES; version: string };
export function checkGitHubVersionInRange(
version: GitHubVersion,
logger: Logger,
@@ -552,54 +537,6 @@ export function assertNever(value: never): never {
throw new ExhaustivityCheckingError(value);
}
/**
* Set some initial environment variables that we can set even without
* knowing what version of CodeQL we're running.
*/
export function initializeEnvironment(version: string, env: Env = getEnv()) {
env.export(EnvVar.FEATURE_MULTI_LANGUAGE, "false");
env.export(EnvVar.FEATURE_SANDWICH, "false");
env.export(EnvVar.FEATURE_SARIF_COMBINE, "true");
env.export(EnvVar.FEATURE_WILL_UPLOAD, "true");
env.export(EnvVar.VERSION, version);
}
export class HTTPError extends Error {
public status: number;
constructor(message: string, status: number) {
super(message);
this.status = status;
}
}
/**
* An Error class that indicates an error that occurred due to
* a misconfiguration of the action or the CodeQL CLI.
*/
export class ConfigurationError extends Error {}
export function asHTTPError(arg: any): HTTPError | undefined {
if (!json.isObject<any>(arg) || !json.isString(arg.message)) {
return undefined;
}
if (Number.isInteger(arg.status)) {
return new HTTPError(arg.message, arg.status as number);
}
// See https://github.com/actions/toolkit/blob/acb230b99a46ed33a3f04a758cd68b47b9a82908/packages/tool-cache/src/tool-cache.ts#L19
if (Number.isInteger(arg.httpStatusCode)) {
return new HTTPError(arg.message, arg.httpStatusCode as number);
}
return undefined;
}
export async function codeQlVersionAtLeast(
codeql: CodeQL,
requiredVersion: string,
): Promise<boolean> {
return semver.gte((await codeql.getVersion()).version, requiredVersion);
}
export function getBaseDatabaseOidsFilePath(
config: ActionConfig<"dbLocation">,
): string {
@@ -886,35 +823,6 @@ export function parseMatrixInput(
}
}
export function wrapError(error: unknown): Error {
return error instanceof Error ? error : new Error(String(error));
}
/**
* Returns an appropriate message for the error.
*
* If the error is an `Error` instance, this returns the error message without
* an `Error: ` prefix.
*/
export function getErrorMessage(error: unknown): string {
return error instanceof Error ? error.message : String(error);
}
/**
* Approximates whether {@link error} is like a {@link NodeJS.ErrnoException}.
* We only typically care about the `code` property, so that's what we check for here.
*/
export function isErrnoException(
error: unknown,
): error is NodeJS.ErrnoException {
return (
typeof error === "object" &&
error !== null &&
"code" in error &&
typeof error.code === "string"
);
}
export function prettyPrintPack(pack: Pack) {
return `${pack.name}${pack.version ? `@${pack.version}` : ""}${
pack.path ? `:${pack.path}` : ""

View File

@@ -1 +0,0 @@
- _Upcoming breaking change_: CodeQL version 2.22.3 and earlier were discontinued on 16 October 2026 alongside GitHub Enterprise Server 3.18, and will be unsupported by the next minor release of the CodeQL Action. Added a deprecation warning for customers using these versions of CodeQL. [#4266](https://github.com/github/codeql-action/pull/4266)

View File

@@ -1 +0,0 @@
- Starting with macOS 27 and Xcode 27, traced analyses are unsupported. See [this blog post for more information](https://github.blog/changelog/2026-10-19-codeql-changes-for-macos-27-xcode-27-and-swift-6-4/). The CodeQL Action will now try to identify if a workflow is about to perform a traced analysis on version 27 or above of macOS or Xcode. If so, the CodeQL Action will emit a corresponding warning that will also be visible on the [Tool Status Page](https://docs.github.com/en/code-security/how-tos/find-and-fix-code-vulnerabilities/manage-your-configuration/use-the-tools-status-page-for-code-scanning). This check will be rolled out following this release of the CodeQL Action. [#4221](https://github.com/github/codeql-action/pull/4221)

View File

@@ -1 +0,0 @@
- The undocumented TRAP cache cleanup feature that could be enabled using the `CODEQL_ACTION_CLEANUP_TRAP_CACHES` environment variable has been removed, as announced in the release notes for CodeQL Action 4.35.2. The `analyze` Action now warns if this environment variable is set. If you used TRAP cache cleanup to limit Actions cache usage, we recommend disabling TRAP caching by passing the `trap-caching: false` input to the `init` Action. [#4243](https://github.com/github/codeql-action/pull/4243)