diff --git a/lib/init-action.js b/lib/init-action.js index fdbba7c1dc..0cfb864152 100644 --- a/lib/init-action.js +++ b/lib/init-action.js @@ -86062,6 +86062,50 @@ function isAnalyzingPullRequest() { return getPullRequestBranches() !== void 0; } +// src/analyses.ts +var AnalysisKind = /* @__PURE__ */ ((AnalysisKind3) => { + AnalysisKind3["CodeScanning"] = "code-scanning"; + AnalysisKind3["CodeQuality"] = "code-quality"; + return AnalysisKind3; +})(AnalysisKind || {}); +var supportedAnalysisKinds = new Set(Object.values(AnalysisKind)); +async function parseAnalysisKinds(input) { + const components = input.split(","); + if (components.length < 1) { + throw new ConfigurationError( + "At least one analysis kind must be configured." + ); + } + for (const component of components) { + if (!supportedAnalysisKinds.has(component)) { + throw new ConfigurationError(`Unknown analysis kind: ${component}`); + } + } + return Array.from( + new Set(components.map((component) => component)) + ); +} +var cachedAnalysisKinds; +async function getAnalysisKinds(logger, skipCache = false) { + if (!skipCache && cachedAnalysisKinds !== void 0) { + return cachedAnalysisKinds; + } + cachedAnalysisKinds = await parseAnalysisKinds( + getRequiredInput("analysis-kinds") + ); + const qualityQueriesInput = getOptionalInput("quality-queries"); + if (qualityQueriesInput !== void 0) { + logger.warning( + "The `quality-queries` input is deprecated and will be removed in a future version of the CodeQL Action. Use the `analysis-kinds` input to configure different analysis kinds instead." + ); + } + if (!cachedAnalysisKinds.includes("code-quality" /* CodeQuality */) && qualityQueriesInput !== void 0) { + cachedAnalysisKinds.push("code-quality" /* CodeQuality */); + } + return cachedAnalysisKinds; +} +var codeQualityQueries = ["code-quality"]; + // src/api-client.ts var core5 = __toESM(require_core()); var githubUtils = __toESM(require_utils4()); @@ -86254,31 +86298,6 @@ var fs9 = __toESM(require("fs")); var path11 = __toESM(require("path")); var import_perf_hooks = require("perf_hooks"); -// src/analyses.ts -var AnalysisKind = /* @__PURE__ */ ((AnalysisKind2) => { - AnalysisKind2["CodeScanning"] = "code-scanning"; - AnalysisKind2["CodeQuality"] = "code-quality"; - return AnalysisKind2; -})(AnalysisKind || {}); -var supportedAnalysisKinds = new Set(Object.values(AnalysisKind)); -async function parseAnalysisKinds(input) { - const components = input.split(","); - if (components.length < 1) { - throw new ConfigurationError( - "At least one analysis kind must be configured." - ); - } - for (const component of components) { - if (!supportedAnalysisKinds.has(component)) { - throw new ConfigurationError(`Unknown analysis kind: ${component}`); - } - } - return Array.from( - new Set(components.map((component) => component)) - ); -} -var codeQualityQueries = ["code-quality"]; - // src/config/db-config.ts var path7 = __toESM(require("path")); var semver2 = __toESM(require_semver2()); @@ -87691,10 +87710,8 @@ async function getRawLanguages(languagesInput, repository, sourceRoot, logger) { }; } async function initActionState({ - analysisKindsInput, languagesInput, queriesInput, - qualityQueriesInput, packsInput, buildModeInput, dbLocation, @@ -87710,12 +87727,9 @@ async function initActionState({ githubVersion, features, repositoryProperties, + analysisKinds, logger }, userConfig) { - const analysisKinds = await parseAnalysisKinds(analysisKindsInput); - if (!analysisKinds.includes("code-quality" /* CodeQuality */) && qualityQueriesInput !== void 0) { - analysisKinds.push("code-quality" /* CodeQuality */); - } const languages = await getLanguages( codeql, languagesInput, @@ -90683,6 +90697,19 @@ async function getWorkflowAbsolutePath(logger) { } // src/init-action.ts +async function sendStartingStatusReport(startedAt, config, logger) { + const statusReportBase = await createStatusReportBase( + "init" /* Init */, + "starting", + startedAt, + config, + await checkDiskUsage(logger), + logger + ); + if (statusReportBase !== void 0) { + await sendStatusReport(statusReportBase); + } +} async function sendCompletedStatusReport(startedAt, config, configFile, toolsDownloadStatusReport, toolsFeatureFlagsValid, toolsSource, toolsVersion, overlayBaseDatabaseStats, dependencyCachingResults, logger, error2) { const statusReportBase = await createStatusReportBase( "init" /* Init */, @@ -90773,17 +90800,15 @@ async function run() { getOptionalInput("source-root") || "" ); try { - const statusReportBase = await createStatusReportBase( - "init" /* Init */, - "starting", - startedAt, - config, - await checkDiskUsage(logger), - logger - ); - if (statusReportBase !== void 0) { - await sendStatusReport(statusReportBase); + let analysisKinds; + try { + analysisKinds = await getAnalysisKinds(logger); + } catch (err) { + logger.debug( + `Failed to parse analysis kinds for 'starting' status report: ${getErrorMessage(err)}` + ); } + await sendStartingStatusReport(startedAt, { analysisKinds }, logger); if (process.env["CODEQL_ACTION_SETUP_CODEQL_HAS_RUN" /* SETUP_CODEQL_ACTION_HAS_RUN */] === "true") { throw new ConfigurationError( `The 'init' action should not be run in the same workflow as 'setup-codeql'.` @@ -90835,17 +90860,11 @@ async function run() { logger.info("Experimental Rust analysis enabled"); } } - const qualityQueriesInput = getOptionalInput("quality-queries"); - if (qualityQueriesInput !== void 0) { - logger.warning( - "The `quality-queries` input is deprecated and will be removed in a future version of the CodeQL Action. Use the `analysis-kinds` input to configure different analysis kinds instead." - ); - } + analysisKinds = await getAnalysisKinds(logger); config = await initConfig2({ - analysisKindsInput: getRequiredInput("analysis-kinds"), + analysisKinds, languagesInput: getOptionalInput("languages"), queriesInput: getOptionalInput("queries"), - qualityQueriesInput, packsInput: getOptionalInput("packs"), buildModeInput: getOptionalInput("build-mode"), configFile, diff --git a/src/analyses.test.ts b/src/analyses.test.ts index 2c314c2c1a..9178ffbd5a 100644 --- a/src/analyses.test.ts +++ b/src/analyses.test.ts @@ -1,12 +1,19 @@ import test from "ava"; +import * as sinon from "sinon"; +import * as actionsUtil from "./actions-util"; import { AnalysisKind, + getAnalysisKinds, parseAnalysisKinds, supportedAnalysisKinds, } from "./analyses"; +import { getRunnerLogger } from "./logging"; +import { setupTests } from "./testing-utils"; import { ConfigurationError } from "./util"; +setupTests(test); + test("All known analysis kinds can be parsed successfully", async (t) => { for (const analysisKind of supportedAnalysisKinds) { t.deepEqual(await parseAnalysisKinds(analysisKind), [analysisKind]); @@ -34,3 +41,29 @@ test("Parsing analysis kinds requires at least one analysis kind", async (t) => instanceOf: ConfigurationError, }); }); + +test("getAnalysisKinds - returns expected analysis kinds for `analysis-kinds` input", async (t) => { + const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput"); + requiredInputStub + .withArgs("analysis-kinds") + .returns("code-scanning,code-quality"); + const result = await getAnalysisKinds(getRunnerLogger(true), true); + t.assert(result.includes(AnalysisKind.CodeScanning)); + t.assert(result.includes(AnalysisKind.CodeQuality)); +}); + +test("getAnalysisKinds - includes `code-quality` when deprecated `quality-queries` input is used", async (t) => { + const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput"); + requiredInputStub.withArgs("analysis-kinds").returns("code-scanning"); + const optionalInputStub = sinon.stub(actionsUtil, "getOptionalInput"); + optionalInputStub.withArgs("quality-queries").returns("code-quality"); + const result = await getAnalysisKinds(getRunnerLogger(true), true); + t.assert(result.includes(AnalysisKind.CodeScanning)); + t.assert(result.includes(AnalysisKind.CodeQuality)); +}); + +test("getAnalysisKinds - throws if `analysis-kinds` input is invalid", async (t) => { + const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput"); + requiredInputStub.withArgs("analysis-kinds").returns("no-such-thing"); + await t.throwsAsync(getAnalysisKinds(getRunnerLogger(true), true)); +}); diff --git a/src/analyses.ts b/src/analyses.ts index 7cac51a345..a8d172e3b6 100644 --- a/src/analyses.ts +++ b/src/analyses.ts @@ -1,4 +1,8 @@ -import { fixCodeQualityCategory } from "./actions-util"; +import { + fixCodeQualityCategory, + getOptionalInput, + getRequiredInput, +} from "./actions-util"; import { Logger } from "./logging"; import { ConfigurationError } from "./util"; @@ -41,6 +45,55 @@ export async function parseAnalysisKinds( ); } +// Used to avoid re-parsing the input after we have done it once. +let cachedAnalysisKinds: AnalysisKind[] | undefined; + +/** + * Initialises the analysis kinds for the analysis based on the `analysis-kinds` input. + * This function will also use the deprecated `quality-queries` input as an indicator to enable `code-quality`. + * If the `analysis-kinds` input cannot be parsed, a `ConfigurationError` is thrown. + * + * @param logger The logger to use. + * @param skipCache For testing, whether to ignore the cached values (default: false). + * + * @returns The array of enabled analysis kinds. + * @throws A `ConfigurationError` if the `analysis-kinds` input cannot be parsed. + */ +export async function getAnalysisKinds( + logger: Logger, + skipCache: boolean = false, +): Promise { + if (!skipCache && cachedAnalysisKinds !== undefined) { + return cachedAnalysisKinds; + } + + cachedAnalysisKinds = await parseAnalysisKinds( + getRequiredInput("analysis-kinds"), + ); + + // Warn that `quality-queries` is deprecated if there is an argument for it. + const qualityQueriesInput = getOptionalInput("quality-queries"); + + if (qualityQueriesInput !== undefined) { + logger.warning( + "The `quality-queries` input is deprecated and will be removed in a future version of the CodeQL Action. " + + "Use the `analysis-kinds` input to configure different analysis kinds instead.", + ); + } + + // For backwards compatibility, add Code Quality to the enabled analysis kinds + // if an input to `quality-queries` was specified. We should remove this once + // `quality-queries` is no longer used. + if ( + !cachedAnalysisKinds.includes(AnalysisKind.CodeQuality) && + qualityQueriesInput !== undefined + ) { + cachedAnalysisKinds.push(AnalysisKind.CodeQuality); + } + + return cachedAnalysisKinds; +} + /** The queries to use for Code Quality analyses. */ export const codeQualityQueries: string[] = ["code-quality"]; diff --git a/src/config-utils.test.ts b/src/config-utils.test.ts index 566a719ca0..c7d20a46f3 100644 --- a/src/config-utils.test.ts +++ b/src/config-utils.test.ts @@ -49,10 +49,9 @@ function createTestInitConfigInputs( return Object.assign( {}, { - analysisKindsInput: "code-scanning", + analysisKinds: [AnalysisKind.CodeScanning], languagesInput: undefined, queriesInput: undefined, - qualityQueriesInput: undefined, packsInput: undefined, configFile: undefined, dbLocation: undefined, @@ -189,7 +188,7 @@ test("load code quality config", async (t) => { const config = await configUtils.initConfig( createTestInitConfigInputs({ - analysisKindsInput: "code-quality", + analysisKinds: [AnalysisKind.CodeQuality], languagesInput: languages, repository: { owner: "github", repo: "example" }, tempDir, @@ -273,7 +272,7 @@ test("initActionState doesn't throw if there are queries configured in the repos await t.notThrowsAsync(async () => { const config = await configUtils.initConfig( createTestInitConfigInputs({ - analysisKindsInput: "code-quality", + analysisKinds: [AnalysisKind.CodeQuality], languagesInput: languages, repository: { owner: "github", repo: "example" }, tempDir, diff --git a/src/config-utils.ts b/src/config-utils.ts index e6c87bf5a6..25d7a949e5 100644 --- a/src/config-utils.ts +++ b/src/config-utils.ts @@ -11,7 +11,6 @@ import { CodeQuality, codeQualityQueries, CodeScanning, - parseAnalysisKinds, } from "./analyses"; import * as api from "./api-client"; import { CachingKind, getCachingKind } from "./caching-utils"; @@ -373,10 +372,8 @@ export async function getRawLanguages( /** Inputs required to initialize a configuration. */ export interface InitConfigInputs { - analysisKindsInput: string; languagesInput: string | undefined; queriesInput: string | undefined; - qualityQueriesInput: string | undefined; packsInput: string | undefined; configFile: string | undefined; dbLocation: string | undefined; @@ -396,6 +393,7 @@ export interface InitConfigInputs { apiDetails: api.GitHubApiCombinedDetails; features: FeatureEnablement; repositoryProperties: RepositoryProperties; + analysisKinds: AnalysisKind[]; logger: Logger; } @@ -405,10 +403,8 @@ export interface InitConfigInputs { */ export async function initActionState( { - analysisKindsInput, languagesInput, queriesInput, - qualityQueriesInput, packsInput, buildModeInput, dbLocation, @@ -424,22 +420,11 @@ export async function initActionState( githubVersion, features, repositoryProperties, + analysisKinds, logger, }: InitConfigInputs, userConfig: UserConfig, ): Promise { - const analysisKinds = await parseAnalysisKinds(analysisKindsInput); - - // For backwards compatibility, add Code Quality to the enabled analysis kinds - // if an input to `quality-queries` was specified. We should remove this once - // `quality-queries` is no longer used. - if ( - !analysisKinds.includes(AnalysisKind.CodeQuality) && - qualityQueriesInput !== undefined - ) { - analysisKinds.push(AnalysisKind.CodeQuality); - } - const languages = await getLanguages( codeql, languagesInput, diff --git a/src/init-action.ts b/src/init-action.ts index 7049851702..90971a1c16 100644 --- a/src/init-action.ts +++ b/src/init-action.ts @@ -15,6 +15,7 @@ import { getTemporaryDirectory, persistInputs, } from "./actions-util"; +import { AnalysisKind, getAnalysisKinds } from "./analyses"; import { getGitHubVersion } from "./api-client"; import { getDependencyCachingEnabled, @@ -87,6 +88,31 @@ import { } from "./util"; import { validateWorkflow } from "./workflow"; +/** + * Sends a status report indicating that the `init` Action is starting. + * + * @param startedAt + * @param config + * @param logger + */ +async function sendStartingStatusReport( + startedAt: Date, + config: Partial | undefined, + logger: Logger, +) { + const statusReportBase = await createStatusReportBase( + ActionName.Init, + "starting", + startedAt, + config, + await checkDiskUsage(logger), + logger, + ); + if (statusReportBase !== undefined) { + await sendStatusReport(statusReportBase); + } +} + async function sendCompletedStatusReport( startedAt: Date, config: configUtils.Config | undefined, @@ -219,18 +245,23 @@ async function run() { ); try { - const statusReportBase = await createStatusReportBase( - ActionName.Init, - "starting", - startedAt, - config, - await checkDiskUsage(logger), - logger, - ); - if (statusReportBase !== undefined) { - await sendStatusReport(statusReportBase); + // Parsing the `analysis-kinds` input may throw a `ConfigurationError`, which we don't want before + // we have called `sendStartingStatusReport` below. However, we want the analysis kinds for that status + // report. To work around this, we ignore exceptions that are thrown here and then call `getAnalysisKinds` + // a second time later. The second call will then throw the exception again. If `getAnalysisKinds` is + // successful, the results are cached so that we don't duplicate the work in normal runs. + let analysisKinds: AnalysisKind[] | undefined; + try { + analysisKinds = await getAnalysisKinds(logger); + } catch (err) { + logger.debug( + `Failed to parse analysis kinds for 'starting' status report: ${getErrorMessage(err)}`, + ); } + // Send a status report indicating that an analysis is starting. + await sendStartingStatusReport(startedAt, { analysisKinds }, logger); + // Throw a `ConfigurationError` if the `setup-codeql` action has been run. if (process.env[EnvVar.SETUP_CODEQL_ACTION_HAS_RUN] === "true") { throw new ConfigurationError( @@ -293,21 +324,11 @@ async function run() { } } - // Warn that `quality-queries` is deprecated if there is an argument for it. - const qualityQueriesInput = getOptionalInput("quality-queries"); - - if (qualityQueriesInput !== undefined) { - logger.warning( - "The `quality-queries` input is deprecated and will be removed in a future version of the CodeQL Action. " + - "Use the `analysis-kinds` input to configure different analysis kinds instead.", - ); - } - + analysisKinds = await getAnalysisKinds(logger); config = await initConfig({ - analysisKindsInput: getRequiredInput("analysis-kinds"), + analysisKinds, languagesInput: getOptionalInput("languages"), queriesInput: getOptionalInput("queries"), - qualityQueriesInput, packsInput: getOptionalInput("packs"), buildModeInput: getOptionalInput("build-mode"), configFile,