diff --git a/.github/workflows/__bundle-from-nightly.yml b/.github/workflows/__bundle-from-nightly.yml new file mode 100644 index 0000000000..21f545feef --- /dev/null +++ b/.github/workflows/__bundle-from-nightly.yml @@ -0,0 +1,68 @@ +# Warning: This file is generated automatically, and should not be modified. +# Instead, please modify the template in the pr-checks directory and run: +# pr-checks/sync.sh +# to regenerate this file. + +name: 'PR Check - Bundle: From nightly' +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GO111MODULE: auto +on: + push: + branches: + - main + - releases/v* + pull_request: + types: + - opened + - synchronize + - reopened + - ready_for_review + schedule: + - cron: '0 5 * * *' + workflow_dispatch: + inputs: {} + workflow_call: + inputs: {} +defaults: + run: + shell: bash +concurrency: + cancel-in-progress: ${{ github.event_name == 'pull_request' || false }} + group: bundle-from-nightly-${{github.ref}} +jobs: + bundle-from-nightly: + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + version: linked + name: 'Bundle: From nightly' + if: github.triggering_actor != 'dependabot[bot]' + permissions: + contents: read + security-events: read + timeout-minutes: 45 + runs-on: ${{ matrix.os }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + - name: Prepare test + id: prepare-test + uses: ./.github/actions/prepare-test + with: + version: ${{ matrix.version }} + use-all-platform-bundle: 'false' + setup-kotlin: 'true' + - id: setup-codeql + uses: ./../action/setup-codeql + env: + CODEQL_ACTION_FORCE_NIGHTLY: true + with: + tools: ${{ steps.prepare-test.outputs.tools-url }} + - name: Fail if the CodeQL version is not a nightly + if: "!contains(steps.setup-codeql.outputs.codeql-version, '+')" + run: exit 1 + env: + CODEQL_ACTION_TEST_MODE: true diff --git a/lib/analyze-action-post.js b/lib/analyze-action-post.js index 2920609e05..901f23e068 100644 --- a/lib/analyze-action-post.js +++ b/lib/analyze-action-post.js @@ -161566,6 +161566,11 @@ var featureConfig = { legacyApi: true, minimumVersion: void 0 }, + ["force_nightly" /* ForceNightly */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_FORCE_NIGHTLY", + minimumVersion: void 0 + }, ["ignore_generated_files" /* IgnoreGeneratedFiles */]: { defaultValue: false, envVar: "CODEQL_ACTION_IGNORE_GENERATED_FILES", diff --git a/lib/analyze-action.js b/lib/analyze-action.js index 40348b31f5..ff5731b50a 100644 --- a/lib/analyze-action.js +++ b/lib/analyze-action.js @@ -107634,6 +107634,11 @@ var featureConfig = { legacyApi: true, minimumVersion: void 0 }, + ["force_nightly" /* ForceNightly */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_FORCE_NIGHTLY", + minimumVersion: void 0 + }, ["ignore_generated_files" /* IgnoreGeneratedFiles */]: { defaultValue: false, envVar: "CODEQL_ACTION_IGNORE_GENERATED_FILES", @@ -108975,10 +108980,18 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian let cliVersion2; let tagName; let url2; - if (toolsInput !== void 0 && CODEQL_NIGHTLY_TOOLS_INPUTS.includes(toolsInput)) { - logger.info( - `Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.` - ); + const forceNightlyValueFF = await features.getValue("force_nightly" /* ForceNightly */); + const forceNightly = forceNightlyValueFF && (isDynamicWorkflow() || isInTestMode()); + if (forceNightly || toolsInput !== void 0 && CODEQL_NIGHTLY_TOOLS_INPUTS.includes(toolsInput)) { + if (forceNightly) { + logger.info( + `Using the latest CodeQL CLI nightly, as forced by the ${"force_nightly" /* ForceNightly */} feature flag.` + ); + } else { + logger.info( + `Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.` + ); + } toolsInput = await getNightlyToolsUrl(logger); } const forceShippedTools = toolsInput && CODEQL_BUNDLE_VERSION_ALIAS.includes(toolsInput); diff --git a/lib/autobuild-action.js b/lib/autobuild-action.js index 61525b980d..95bfad8d48 100644 --- a/lib/autobuild-action.js +++ b/lib/autobuild-action.js @@ -103971,6 +103971,11 @@ var featureConfig = { legacyApi: true, minimumVersion: void 0 }, + ["force_nightly" /* ForceNightly */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_FORCE_NIGHTLY", + minimumVersion: void 0 + }, ["ignore_generated_files" /* IgnoreGeneratedFiles */]: { defaultValue: false, envVar: "CODEQL_ACTION_IGNORE_GENERATED_FILES", diff --git a/lib/init-action-post.js b/lib/init-action-post.js index 2fb2de3518..e943a4f540 100644 --- a/lib/init-action-post.js +++ b/lib/init-action-post.js @@ -164960,6 +164960,11 @@ var featureConfig = { legacyApi: true, minimumVersion: void 0 }, + ["force_nightly" /* ForceNightly */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_FORCE_NIGHTLY", + minimumVersion: void 0 + }, ["ignore_generated_files" /* IgnoreGeneratedFiles */]: { defaultValue: false, envVar: "CODEQL_ACTION_IGNORE_GENERATED_FILES", @@ -166037,10 +166042,18 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian let cliVersion2; let tagName; let url2; - if (toolsInput !== void 0 && CODEQL_NIGHTLY_TOOLS_INPUTS.includes(toolsInput)) { - logger.info( - `Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.` - ); + const forceNightlyValueFF = await features.getValue("force_nightly" /* ForceNightly */); + const forceNightly = forceNightlyValueFF && (isDynamicWorkflow() || isInTestMode()); + if (forceNightly || toolsInput !== void 0 && CODEQL_NIGHTLY_TOOLS_INPUTS.includes(toolsInput)) { + if (forceNightly) { + logger.info( + `Using the latest CodeQL CLI nightly, as forced by the ${"force_nightly" /* ForceNightly */} feature flag.` + ); + } else { + logger.info( + `Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.` + ); + } toolsInput = await getNightlyToolsUrl(logger); } const forceShippedTools = toolsInput && CODEQL_BUNDLE_VERSION_ALIAS.includes(toolsInput); diff --git a/lib/init-action.js b/lib/init-action.js index 19a73d5c41..5712761bcb 100644 --- a/lib/init-action.js +++ b/lib/init-action.js @@ -105167,6 +105167,11 @@ var featureConfig = { legacyApi: true, minimumVersion: void 0 }, + ["force_nightly" /* ForceNightly */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_FORCE_NIGHTLY", + minimumVersion: void 0 + }, ["ignore_generated_files" /* IgnoreGeneratedFiles */]: { defaultValue: false, envVar: "CODEQL_ACTION_IGNORE_GENERATED_FILES", @@ -107378,10 +107383,18 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian let cliVersion2; let tagName; let url; - if (toolsInput !== void 0 && CODEQL_NIGHTLY_TOOLS_INPUTS.includes(toolsInput)) { - logger.info( - `Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.` - ); + const forceNightlyValueFF = await features.getValue("force_nightly" /* ForceNightly */); + const forceNightly = forceNightlyValueFF && (isDynamicWorkflow() || isInTestMode()); + if (forceNightly || toolsInput !== void 0 && CODEQL_NIGHTLY_TOOLS_INPUTS.includes(toolsInput)) { + if (forceNightly) { + logger.info( + `Using the latest CodeQL CLI nightly, as forced by the ${"force_nightly" /* ForceNightly */} feature flag.` + ); + } else { + logger.info( + `Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.` + ); + } toolsInput = await getNightlyToolsUrl(logger); } const forceShippedTools = toolsInput && CODEQL_BUNDLE_VERSION_ALIAS.includes(toolsInput); diff --git a/lib/resolve-environment-action.js b/lib/resolve-environment-action.js index 2e53715e63..d1e66317df 100644 --- a/lib/resolve-environment-action.js +++ b/lib/resolve-environment-action.js @@ -103958,6 +103958,11 @@ var featureConfig = { legacyApi: true, minimumVersion: void 0 }, + ["force_nightly" /* ForceNightly */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_FORCE_NIGHTLY", + minimumVersion: void 0 + }, ["ignore_generated_files" /* IgnoreGeneratedFiles */]: { defaultValue: false, envVar: "CODEQL_ACTION_IGNORE_GENERATED_FILES", diff --git a/lib/setup-codeql-action.js b/lib/setup-codeql-action.js index 289d27e5c6..c2b1a51192 100644 --- a/lib/setup-codeql-action.js +++ b/lib/setup-codeql-action.js @@ -103872,6 +103872,11 @@ var featureConfig = { legacyApi: true, minimumVersion: void 0 }, + ["force_nightly" /* ForceNightly */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_FORCE_NIGHTLY", + minimumVersion: void 0 + }, ["ignore_generated_files" /* IgnoreGeneratedFiles */]: { defaultValue: false, envVar: "CODEQL_ACTION_IGNORE_GENERATED_FILES", @@ -105120,10 +105125,18 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian let cliVersion2; let tagName; let url; - if (toolsInput !== void 0 && CODEQL_NIGHTLY_TOOLS_INPUTS.includes(toolsInput)) { - logger.info( - `Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.` - ); + const forceNightlyValueFF = await features.getValue("force_nightly" /* ForceNightly */); + const forceNightly = forceNightlyValueFF && (isDynamicWorkflow() || isInTestMode()); + if (forceNightly || toolsInput !== void 0 && CODEQL_NIGHTLY_TOOLS_INPUTS.includes(toolsInput)) { + if (forceNightly) { + logger.info( + `Using the latest CodeQL CLI nightly, as forced by the ${"force_nightly" /* ForceNightly */} feature flag.` + ); + } else { + logger.info( + `Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.` + ); + } toolsInput = await getNightlyToolsUrl(logger); } const forceShippedTools = toolsInput && CODEQL_BUNDLE_VERSION_ALIAS.includes(toolsInput); diff --git a/lib/start-proxy-action-post.js b/lib/start-proxy-action-post.js index 0ba8bd6e7e..6511bca4b1 100644 --- a/lib/start-proxy-action-post.js +++ b/lib/start-proxy-action-post.js @@ -160972,6 +160972,11 @@ var featureConfig = { legacyApi: true, minimumVersion: void 0 }, + ["force_nightly" /* ForceNightly */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_FORCE_NIGHTLY", + minimumVersion: void 0 + }, ["ignore_generated_files" /* IgnoreGeneratedFiles */]: { defaultValue: false, envVar: "CODEQL_ACTION_IGNORE_GENERATED_FILES", diff --git a/lib/start-proxy-action.js b/lib/start-proxy-action.js index 2b16dae66c..f46813ec5c 100644 --- a/lib/start-proxy-action.js +++ b/lib/start-proxy-action.js @@ -120665,6 +120665,11 @@ var featureConfig = { legacyApi: true, minimumVersion: void 0 }, + ["force_nightly" /* ForceNightly */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_FORCE_NIGHTLY", + minimumVersion: void 0 + }, ["ignore_generated_files" /* IgnoreGeneratedFiles */]: { defaultValue: false, envVar: "CODEQL_ACTION_IGNORE_GENERATED_FILES", diff --git a/lib/upload-lib.js b/lib/upload-lib.js index 730c460ab8..5562a4e0c4 100644 --- a/lib/upload-lib.js +++ b/lib/upload-lib.js @@ -107027,6 +107027,11 @@ var featureConfig = { legacyApi: true, minimumVersion: void 0 }, + ["force_nightly" /* ForceNightly */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_FORCE_NIGHTLY", + minimumVersion: void 0 + }, ["ignore_generated_files" /* IgnoreGeneratedFiles */]: { defaultValue: false, envVar: "CODEQL_ACTION_IGNORE_GENERATED_FILES", @@ -107818,10 +107823,18 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian let cliVersion2; let tagName; let url2; - if (toolsInput !== void 0 && CODEQL_NIGHTLY_TOOLS_INPUTS.includes(toolsInput)) { - logger.info( - `Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.` - ); + const forceNightlyValueFF = await features.getValue("force_nightly" /* ForceNightly */); + const forceNightly = forceNightlyValueFF && (isDynamicWorkflow() || isInTestMode()); + if (forceNightly || toolsInput !== void 0 && CODEQL_NIGHTLY_TOOLS_INPUTS.includes(toolsInput)) { + if (forceNightly) { + logger.info( + `Using the latest CodeQL CLI nightly, as forced by the ${"force_nightly" /* ForceNightly */} feature flag.` + ); + } else { + logger.info( + `Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.` + ); + } toolsInput = await getNightlyToolsUrl(logger); } const forceShippedTools = toolsInput && CODEQL_BUNDLE_VERSION_ALIAS.includes(toolsInput); diff --git a/lib/upload-sarif-action-post.js b/lib/upload-sarif-action-post.js index 00f580c815..5bd76e9ed9 100644 --- a/lib/upload-sarif-action-post.js +++ b/lib/upload-sarif-action-post.js @@ -161134,6 +161134,11 @@ var featureConfig = { legacyApi: true, minimumVersion: void 0 }, + ["force_nightly" /* ForceNightly */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_FORCE_NIGHTLY", + minimumVersion: void 0 + }, ["ignore_generated_files" /* IgnoreGeneratedFiles */]: { defaultValue: false, envVar: "CODEQL_ACTION_IGNORE_GENERATED_FILES", diff --git a/lib/upload-sarif-action.js b/lib/upload-sarif-action.js index 15e29edd58..e154e97e77 100644 --- a/lib/upload-sarif-action.js +++ b/lib/upload-sarif-action.js @@ -106822,6 +106822,11 @@ var featureConfig = { legacyApi: true, minimumVersion: void 0 }, + ["force_nightly" /* ForceNightly */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_FORCE_NIGHTLY", + minimumVersion: void 0 + }, ["ignore_generated_files" /* IgnoreGeneratedFiles */]: { defaultValue: false, envVar: "CODEQL_ACTION_IGNORE_GENERATED_FILES", @@ -108380,10 +108385,18 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian let cliVersion2; let tagName; let url2; - if (toolsInput !== void 0 && CODEQL_NIGHTLY_TOOLS_INPUTS.includes(toolsInput)) { - logger.info( - `Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.` - ); + const forceNightlyValueFF = await features.getValue("force_nightly" /* ForceNightly */); + const forceNightly = forceNightlyValueFF && (isDynamicWorkflow() || isInTestMode()); + if (forceNightly || toolsInput !== void 0 && CODEQL_NIGHTLY_TOOLS_INPUTS.includes(toolsInput)) { + if (forceNightly) { + logger.info( + `Using the latest CodeQL CLI nightly, as forced by the ${"force_nightly" /* ForceNightly */} feature flag.` + ); + } else { + logger.info( + `Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.` + ); + } toolsInput = await getNightlyToolsUrl(logger); } const forceShippedTools = toolsInput && CODEQL_BUNDLE_VERSION_ALIAS.includes(toolsInput); diff --git a/pr-checks/checks/bundle-from-nightly.yml b/pr-checks/checks/bundle-from-nightly.yml new file mode 100644 index 0000000000..961de4ce75 --- /dev/null +++ b/pr-checks/checks/bundle-from-nightly.yml @@ -0,0 +1,14 @@ +name: "Bundle: From nightly" +description: "The nightly CodeQL bundle should be used when forced" +versions: + - linked # overruled by the FF set below +steps: + - id: setup-codeql + uses: ./../action/setup-codeql + env: + CODEQL_ACTION_FORCE_NIGHTLY: true + with: + tools: ${{ steps.prepare-test.outputs.tools-url }} + - name: Fail if the CodeQL version is not a nightly + if: "!contains(steps.setup-codeql.outputs.codeql-version, '+')" + run: exit 1 diff --git a/src/feature-flags.ts b/src/feature-flags.ts index 04508089d9..9f0db08163 100644 --- a/src/feature-flags.ts +++ b/src/feature-flags.ts @@ -46,6 +46,7 @@ export enum Feature { DisableJavaBuildlessEnabled = "disable_java_buildless_enabled", DisableKotlinAnalysisEnabled = "disable_kotlin_analysis_enabled", ExportDiagnosticsEnabled = "export_diagnostics_enabled", + ForceNightly = "force_nightly", IgnoreGeneratedFiles = "ignore_generated_files", OverlayAnalysis = "overlay_analysis", OverlayAnalysisActions = "overlay_analysis_actions", @@ -163,6 +164,11 @@ export const featureConfig = { legacyApi: true, minimumVersion: undefined, }, + [Feature.ForceNightly]: { + defaultValue: false, + envVar: "CODEQL_ACTION_FORCE_NIGHTLY", + minimumVersion: undefined, + }, [Feature.IgnoreGeneratedFiles]: { defaultValue: false, envVar: "CODEQL_ACTION_IGNORE_GENERATED_FILES", diff --git a/src/setup-codeql.test.ts b/src/setup-codeql.test.ts index 3046b6ff56..c93271975d 100644 --- a/src/setup-codeql.test.ts +++ b/src/setup-codeql.test.ts @@ -1,18 +1,22 @@ import * as path from "path"; +import * as github from "@actions/github"; import * as toolcache from "@actions/tool-cache"; import test, { ExecutionContext } from "ava"; import * as sinon from "sinon"; import * as actionsUtil from "./actions-util"; +import * as api from "./api-client"; import { Feature, FeatureEnablement } from "./feature-flags"; import { getRunnerLogger } from "./logging"; import * as setupCodeql from "./setup-codeql"; +import * as tar from "./tar"; import { LINKED_CLI_VERSION, LoggedMessage, SAMPLE_DEFAULT_CLI_VERSION, SAMPLE_DOTCOM_API_DETAILS, + checkExpectedLogMessages, createFeatures, getRecordingLogger, initializeFeatures, @@ -268,6 +272,122 @@ test("setupCodeQLBundle logs the CodeQL CLI version being used when asked to dow }); }); +test("getCodeQLSource correctly returns nightly CLI version when tools == nightly", async (t) => { + const loggedMessages: LoggedMessage[] = []; + const logger = getRecordingLogger(loggedMessages); + const features = createFeatures([]); + + const expectedDate = "30260213"; + const expectedTag = `codeql-bundle-${expectedDate}`; + + // Ensure that we consistently select "zstd" for the test. + sinon.stub(process, "platform").value("linux"); + sinon.stub(tar, "isZstdAvailable").resolves({ + available: true, + foundZstdBinary: true, + }); + + const client = github.getOctokit("123"); + const listReleases = sinon.stub(client.rest.repos, "listReleases"); + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + listReleases.resolves({ + data: [{ tag_name: expectedTag }], + } as any); + sinon.stub(api, "getApiClient").value(() => client); + + await withTmpDir(async (tmpDir) => { + setupActionsVars(tmpDir, tmpDir); + const source = await setupCodeql.getCodeQLSource( + "nightly", + SAMPLE_DEFAULT_CLI_VERSION, + SAMPLE_DOTCOM_API_DETAILS, + GitHubVariant.DOTCOM, + false, + features, + logger, + ); + + // Check that the `CodeQLToolsSource` object matches our expectations. + const expectedVersion = `0.0.0-${expectedDate}`; + const expectedURL = `https://github.com/dsp-testing/codeql-cli-nightlies/releases/download/${expectedTag}/${setupCodeql.getCodeQLBundleName("zstd")}`; + t.deepEqual(source, { + bundleVersion: expectedDate, + cliVersion: undefined, + codeqlURL: expectedURL, + compressionMethod: "zstd", + sourceType: "download", + toolsVersion: expectedVersion, + } satisfies setupCodeql.CodeQLToolsSource); + + // Afterwards, ensure that we see the expected messages in the log. + checkExpectedLogMessages(t, loggedMessages, [ + "Using the latest CodeQL CLI nightly, as requested by 'tools: nightly'.", + `Bundle version ${expectedDate} is not in SemVer format. Will treat it as pre-release ${expectedVersion}.`, + `Attempting to obtain CodeQL tools. CLI version: unknown, bundle tag name: ${expectedTag}`, + `Using CodeQL CLI sourced from ${expectedURL}`, + ]); + }); +}); + +test("getCodeQLSource correctly returns nightly CLI version when forced by FF", async (t) => { + const loggedMessages: LoggedMessage[] = []; + const logger = getRecordingLogger(loggedMessages); + const features = createFeatures([Feature.ForceNightly]); + + process.env["GITHUB_EVENT_NAME"] = "dynamic"; + + const expectedDate = "30260213"; + const expectedTag = `codeql-bundle-${expectedDate}`; + + // Ensure that we consistently select "zstd" for the test. + sinon.stub(process, "platform").value("linux"); + sinon.stub(tar, "isZstdAvailable").resolves({ + available: true, + foundZstdBinary: true, + }); + + const client = github.getOctokit("123"); + const listReleases = sinon.stub(client.rest.repos, "listReleases"); + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + listReleases.resolves({ + data: [{ tag_name: expectedTag }], + } as any); + sinon.stub(api, "getApiClient").value(() => client); + + await withTmpDir(async (tmpDir) => { + setupActionsVars(tmpDir, tmpDir); + const source = await setupCodeql.getCodeQLSource( + undefined, + SAMPLE_DEFAULT_CLI_VERSION, + SAMPLE_DOTCOM_API_DETAILS, + GitHubVariant.DOTCOM, + false, + features, + logger, + ); + + // Check that the `CodeQLToolsSource` object matches our expectations. + const expectedVersion = `0.0.0-${expectedDate}`; + const expectedURL = `https://github.com/dsp-testing/codeql-cli-nightlies/releases/download/${expectedTag}/${setupCodeql.getCodeQLBundleName("zstd")}`; + t.deepEqual(source, { + bundleVersion: expectedDate, + cliVersion: undefined, + codeqlURL: expectedURL, + compressionMethod: "zstd", + sourceType: "download", + toolsVersion: expectedVersion, + } satisfies setupCodeql.CodeQLToolsSource); + + // Afterwards, ensure that we see the expected messages in the log. + checkExpectedLogMessages(t, loggedMessages, [ + `Using the latest CodeQL CLI nightly, as forced by the ${Feature.ForceNightly} feature flag.`, + `Bundle version ${expectedDate} is not in SemVer format. Will treat it as pre-release ${expectedVersion}.`, + `Attempting to obtain CodeQL tools. CLI version: unknown, bundle tag name: ${expectedTag}`, + `Using CodeQL CLI sourced from ${expectedURL}`, + ]); + }); +}); + test("getCodeQLSource correctly returns latest version from toolcache when tools == toolcache", async (t) => { const loggedMessages: LoggedMessage[] = []; const logger = getRecordingLogger(loggedMessages); diff --git a/src/setup-codeql.ts b/src/setup-codeql.ts index 2a39673d97..e907da456c 100644 --- a/src/setup-codeql.ts +++ b/src/setup-codeql.ts @@ -55,7 +55,9 @@ function getCodeQLBundleExtension( } } -function getCodeQLBundleName(compressionMethod: tar.CompressionMethod): string { +export function getCodeQLBundleName( + compressionMethod: tar.CompressionMethod, +): string { const extension = getCodeQLBundleExtension(compressionMethod); let platform: string; @@ -196,7 +198,7 @@ export function convertToSemVer(version: string, logger: Logger): string { return s; } -type CodeQLToolsSource = +export type CodeQLToolsSource = | { codeqlTarPath: string; compressionMethod: tar.CompressionMethod; @@ -261,6 +263,20 @@ async function findOverridingToolsInCache( return undefined; } +/** + * Determines where the CodeQL CLI we want to use comes from. This can be from a local file, + * the Actions toolcache, or a download. + * + * @param toolsInput The argument provided for the `tools` input, if any. + * @param defaultCliVersion The default CLI version that's linked to the CodeQL Action. + * @param apiDetails Information about the GitHub API. + * @param variant The GitHub variant we are running on. + * @param tarSupportsZstd Whether zstd is supported by `tar`. + * @param features Information about enabled features. + * @param logger The logger to use. + * + * @returns + */ export async function getCodeQLSource( toolsInput: string | undefined, defaultCliVersion: CodeQLDefaultVersionInfo, @@ -270,6 +286,9 @@ export async function getCodeQLSource( features: FeatureEnablement, logger: Logger, ): Promise { + // If there is an explicit `tools` input, it's not one of the reserved values, and it doesn't appear + // to point to a URL, then we assume it is a local path and use the CLI from there. + // TODO: This appears to misclassify filenames that happen to start with `http` as URLs. if ( toolsInput && !isReservedToolsValue(toolsInput) && @@ -302,13 +321,26 @@ export async function getCodeQLSource( */ let url: string | undefined; + // We only allow forcing the nightly CLI via the FF for `dynamic` events (or in test mode). + // For advanced workflows, a value from `CODEQL_NIGHTLY_TOOLS_INPUTS` can be specified. + const forceNightlyValueFF = await features.getValue(Feature.ForceNightly); + const forceNightly = + forceNightlyValueFF && (isDynamicWorkflow() || util.isInTestMode()); + if ( - toolsInput !== undefined && - CODEQL_NIGHTLY_TOOLS_INPUTS.includes(toolsInput) + forceNightly || + (toolsInput !== undefined && + CODEQL_NIGHTLY_TOOLS_INPUTS.includes(toolsInput)) ) { - logger.info( - `Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.`, - ); + if (forceNightly) { + logger.info( + `Using the latest CodeQL CLI nightly, as forced by the ${Feature.ForceNightly} feature flag.`, + ); + } else { + logger.info( + `Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.`, + ); + } toolsInput = await getNightlyToolsUrl(logger); }