diff --git a/.github/workflows/__multi-language-autodetect.yml b/.github/workflows/__multi-language-autodetect.yml index 33dbd2f69..ff54c07eb 100644 --- a/.github/workflows/__multi-language-autodetect.yml +++ b/.github/workflows/__multi-language-autodetect.yml @@ -61,39 +61,39 @@ jobs: include: - os: ubuntu-latest version: stable-v2.17.6 - - os: macos-latest + - os: macos-latest-xlarge version: stable-v2.17.6 - os: ubuntu-latest version: stable-v2.18.4 - - os: macos-latest + - os: macos-latest-xlarge version: stable-v2.18.4 - os: ubuntu-latest version: stable-v2.19.4 - - os: macos-latest + - os: macos-latest-xlarge version: stable-v2.19.4 - os: ubuntu-latest version: stable-v2.20.7 - - os: macos-latest + - os: macos-latest-xlarge version: stable-v2.20.7 - os: ubuntu-latest version: stable-v2.21.4 - - os: macos-latest + - os: macos-latest-xlarge version: stable-v2.21.4 - os: ubuntu-latest version: stable-v2.22.4 - - os: macos-latest + - os: macos-latest-xlarge version: stable-v2.22.4 - os: ubuntu-latest version: default - - os: macos-latest + - os: macos-latest-xlarge version: default - os: ubuntu-latest version: linked - - os: macos-latest + - os: macos-latest-xlarge version: linked - os: ubuntu-latest version: nightly-latest - - os: macos-latest + - os: macos-latest-xlarge version: nightly-latest name: Multi-language repository if: github.triggering_actor != 'dependabot[bot]' diff --git a/.github/workflows/__swift-autobuild.yml b/.github/workflows/__swift-autobuild.yml index 473c13644..cd26309f4 100644 --- a/.github/workflows/__swift-autobuild.yml +++ b/.github/workflows/__swift-autobuild.yml @@ -39,7 +39,7 @@ jobs: fail-fast: false matrix: include: - - os: macos-latest + - os: macos-latest-xlarge version: nightly-latest name: Swift analysis using autobuild if: github.triggering_actor != 'dependabot[bot]' diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 0b32bc20e..9f14b05bf 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -77,7 +77,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-22.04,ubuntu-24.04,windows-2022,windows-2025,macos-14,macos-15] + os: [ubuntu-22.04,ubuntu-24.04,windows-2022,windows-2025,macos-14-xlarge,macos-15-xlarge] tools: ${{ fromJson(needs.check-codeql-versions.outputs.versions) }} runs-on: ${{ matrix.os }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f61251a2..17d47706f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ See the [releases page](https://github.com/github/codeql-action/releases) for th ## [UNRELEASED] - Add support for SHA-256 Git object IDs. [#3893](https://github.com/github/codeql-action/pull/3893) + +## 4.35.5 - 15 May 2026 + +- We have improved how the JavaScript bundles for the CodeQL Action are generated to avoid duplication across bundles and reduce the size of the repository by around 70%. This should have no effect on the runtime behaviour of the CodeQL Action. [#3899](https://github.com/github/codeql-action/pull/3899) - For performance and accuracy reasons, [improved incremental analysis](https://github.com/github/roadmap/issues/1158) will now only be enabled on a pull request when diff-informed analysis is also enabled for that run. If diff-informed analysis is unavailable (for example, because the PR diff ranges could not be computed), the action will fall back to a full analysis. [#3791](https://github.com/github/codeql-action/pull/3791) - If multiple inputs are provided for the GitHub-internal `analysis-kinds` input, only `code-scanning` will be enabled. The `analysis-kinds` input is experimental, for GitHub-internal use only, and may change without notice at any time. [#3892](https://github.com/github/codeql-action/pull/3892) - Added an experimental change which, when running a Code Scanning analysis for a PR with [improved incremental analysis](https://github.com/github/roadmap/issues/1158) enabled, prefers CodeQL CLI versions that have a cached overlay-base database for the configured languages. This speeds up analysis for a repository when there is not yet a cached overlay-base database for the latest CLI version. We expect to roll this change out to everyone in May. [#3880](https://github.com/github/codeql-action/pull/3880) diff --git a/build.mjs b/build.mjs index 01a1c4d51..d4df66b2b 100644 --- a/build.mjs +++ b/build.mjs @@ -66,8 +66,8 @@ const onEndPlugin = { const SHARED_ENTRYPOINT = "entry-points"; /** - * This plugin finds all source files that contain action entry points. - * It then generates the virtual `entry-points` module which imports all identifies files, + * This plugin finds all source files that contain Action entry points. + * It then generates the virtual `entry-points` module which imports all identified files, * and re-exports their `runWrapper` functions with suitable aliases. * A tiny stub file is emitted for each Action entrypoint. Each stub imports the shared bundle * and calls the respective entry point. @@ -83,7 +83,7 @@ const entryPointsPlugin = { const toPascal = (s) => s.replace(/(^|-)([a-z0-9])/gi, (_, __, c) => c.toUpperCase()); - // Find the source files containing action entry points. + // Find the source files containing Action entry points. build.onStart(() => { const actionFiles = globSync("src/*-action{,-post}.ts"); for (const actionFile of actionFiles) { @@ -112,7 +112,7 @@ const entryPointsPlugin = { return { path: SHARED_ENTRYPOINT, namespace }; }); - // Generate the virtual `entry-points` file based on the actions we discovered. + // Generate the virtual `entry-points` file based on the Actions we discovered. // Restrict using the namespace. The path filter does not need to discriminate any further. build.onLoad({ filter: /.*/, namespace }, async () => { const wrapperTemplatePath = "entry-wrapper.js.tpl"; @@ -127,7 +127,7 @@ const entryPointsPlugin = { const imports = actionsSorted .map( (action) => - `import * as ${action.pascalCaseName} from "./src/${basename(action.path)}"`, + `import * as ${action.pascalCaseName} from "./src/${basename(action.path)}";`, ) .join("\n"); const wrappers = actionsSorted @@ -143,7 +143,7 @@ const entryPointsPlugin = { }; }); - // Emit entry point stubs for each action using the entry template. + // Emit entry point stubs for each Action using the entry template. build.onEnd(async (result) => { // Read the entry point template. const templatePath = "action-entry.js.tpl"; @@ -152,7 +152,7 @@ const entryPointsPlugin = { const makeHeader = (sourceFile) => `// Automatically generated from '${templatePath}' for 'src/${basename(sourceFile)}'.\n\n`; - // Write entry point stubs for each action. + // Write entry point stubs for each Action. for (const action of actions) { await writeFile( join( diff --git a/lib/entry-points.js b/lib/entry-points.js index 6deaa0714..041ec4f63 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -148304,7 +148304,7 @@ function getDiffRangesJsonFilePath() { return path2.join(getTemporaryDirectory(), PR_DIFF_RANGE_JSON_FILENAME); } function getActionVersion() { - return "4.35.5"; + return "4.35.6"; } function getWorkflowEventName() { return getRequiredEnvParam("GITHUB_EVENT_NAME"); diff --git a/lib/upload-lib.js b/lib/upload-lib.js index fe48b6dc0..1e3903071 100644 --- a/lib/upload-lib.js +++ b/lib/upload-lib.js @@ -88509,7 +88509,7 @@ function getDiffRangesJsonFilePath() { return path2.join(getTemporaryDirectory(), PR_DIFF_RANGE_JSON_FILENAME); } function getActionVersion() { - return "4.35.5"; + return "4.35.6"; } function getWorkflowEventName() { return getRequiredEnvParam("GITHUB_EVENT_NAME"); diff --git a/package-lock.json b/package-lock.json index 48052b773..130ed3da1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "codeql", - "version": "4.35.5", + "version": "4.35.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codeql", - "version": "4.35.5", + "version": "4.35.6", "license": "MIT", "workspaces": [ "pr-checks" diff --git a/package.json b/package.json index d46e50792..db78e980e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codeql", - "version": "4.35.5", + "version": "4.35.6", "private": true, "description": "CodeQL action", "scripts": { @@ -12,7 +12,8 @@ "ava": "npm run transpile && ava --verbose", "test": "npm run ava -- src/", "test-debug": "npm run test -- --timeout=20m", - "transpile": "tsc --build --verbose tsconfig.json" + "transpile": "tsc --build --verbose tsconfig.json", + "update-pr-checks": "./pr-checks/sync.sh" }, "license": "MIT", "workspaces": [ diff --git a/pr-checks/checks/multi-language-autodetect.yml b/pr-checks/checks/multi-language-autodetect.yml index e005a9239..c52dcf940 100644 --- a/pr-checks/checks/multi-language-autodetect.yml +++ b/pr-checks/checks/multi-language-autodetect.yml @@ -2,7 +2,8 @@ name: "Multi-language repository" description: "An end-to-end integration test of a multi-language repository using automatic language detection" operatingSystems: - ubuntu - - macos + - os: macos + runner-image: macos-latest-xlarge env: CODEQL_ACTION_RESOLVE_SUPPORTED_LANGUAGES_USING_CLI: true installGo: true diff --git a/pr-checks/checks/swift-autobuild.yml b/pr-checks/checks/swift-autobuild.yml index e9949c12e..393857cd2 100644 --- a/pr-checks/checks/swift-autobuild.yml +++ b/pr-checks/checks/swift-autobuild.yml @@ -3,7 +3,8 @@ description: "Tests creation of a Swift database using autobuild" versions: - nightly-latest operatingSystems: - - macos + - os: macos + runner-image: macos-latest-xlarge steps: - uses: ./../action/init id: init diff --git a/pr-checks/sync.ts b/pr-checks/sync.ts index e46fca248..c810e7cbf 100755 --- a/pr-checks/sync.ts +++ b/pr-checks/sync.ts @@ -28,6 +28,24 @@ interface WorkflowInput { /** A partial mapping from known input names to input definitions. */ type WorkflowInputs = Partial>; +/** An operating system identifier. */ +type OperatingSystemIdentifier = "ubuntu" | "macos" | "windows"; + +/** + * Represents an operating system matrix entry for a generated PR check workflow. + * + * Either a string containing the OS identifier or an object containing the OS identifier and an + * optional runner image label. + */ +type OperatingSystem = + | OperatingSystemIdentifier + | { + /** OS identifier. */ + os: OperatingSystemIdentifier; + /** Optional runner image label. */ + "runner-image"?: string; + }; + /** * Represents PR check specifications. */ @@ -36,8 +54,8 @@ interface Specification extends JobSpecification { inputs?: Record; /** CodeQL bundle versions to test against. Defaults to `DEFAULT_TEST_VERSIONS`. */ versions?: string[]; - /** Operating system prefixes used to select runner images (e.g. `["ubuntu", "macos"]`). */ - operatingSystems?: string[]; + /** Operating system prefixes, either as strings or with explicit runner image labels. */ + operatingSystems?: OperatingSystem[]; /** Per-OS version overrides. If specified for an OS, only those versions are tested on that OS. */ osCodeQlVersions?: Record; /** Whether to use the all-platform CodeQL bundle. */ @@ -311,10 +329,19 @@ function generateJobMatrix( ); } - const runnerImages = ["ubuntu-latest", "macos-latest", "windows-latest"]; + const defaultRunnerImages = [ + "ubuntu-latest", + "macos-latest", + "windows-latest", + ]; const operatingSystems = checkSpecification.operatingSystems ?? ["ubuntu"]; - for (const operatingSystem of operatingSystems) { + for (const operatingSystemConfig of operatingSystems) { + const operatingSystem = + typeof operatingSystemConfig === "string" + ? operatingSystemConfig + : operatingSystemConfig.os; + // If osCodeQlVersions is set for this OS, only include the specified CodeQL versions. const allowedVersions = checkSpecification.osCodeQlVersions?.[operatingSystem]; @@ -322,9 +349,13 @@ function generateJobMatrix( continue; } - const runnerImagesForOs = runnerImages.filter((image) => - image.startsWith(operatingSystem), - ); + const runnerImagesForOs = + typeof operatingSystemConfig === "string" || + operatingSystemConfig["runner-image"] === undefined + ? defaultRunnerImages.filter((image) => + image.startsWith(operatingSystem), + ) + : [operatingSystemConfig["runner-image"]]; for (const runnerImage of runnerImagesForOs) { matrix.push({ diff --git a/src/analyze-action-env.test.ts b/src/analyze-action-env.test.ts deleted file mode 100644 index 93992c4a8..000000000 --- a/src/analyze-action-env.test.ts +++ /dev/null @@ -1,85 +0,0 @@ -import test from "ava"; -import * as sinon from "sinon"; - -import * as actionsUtil from "./actions-util"; -import * as analyze from "./analyze"; -import { runWrapper } from "./analyze-action"; -import * as api from "./api-client"; -import * as configUtils from "./config-utils"; -import * as gitUtils from "./git-utils"; -import * as statusReport from "./status-report"; -import { - setupTests, - setupActionsVars, - mockFeatureFlagApiEndpoint, -} from "./testing-utils"; -import * as util from "./util"; - -setupTests(test); - -// This test needs to be in its own file so that ava would run it in its own -// nodejs process. The code being tested is in analyze-action.ts, which runs -// immediately on load. So the file needs to be loaded during part of the test, -// and that can happen only once per nodejs process. If multiple such tests are -// in the same test file, ava would run them in the same nodejs process, and all -// but the first test would fail. - -test("analyze action with RAM & threads from environment variables", async (t) => { - // This test frequently times out on Windows with the default timeout, so we bump - // it a bit to 20s. - t.timeout(1000 * 20); - await util.withTmpDir(async (tmpDir) => { - setupActionsVars(tmpDir, tmpDir); - sinon - .stub(statusReport, "createStatusReportBase") - .resolves({} as statusReport.StatusReportBase); - sinon.stub(statusReport, "sendStatusReport").resolves(); - sinon.stub(gitUtils, "isAnalyzingDefaultBranch").resolves(true); - - const gitHubVersion: util.GitHubVersion = { - type: util.GitHubVariant.DOTCOM, - }; - sinon.stub(configUtils, "getConfig").resolves({ - gitHubVersion, - augmentationProperties: {}, - languages: [], - packs: [], - trapCaches: {}, - } as unknown as configUtils.Config); - const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput"); - requiredInputStub.withArgs("token").returns("fake-token"); - requiredInputStub.withArgs("upload-database").returns("false"); - requiredInputStub.withArgs("output").returns("out"); - const optionalInputStub = sinon.stub(actionsUtil, "getOptionalInput"); - optionalInputStub.withArgs("expect-error").returns("false"); - sinon.stub(api, "getGitHubVersion").resolves(gitHubVersion); - mockFeatureFlagApiEndpoint(200, {}); - - // When there are no action inputs for RAM and threads, the action uses - // environment variables (passed down from the init action) to set RAM and - // threads usage. - process.env["CODEQL_THREADS"] = "-1"; - process.env["CODEQL_RAM"] = "4992"; - - const runFinalizeStub = sinon.stub(analyze, "runFinalize"); - const runQueriesStub = sinon.stub(analyze, "runQueries"); - - await runWrapper(); - - t.assert( - runFinalizeStub.calledOnceWith( - sinon.match.any, - sinon.match.any, - "--threads=-1", - "--ram=4992", - ), - ); - t.assert( - runQueriesStub.calledOnceWith( - sinon.match.any, - "--ram=4992", - "--threads=-1", - ), - ); - }); -}); diff --git a/src/analyze-action-input.test.ts b/src/analyze-action-input.test.ts deleted file mode 100644 index b0c2f90c0..000000000 --- a/src/analyze-action-input.test.ts +++ /dev/null @@ -1,83 +0,0 @@ -import test from "ava"; -import * as sinon from "sinon"; - -import * as actionsUtil from "./actions-util"; -import * as analyze from "./analyze"; -import { runWrapper } from "./analyze-action"; -import * as api from "./api-client"; -import * as configUtils from "./config-utils"; -import * as gitUtils from "./git-utils"; -import * as statusReport from "./status-report"; -import { - setupTests, - setupActionsVars, - mockFeatureFlagApiEndpoint, -} from "./testing-utils"; -import * as util from "./util"; - -setupTests(test); - -// This test needs to be in its own file so that ava would run it in its own -// nodejs process. The code being tested is in analyze-action.ts, which runs -// immediately on load. So the file needs to be loaded during part of the test, -// and that can happen only once per nodejs process. If multiple such tests are -// in the same test file, ava would run them in the same nodejs process, and all -// but the first test would fail. - -test("analyze action with RAM & threads from action inputs", async (t) => { - t.timeout(1000 * 20); - await util.withTmpDir(async (tmpDir) => { - setupActionsVars(tmpDir, tmpDir); - sinon - .stub(statusReport, "createStatusReportBase") - .resolves({} as statusReport.StatusReportBase); - sinon.stub(statusReport, "sendStatusReport").resolves(); - const gitHubVersion: util.GitHubVersion = { - type: util.GitHubVariant.DOTCOM, - }; - sinon.stub(configUtils, "getConfig").resolves({ - gitHubVersion, - augmentationProperties: {}, - languages: [], - packs: [], - trapCaches: {}, - } as unknown as configUtils.Config); - const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput"); - requiredInputStub.withArgs("token").returns("fake-token"); - requiredInputStub.withArgs("upload-database").returns("false"); - requiredInputStub.withArgs("output").returns("out"); - const optionalInputStub = sinon.stub(actionsUtil, "getOptionalInput"); - optionalInputStub.withArgs("expect-error").returns("false"); - sinon.stub(api, "getGitHubVersion").resolves(gitHubVersion); - sinon.stub(gitUtils, "isAnalyzingDefaultBranch").resolves(true); - mockFeatureFlagApiEndpoint(200, {}); - - process.env["CODEQL_THREADS"] = "1"; - process.env["CODEQL_RAM"] = "4992"; - - // Action inputs have precedence over environment variables. - optionalInputStub.withArgs("threads").returns("-1"); - optionalInputStub.withArgs("ram").returns("3012"); - - const runFinalizeStub = sinon.stub(analyze, "runFinalize"); - const runQueriesStub = sinon.stub(analyze, "runQueries"); - - await runWrapper(); - - t.assert( - runFinalizeStub.calledOnceWith( - sinon.match.any, - sinon.match.any, - "--threads=-1", - "--ram=3012", - ), - ); - t.assert( - runQueriesStub.calledOnceWith( - sinon.match.any, - "--ram=3012", - "--threads=-1", - ), - ); - }); -}); diff --git a/src/analyze-action.test.ts b/src/analyze-action.test.ts new file mode 100644 index 000000000..923908a64 --- /dev/null +++ b/src/analyze-action.test.ts @@ -0,0 +1,142 @@ +import test from "ava"; +import * as sinon from "sinon"; + +import * as actionsUtil from "./actions-util"; +import * as analyze from "./analyze"; +import { runWrapper } from "./analyze-action"; +import * as api from "./api-client"; +import * as configUtils from "./config-utils"; +import * as gitUtils from "./git-utils"; +import * as statusReport from "./status-report"; +import { + setupTests, + setupActionsVars, + mockFeatureFlagApiEndpoint, +} from "./testing-utils"; +import * as util from "./util"; + +setupTests(test); + +test.serial( + "analyze action with RAM & threads from environment variables", + async (t) => { + // This test frequently times out on Windows with the default timeout, so we bump + // it a bit to 20s. + t.timeout(1000 * 20); + await util.withTmpDir(async (tmpDir) => { + setupActionsVars(tmpDir, tmpDir); + sinon + .stub(statusReport, "createStatusReportBase") + .resolves({} as statusReport.StatusReportBase); + sinon.stub(statusReport, "sendStatusReport").resolves(); + sinon.stub(gitUtils, "isAnalyzingDefaultBranch").resolves(true); + + const gitHubVersion: util.GitHubVersion = { + type: util.GitHubVariant.DOTCOM, + }; + sinon.stub(configUtils, "getConfig").resolves({ + gitHubVersion, + augmentationProperties: {}, + languages: [], + packs: [], + trapCaches: {}, + } as unknown as configUtils.Config); + const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput"); + requiredInputStub.withArgs("token").returns("fake-token"); + requiredInputStub.withArgs("upload-database").returns("false"); + requiredInputStub.withArgs("output").returns("out"); + const optionalInputStub = sinon.stub(actionsUtil, "getOptionalInput"); + optionalInputStub.withArgs("expect-error").returns("false"); + sinon.stub(api, "getGitHubVersion").resolves(gitHubVersion); + mockFeatureFlagApiEndpoint(200, {}); + + // When there are no action inputs for RAM and threads, the action uses + // environment variables (passed down from the init action) to set RAM and + // threads usage. + process.env["CODEQL_THREADS"] = "-1"; + process.env["CODEQL_RAM"] = "4992"; + + const runFinalizeStub = sinon.stub(analyze, "runFinalize"); + const runQueriesStub = sinon.stub(analyze, "runQueries"); + + await runWrapper(); + + t.assert( + runFinalizeStub.calledOnceWith( + sinon.match.any, + sinon.match.any, + "--threads=-1", + "--ram=4992", + ), + ); + t.assert( + runQueriesStub.calledOnceWith( + sinon.match.any, + "--ram=4992", + "--threads=-1", + ), + ); + }); + }, +); + +test.serial( + "analyze action with RAM & threads from action inputs", + async (t) => { + t.timeout(1000 * 20); + await util.withTmpDir(async (tmpDir) => { + setupActionsVars(tmpDir, tmpDir); + sinon + .stub(statusReport, "createStatusReportBase") + .resolves({} as statusReport.StatusReportBase); + sinon.stub(statusReport, "sendStatusReport").resolves(); + const gitHubVersion: util.GitHubVersion = { + type: util.GitHubVariant.DOTCOM, + }; + sinon.stub(configUtils, "getConfig").resolves({ + gitHubVersion, + augmentationProperties: {}, + languages: [], + packs: [], + trapCaches: {}, + } as unknown as configUtils.Config); + const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput"); + requiredInputStub.withArgs("token").returns("fake-token"); + requiredInputStub.withArgs("upload-database").returns("false"); + requiredInputStub.withArgs("output").returns("out"); + const optionalInputStub = sinon.stub(actionsUtil, "getOptionalInput"); + optionalInputStub.withArgs("expect-error").returns("false"); + sinon.stub(api, "getGitHubVersion").resolves(gitHubVersion); + sinon.stub(gitUtils, "isAnalyzingDefaultBranch").resolves(true); + mockFeatureFlagApiEndpoint(200, {}); + + process.env["CODEQL_THREADS"] = "1"; + process.env["CODEQL_RAM"] = "4992"; + + // Action inputs have precedence over environment variables. + optionalInputStub.withArgs("threads").returns("-1"); + optionalInputStub.withArgs("ram").returns("3012"); + + const runFinalizeStub = sinon.stub(analyze, "runFinalize"); + const runQueriesStub = sinon.stub(analyze, "runQueries"); + + await runWrapper(); + + t.assert( + runFinalizeStub.calledOnceWith( + sinon.match.any, + sinon.match.any, + "--threads=-1", + "--ram=3012", + ), + ); + t.assert( + runQueriesStub.calledOnceWith( + sinon.match.any, + "--ram=3012", + "--threads=-1", + ), + ); + }); + }, +);