diff --git a/CHANGELOG.md b/CHANGELOG.md index d94f28e616..4ab7938f6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ See the [releases page](https://github.com/github/codeql-action/releases) for th ## [UNRELEASED] -No user facing changes. +- Organizations can now create a custom repository property with the name `github-codeql-tools` to control the CodeQL CLI tools input at the repository level. When this property is set to a valid tools input value (such as `"toolcache"`, `"latest"`, or a specific version), it will override the default tools configuration for that repository. This allows organization administrators to standardize CodeQL CLI versions across repositories or enable toolcache usage on repositories where it would otherwise be restricted. For more information on creating custom repository properties, see [Managing custom properties for repositories in your organization](https://docs.github.com/en/organizations/managing-organization-settings/managing-custom-properties-for-repositories-in-your-organization). ## 4.34.1 - 20 Mar 2026 diff --git a/lib/analyze-action-post.js b/lib/analyze-action-post.js index b501469f5e..fc411ec94e 100644 --- a/lib/analyze-action-post.js +++ b/lib/analyze-action-post.js @@ -162033,6 +162033,7 @@ var RepositoryPropertyName = /* @__PURE__ */ ((RepositoryPropertyName2) => { RepositoryPropertyName2["DISABLE_OVERLAY"] = "github-codeql-disable-overlay"; RepositoryPropertyName2["EXTRA_QUERIES"] = "github-codeql-extra-queries"; RepositoryPropertyName2["FILE_COVERAGE_ON_PRS"] = "github-codeql-file-coverage-on-prs"; + RepositoryPropertyName2["TOOLS"] = "github-codeql-tools"; return RepositoryPropertyName2; })(RepositoryPropertyName || {}); var KNOWN_REPOSITORY_PROPERTY_NAMES = new Set( diff --git a/lib/analyze-action.js b/lib/analyze-action.js index 7f7f4fbe88..24494ac487 100644 --- a/lib/analyze-action.js +++ b/lib/analyze-action.js @@ -107537,6 +107537,7 @@ var RepositoryPropertyName = /* @__PURE__ */ ((RepositoryPropertyName2) => { RepositoryPropertyName2["DISABLE_OVERLAY"] = "github-codeql-disable-overlay"; RepositoryPropertyName2["EXTRA_QUERIES"] = "github-codeql-extra-queries"; RepositoryPropertyName2["FILE_COVERAGE_ON_PRS"] = "github-codeql-file-coverage-on-prs"; + RepositoryPropertyName2["TOOLS"] = "github-codeql-tools"; return RepositoryPropertyName2; })(RepositoryPropertyName || {}); var KNOWN_REPOSITORY_PROPERTY_NAMES = new Set( @@ -109491,7 +109492,7 @@ async function findOverridingToolsInCache(humanReadableVersion, logger) { } return void 0; } -async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, variant, tarSupportsZstd, features, logger) { +async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, variant, tarSupportsZstd, features, logger, toolsInputFromRepositoryProperty = false) { if (toolsInput && !isReservedToolsValue(toolsInput) && !toolsInput.startsWith("http")) { logger.info(`Using CodeQL CLI from local path ${toolsInput}`); const compressionMethod2 = inferCompressionMethod(toolsInput); @@ -109537,7 +109538,7 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian ); } else { logger.info( - `Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.` + `Using the latest CodeQL CLI nightly, as requested.` ); } toolsInput = await getNightlyToolsUrl(logger); @@ -109559,11 +109560,18 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian const allowToolcacheValueFF = await features.getValue( "allow_toolcache_input" /* AllowToolcacheInput */ ); - const allowToolcacheValue = allowToolcacheValueFF && (isDynamicWorkflow() || isInTestMode()); + const allowToolcacheValue = toolsInputFromRepositoryProperty || // Repository properties bypass all restrictions + allowToolcacheValueFF && (isDynamicWorkflow() || isInTestMode()); if (allowToolcacheValue) { - logger.info( - `Attempting to use the latest CodeQL CLI version in the toolcache, as requested by 'tools: ${toolsInput}'.` - ); + if (toolsInputFromRepositoryProperty) { + logger.info( + `Attempting to use the latest CodeQL CLI version in the toolcache, as requested by the '${"github-codeql-tools" /* TOOLS */}' repository property.` + ); + } else { + logger.info( + `Attempting to use the latest CodeQL CLI version in the toolcache, as requested.` + ); + } latestToolcacheVersion = getLatestToolcacheVersion(logger); if (latestToolcacheVersion) { cliVersion2 = latestToolcacheVersion; @@ -109571,16 +109579,22 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian } if (latestToolcacheVersion === void 0) { if (allowToolcacheValue) { - logger.info( - `Found no CodeQL CLI in the toolcache, ignoring 'tools: ${toolsInput}'...` - ); + if (toolsInputFromRepositoryProperty) { + logger.info( + `Found no CodeQL CLI in the toolcache, ignoring the '${"github-codeql-tools" /* TOOLS */}' repository property...` + ); + } else { + logger.info( + `Found no CodeQL CLI in the toolcache, ignoring 'tools: ${toolsInput}'...` + ); + } } else { if (allowToolcacheValueFF) { logger.warning( `Ignoring 'tools: ${toolsInput}' because the workflow was not triggered dynamically.` ); } else { - logger.info( + logger.warning( `Ignoring 'tools: ${toolsInput}' because the feature is not enabled.` ); } @@ -109796,7 +109810,7 @@ function getCanonicalToolcacheVersion(cliVersion2, bundleVersion2, logger) { } return cliVersion2; } -async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, features, logger) { +async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, features, logger, toolsInputFromRepositoryProperty = false) { if (!await isBinaryAccessible("tar", logger)) { throw new ConfigurationError( "Could not find tar in PATH, so unable to extract CodeQL bundle." @@ -109810,7 +109824,8 @@ async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defau variant, zstdAvailability.available, features, - logger + logger, + toolsInputFromRepositoryProperty ); let codeqlFolder; let toolsVersion = source.toolsVersion; @@ -113232,20 +113247,33 @@ async function waitForProcessing(repositoryNwo, sarifID, logger, options = { ); break; } + if (!response) { + logger.warning( + "Unable to check analysis status due to missing response. It should still be processed in the background." + ); + break; + } const status = response.data.processing_status; logger.info(`Analysis upload status is ${status}.`); if (status === "pending") { logger.debug("Analysis processing is still pending..."); } else if (options.isUnsuccessfulExecution) { - handleProcessingResultForUnsuccessfulExecution( - response, - status, - logger - ); + if (response) { + handleProcessingResultForUnsuccessfulExecution( + response, + status, + logger + ); + } break; } else if (status === "complete") { break; } else if (status === "failed") { + if (!response) { + throw new Error( + "Code Scanning could not process the submitted SARIF file: Unable to retrieve error details." + ); + } const message = `Code Scanning could not process the submitted SARIF file: ${response.data.errors}`; const processingErrors = response.data.errors; diff --git a/lib/autobuild-action.js b/lib/autobuild-action.js index 42d955963e..eabf92cff0 100644 --- a/lib/autobuild-action.js +++ b/lib/autobuild-action.js @@ -104089,6 +104089,7 @@ var RepositoryPropertyName = /* @__PURE__ */ ((RepositoryPropertyName2) => { RepositoryPropertyName2["DISABLE_OVERLAY"] = "github-codeql-disable-overlay"; RepositoryPropertyName2["EXTRA_QUERIES"] = "github-codeql-extra-queries"; RepositoryPropertyName2["FILE_COVERAGE_ON_PRS"] = "github-codeql-file-coverage-on-prs"; + RepositoryPropertyName2["TOOLS"] = "github-codeql-tools"; return RepositoryPropertyName2; })(RepositoryPropertyName || {}); var KNOWN_REPOSITORY_PROPERTY_NAMES = new Set( diff --git a/lib/init-action-post.js b/lib/init-action-post.js index 27cb1e9b40..36d42f87f1 100644 --- a/lib/init-action-post.js +++ b/lib/init-action-post.js @@ -165431,6 +165431,7 @@ var RepositoryPropertyName = /* @__PURE__ */ ((RepositoryPropertyName2) => { RepositoryPropertyName2["DISABLE_OVERLAY"] = "github-codeql-disable-overlay"; RepositoryPropertyName2["EXTRA_QUERIES"] = "github-codeql-extra-queries"; RepositoryPropertyName2["FILE_COVERAGE_ON_PRS"] = "github-codeql-file-coverage-on-prs"; + RepositoryPropertyName2["TOOLS"] = "github-codeql-tools"; return RepositoryPropertyName2; })(RepositoryPropertyName || {}); var KNOWN_REPOSITORY_PROPERTY_NAMES = new Set( @@ -167044,7 +167045,7 @@ async function findOverridingToolsInCache(humanReadableVersion, logger) { } return void 0; } -async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, variant, tarSupportsZstd, features, logger) { +async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, variant, tarSupportsZstd, features, logger, toolsInputFromRepositoryProperty = false) { if (toolsInput && !isReservedToolsValue(toolsInput) && !toolsInput.startsWith("http")) { logger.info(`Using CodeQL CLI from local path ${toolsInput}`); const compressionMethod2 = inferCompressionMethod(toolsInput); @@ -167090,7 +167091,7 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian ); } else { logger.info( - `Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.` + `Using the latest CodeQL CLI nightly, as requested.` ); } toolsInput = await getNightlyToolsUrl(logger); @@ -167112,11 +167113,18 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian const allowToolcacheValueFF = await features.getValue( "allow_toolcache_input" /* AllowToolcacheInput */ ); - const allowToolcacheValue = allowToolcacheValueFF && (isDynamicWorkflow() || isInTestMode()); + const allowToolcacheValue = toolsInputFromRepositoryProperty || // Repository properties bypass all restrictions + allowToolcacheValueFF && (isDynamicWorkflow() || isInTestMode()); if (allowToolcacheValue) { - logger.info( - `Attempting to use the latest CodeQL CLI version in the toolcache, as requested by 'tools: ${toolsInput}'.` - ); + if (toolsInputFromRepositoryProperty) { + logger.info( + `Attempting to use the latest CodeQL CLI version in the toolcache, as requested by the '${"github-codeql-tools" /* TOOLS */}' repository property.` + ); + } else { + logger.info( + `Attempting to use the latest CodeQL CLI version in the toolcache, as requested.` + ); + } latestToolcacheVersion = getLatestToolcacheVersion(logger); if (latestToolcacheVersion) { cliVersion2 = latestToolcacheVersion; @@ -167124,16 +167132,22 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian } if (latestToolcacheVersion === void 0) { if (allowToolcacheValue) { - logger.info( - `Found no CodeQL CLI in the toolcache, ignoring 'tools: ${toolsInput}'...` - ); + if (toolsInputFromRepositoryProperty) { + logger.info( + `Found no CodeQL CLI in the toolcache, ignoring the '${"github-codeql-tools" /* TOOLS */}' repository property...` + ); + } else { + logger.info( + `Found no CodeQL CLI in the toolcache, ignoring 'tools: ${toolsInput}'...` + ); + } } else { if (allowToolcacheValueFF) { logger.warning( `Ignoring 'tools: ${toolsInput}' because the workflow was not triggered dynamically.` ); } else { - logger.info( + logger.warning( `Ignoring 'tools: ${toolsInput}' because the feature is not enabled.` ); } @@ -167349,7 +167363,7 @@ function getCanonicalToolcacheVersion(cliVersion2, bundleVersion2, logger) { } return cliVersion2; } -async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, features, logger) { +async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, features, logger, toolsInputFromRepositoryProperty = false) { if (!await isBinaryAccessible("tar", logger)) { throw new ConfigurationError( "Could not find tar in PATH, so unable to extract CodeQL bundle." @@ -167363,7 +167377,8 @@ async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defau variant, zstdAvailability.available, features, - logger + logger, + toolsInputFromRepositoryProperty ); let codeqlFolder; let toolsVersion = source.toolsVersion; @@ -170280,20 +170295,33 @@ async function waitForProcessing(repositoryNwo, sarifID, logger, options = { ); break; } + if (!response) { + logger.warning( + "Unable to check analysis status due to missing response. It should still be processed in the background." + ); + break; + } const status = response.data.processing_status; logger.info(`Analysis upload status is ${status}.`); if (status === "pending") { logger.debug("Analysis processing is still pending..."); } else if (options.isUnsuccessfulExecution) { - handleProcessingResultForUnsuccessfulExecution( - response, - status, - logger - ); + if (response) { + handleProcessingResultForUnsuccessfulExecution( + response, + status, + logger + ); + } break; } else if (status === "complete") { break; } else if (status === "failed") { + if (!response) { + throw new Error( + "Code Scanning could not process the submitted SARIF file: Unable to retrieve error details." + ); + } const message = `Code Scanning could not process the submitted SARIF file: ${response.data.errors}`; const processingErrors = response.data.errors; diff --git a/lib/init-action.js b/lib/init-action.js index 625562ed91..2c9d70be0b 100644 --- a/lib/init-action.js +++ b/lib/init-action.js @@ -104077,6 +104077,19 @@ var getOptionalInput = function(name) { const value = core4.getInput(name); return value.length > 0 ? value : void 0; }; +function resolveToolsInput(repositoryProperties, toolsPropertyName, logger) { + const toolsWorkflowInput = getOptionalInput("tools"); + const toolsPropertyValue = repositoryProperties[toolsPropertyName]; + const effectiveToolsInput = toolsWorkflowInput ?? toolsPropertyValue; + if (effectiveToolsInput) { + if (toolsWorkflowInput) { + logger.info(`Setting tools: ${effectiveToolsInput} based on workflow input.`); + } else { + logger.info(`Setting tools: ${effectiveToolsInput} based on the '${toolsPropertyName}' repository property.`); + } + } + return effectiveToolsInput; +} function getTemporaryDirectory() { const value = process.env["CODEQL_ACTION_TEMP"]; return value !== void 0 && value !== "" ? value : getRequiredEnvParam("RUNNER_TEMP"); @@ -104710,6 +104723,7 @@ var RepositoryPropertyName = /* @__PURE__ */ ((RepositoryPropertyName2) => { RepositoryPropertyName2["DISABLE_OVERLAY"] = "github-codeql-disable-overlay"; RepositoryPropertyName2["EXTRA_QUERIES"] = "github-codeql-extra-queries"; RepositoryPropertyName2["FILE_COVERAGE_ON_PRS"] = "github-codeql-file-coverage-on-prs"; + RepositoryPropertyName2["TOOLS"] = "github-codeql-tools"; return RepositoryPropertyName2; })(RepositoryPropertyName || {}); function isString2(value) { @@ -104727,7 +104741,8 @@ var booleanProperty = { var repositoryPropertyParsers = { ["github-codeql-disable-overlay" /* DISABLE_OVERLAY */]: booleanProperty, ["github-codeql-extra-queries" /* EXTRA_QUERIES */]: stringProperty, - ["github-codeql-file-coverage-on-prs" /* FILE_COVERAGE_ON_PRS */]: booleanProperty + ["github-codeql-file-coverage-on-prs" /* FILE_COVERAGE_ON_PRS */]: booleanProperty, + ["github-codeql-tools" /* TOOLS */]: stringProperty }; async function loadPropertiesFromApi(logger, repositoryNwo) { try { @@ -108177,7 +108192,7 @@ async function findOverridingToolsInCache(humanReadableVersion, logger) { } return void 0; } -async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, variant, tarSupportsZstd, features, logger) { +async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, variant, tarSupportsZstd, features, logger, toolsInputFromRepositoryProperty = false) { if (toolsInput && !isReservedToolsValue(toolsInput) && !toolsInput.startsWith("http")) { logger.info(`Using CodeQL CLI from local path ${toolsInput}`); const compressionMethod2 = inferCompressionMethod(toolsInput); @@ -108223,7 +108238,7 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian ); } else { logger.info( - `Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.` + `Using the latest CodeQL CLI nightly, as requested.` ); } toolsInput = await getNightlyToolsUrl(logger); @@ -108245,11 +108260,18 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian const allowToolcacheValueFF = await features.getValue( "allow_toolcache_input" /* AllowToolcacheInput */ ); - const allowToolcacheValue = allowToolcacheValueFF && (isDynamicWorkflow() || isInTestMode()); + const allowToolcacheValue = toolsInputFromRepositoryProperty || // Repository properties bypass all restrictions + allowToolcacheValueFF && (isDynamicWorkflow() || isInTestMode()); if (allowToolcacheValue) { - logger.info( - `Attempting to use the latest CodeQL CLI version in the toolcache, as requested by 'tools: ${toolsInput}'.` - ); + if (toolsInputFromRepositoryProperty) { + logger.info( + `Attempting to use the latest CodeQL CLI version in the toolcache, as requested by the '${"github-codeql-tools" /* TOOLS */}' repository property.` + ); + } else { + logger.info( + `Attempting to use the latest CodeQL CLI version in the toolcache, as requested.` + ); + } latestToolcacheVersion = getLatestToolcacheVersion(logger); if (latestToolcacheVersion) { cliVersion2 = latestToolcacheVersion; @@ -108257,16 +108279,22 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian } if (latestToolcacheVersion === void 0) { if (allowToolcacheValue) { - logger.info( - `Found no CodeQL CLI in the toolcache, ignoring 'tools: ${toolsInput}'...` - ); + if (toolsInputFromRepositoryProperty) { + logger.info( + `Found no CodeQL CLI in the toolcache, ignoring the '${"github-codeql-tools" /* TOOLS */}' repository property...` + ); + } else { + logger.info( + `Found no CodeQL CLI in the toolcache, ignoring 'tools: ${toolsInput}'...` + ); + } } else { if (allowToolcacheValueFF) { logger.warning( `Ignoring 'tools: ${toolsInput}' because the workflow was not triggered dynamically.` ); } else { - logger.info( + logger.warning( `Ignoring 'tools: ${toolsInput}' because the feature is not enabled.` ); } @@ -108482,7 +108510,7 @@ function getCanonicalToolcacheVersion(cliVersion2, bundleVersion2, logger) { } return cliVersion2; } -async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, features, logger) { +async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, features, logger, toolsInputFromRepositoryProperty = false) { if (!await isBinaryAccessible("tar", logger)) { throw new ConfigurationError( "Could not find tar in PATH, so unable to extract CodeQL bundle." @@ -108496,7 +108524,8 @@ async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defau variant, zstdAvailability.available, features, - logger + logger, + toolsInputFromRepositoryProperty ); let codeqlFolder; let toolsVersion = source.toolsVersion; @@ -109931,7 +109960,7 @@ async function sendStartingStatusReport(startedAt, config, logger) { await sendStatusReport(statusReportBase); } } -async function sendCompletedStatusReport(startedAt, config, configFile, toolsDownloadStatusReport, toolsFeatureFlagsValid, toolsSource, toolsVersion, overlayBaseDatabaseStats, dependencyCachingResults, logger, error3) { +async function sendCompletedStatusReport(startedAt, config, configFile, toolsDownloadStatusReport, toolsFeatureFlagsValid, toolsSource, toolsVersion, effectiveToolsInput, overlayBaseDatabaseStats, dependencyCachingResults, logger, error3) { const statusReportBase = await createStatusReportBase( "init" /* Init */, getActionsStatus(error3), @@ -109948,7 +109977,7 @@ async function sendCompletedStatusReport(startedAt, config, configFile, toolsDow const workflowLanguages = getOptionalInput("languages"); const initStatusReport = { ...statusReportBase, - tools_input: getOptionalInput("tools") || "", + tools_input: effectiveToolsInput || "", tools_resolved_version: toolsVersion, tools_source: toolsSource || "UNKNOWN" /* Unknown */, workflow_languages: workflowLanguages || "" @@ -109992,6 +110021,7 @@ async function run(startedAt) { let toolsSource; let toolsVersion; let zstdAvailability; + let effectiveToolsInput; try { initializeEnvironment(getActionVersion()); persistInputs(); @@ -110042,8 +110072,13 @@ async function run(startedAt) { gitHubVersion.type ); toolsFeatureFlagsValid = codeQLDefaultVersionInfo.toolsFeatureFlagsValid; + effectiveToolsInput = resolveToolsInput( + repositoryPropertiesResult.orElse({}), + "github-codeql-tools" /* TOOLS */, + logger + ); const initCodeQLResult = await initCodeQL( - getOptionalInput("tools"), + effectiveToolsInput, apiDetails, getTemporaryDirectory(), gitHubVersion.type, @@ -110384,6 +110419,7 @@ exec ${goBinaryPath} "$@"` toolsFeatureFlagsValid, toolsSource, toolsVersion, + effectiveToolsInput, overlayBaseDatabaseStats, dependencyCachingStatus, logger, @@ -110401,6 +110437,7 @@ exec ${goBinaryPath} "$@"` toolsFeatureFlagsValid, toolsSource, toolsVersion, + effectiveToolsInput, overlayBaseDatabaseStats, dependencyCachingStatus, logger diff --git a/lib/resolve-environment-action.js b/lib/resolve-environment-action.js index df13d019d4..f4abd56335 100644 --- a/lib/resolve-environment-action.js +++ b/lib/resolve-environment-action.js @@ -104088,6 +104088,7 @@ var RepositoryPropertyName = /* @__PURE__ */ ((RepositoryPropertyName2) => { RepositoryPropertyName2["DISABLE_OVERLAY"] = "github-codeql-disable-overlay"; RepositoryPropertyName2["EXTRA_QUERIES"] = "github-codeql-extra-queries"; RepositoryPropertyName2["FILE_COVERAGE_ON_PRS"] = "github-codeql-file-coverage-on-prs"; + RepositoryPropertyName2["TOOLS"] = "github-codeql-tools"; return RepositoryPropertyName2; })(RepositoryPropertyName || {}); var KNOWN_REPOSITORY_PROPERTY_NAMES = new Set( diff --git a/lib/setup-codeql-action.js b/lib/setup-codeql-action.js index e710db4911..7784118bba 100644 --- a/lib/setup-codeql-action.js +++ b/lib/setup-codeql-action.js @@ -100236,7 +100236,7 @@ var require_follow_redirects = __commonJS({ if (this._ending) { throw new WriteAfterEndError(); } - if (!isString2(data) && !isBuffer(data)) { + if (!isString3(data) && !isBuffer(data)) { throw new TypeError("data should be a string, Buffer or Uint8Array"); } if (isFunction(encoding)) { @@ -100491,7 +100491,7 @@ var require_follow_redirects = __commonJS({ function request2(input, options, callback) { if (isURL(input)) { input = spreadUrlObject(input); - } else if (isString2(input)) { + } else if (isString3(input)) { input = spreadUrlObject(parseUrl2(input)); } else { callback = options; @@ -100507,7 +100507,7 @@ var require_follow_redirects = __commonJS({ maxBodyLength: exports3.maxBodyLength }, input, options); options.nativeProtocols = nativeProtocols; - if (!isString2(options.host) && !isString2(options.hostname)) { + if (!isString3(options.host) && !isString3(options.hostname)) { options.hostname = "::1"; } assert.equal(options.protocol, protocol, "protocol mismatch"); @@ -100534,7 +100534,7 @@ var require_follow_redirects = __commonJS({ parsed = new URL2(input); } else { parsed = validateUrl(url.parse(input)); - if (!isString2(parsed.protocol)) { + if (!isString3(parsed.protocol)) { throw new InvalidUrlError({ input }); } } @@ -100606,11 +100606,11 @@ var require_follow_redirects = __commonJS({ request2.destroy(error3); } function isSubdomain(subdomain, domain) { - assert(isString2(subdomain) && isString2(domain)); + assert(isString3(subdomain) && isString3(domain)); var dot = subdomain.length - domain.length - 1; return dot > 0 && subdomain[dot] === "." && subdomain.endsWith(domain); } - function isString2(value) { + function isString3(value) { return typeof value === "string" || value instanceof String; } function isFunction(value) { @@ -103620,6 +103620,19 @@ var getOptionalInput = function(name) { const value = core4.getInput(name); return value.length > 0 ? value : void 0; }; +function resolveToolsInput(repositoryProperties, toolsPropertyName, logger) { + const toolsWorkflowInput = getOptionalInput("tools"); + const toolsPropertyValue = repositoryProperties[toolsPropertyName]; + const effectiveToolsInput = toolsWorkflowInput ?? toolsPropertyValue; + if (effectiveToolsInput) { + if (toolsWorkflowInput) { + logger.info(`Setting tools: ${effectiveToolsInput} based on workflow input.`); + } else { + logger.info(`Setting tools: ${effectiveToolsInput} based on the '${toolsPropertyName}' repository property.`); + } + } + return effectiveToolsInput; +} function getTemporaryDirectory() { const value = process.env["CODEQL_ACTION_TEMP"]; return value !== void 0 && value !== "" ? value : getRequiredEnvParam("RUNNER_TEMP"); @@ -103936,6 +103949,12 @@ async function getAnalysisKey() { core5.exportVariable("CODEQL_ACTION_ANALYSIS_KEY" /* ANALYSIS_KEY */, analysisKey); return analysisKey; } +async function getRepositoryProperties(repositoryNwo) { + return getApiClient().request("GET /repos/:owner/:repo/properties/values", { + owner: repositoryNwo.owner, + repo: repositoryNwo.repo + }); +} function isEnablementError(msg) { return [ /Code Security must be enabled/i, @@ -104794,6 +104813,112 @@ function initFeatures(gitHubVersion, repositoryNwo, tempDir, logger) { } } +// src/feature-flags/properties.ts +var GITHUB_CODEQL_PROPERTY_PREFIX = "github-codeql-"; +var RepositoryPropertyName = /* @__PURE__ */ ((RepositoryPropertyName2) => { + RepositoryPropertyName2["DISABLE_OVERLAY"] = "github-codeql-disable-overlay"; + RepositoryPropertyName2["EXTRA_QUERIES"] = "github-codeql-extra-queries"; + RepositoryPropertyName2["FILE_COVERAGE_ON_PRS"] = "github-codeql-file-coverage-on-prs"; + RepositoryPropertyName2["TOOLS"] = "github-codeql-tools"; + return RepositoryPropertyName2; +})(RepositoryPropertyName || {}); +function isString2(value) { + return typeof value === "string"; +} +var stringProperty = { + validate: isString2, + parse: parseStringRepositoryProperty +}; +var booleanProperty = { + // The value from the API should come as a string, which we then parse into a boolean. + validate: isString2, + parse: parseBooleanRepositoryProperty +}; +var repositoryPropertyParsers = { + ["github-codeql-disable-overlay" /* DISABLE_OVERLAY */]: booleanProperty, + ["github-codeql-extra-queries" /* EXTRA_QUERIES */]: stringProperty, + ["github-codeql-file-coverage-on-prs" /* FILE_COVERAGE_ON_PRS */]: booleanProperty, + ["github-codeql-tools" /* TOOLS */]: stringProperty +}; +async function loadPropertiesFromApi(logger, repositoryNwo) { + try { + const response = await getRepositoryProperties(repositoryNwo); + const remoteProperties = response.data; + if (!Array.isArray(remoteProperties)) { + throw new Error( + `Expected repository properties API to return an array, but got: ${JSON.stringify(response.data)}` + ); + } + logger.debug( + `Retrieved ${remoteProperties.length} repository properties: ${remoteProperties.map((p) => p.property_name).join(", ")}` + ); + const properties = {}; + const unrecognisedProperties = []; + for (const property of remoteProperties) { + if (property.property_name === void 0) { + throw new Error( + `Expected repository property object to have a 'property_name', but got: ${JSON.stringify(property)}` + ); + } + if (isKnownPropertyName(property.property_name)) { + setProperty2(properties, property.property_name, property.value, logger); + } else if (property.property_name.startsWith(GITHUB_CODEQL_PROPERTY_PREFIX) && !isDynamicWorkflow()) { + unrecognisedProperties.push(property.property_name); + } + } + if (Object.keys(properties).length === 0) { + logger.debug("No known repository properties were found."); + } else { + logger.debug( + "Loaded the following values for the repository properties:" + ); + for (const [property, value] of Object.entries(properties).sort( + ([nameA], [nameB]) => nameA.localeCompare(nameB) + )) { + logger.debug(` ${property}: ${value}`); + } + } + if (unrecognisedProperties.length > 0) { + const unrecognisedPropertyList = unrecognisedProperties.map((name) => `'${name}'`).join(", "); + logger.warning( + `Found repository properties (${unrecognisedPropertyList}), which look like CodeQL Action repository properties, but which are not understood by this version of the CodeQL Action. Do you need to update to a newer version?` + ); + } + return properties; + } catch (e) { + throw new Error( + `Encountered an error while trying to determine repository properties: ${e}` + ); + } +} +function setProperty2(properties, name, value, logger) { + const propertyOptions = repositoryPropertyParsers[name]; + if (propertyOptions.validate(value)) { + properties[name] = propertyOptions.parse(name, value, logger); + } else { + throw new Error( + `Unexpected value for repository property '${name}' (${typeof value}), got: ${JSON.stringify(value)}` + ); + } +} +function parseBooleanRepositoryProperty(name, value, logger) { + if (value !== "true" && value !== "false") { + logger.warning( + `Repository property '${name}' has unexpected value '${value}'. Expected 'true' or 'false'. Defaulting to false.` + ); + } + return value === "true"; +} +function parseStringRepositoryProperty(_name, value) { + return value; +} +var KNOWN_REPOSITORY_PROPERTY_NAMES = new Set( + Object.values(RepositoryPropertyName) +); +function isKnownPropertyName(name) { + return KNOWN_REPOSITORY_PROPERTY_NAMES.has(name); +} + // src/init.ts var core12 = __toESM(require_core()); var toolrunner4 = __toESM(require_toolrunner()); @@ -105063,19 +105188,6 @@ var supportedAnalysisKinds = new Set(Object.values(AnalysisKind)); // src/config/db-config.ts var jsonschema = __toESM(require_lib2()); var semver5 = __toESM(require_semver2()); - -// src/feature-flags/properties.ts -var RepositoryPropertyName = /* @__PURE__ */ ((RepositoryPropertyName2) => { - RepositoryPropertyName2["DISABLE_OVERLAY"] = "github-codeql-disable-overlay"; - RepositoryPropertyName2["EXTRA_QUERIES"] = "github-codeql-extra-queries"; - RepositoryPropertyName2["FILE_COVERAGE_ON_PRS"] = "github-codeql-file-coverage-on-prs"; - return RepositoryPropertyName2; -})(RepositoryPropertyName || {}); -var KNOWN_REPOSITORY_PROPERTY_NAMES = new Set( - Object.values(RepositoryPropertyName) -); - -// src/config/db-config.ts var PACK_IDENTIFIER_PATTERN = (function() { const alphaNumeric = "[a-z0-9]"; const alphaNumericDash = "[a-z0-9-]"; @@ -105658,7 +105770,7 @@ async function findOverridingToolsInCache(humanReadableVersion, logger) { } return void 0; } -async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, variant, tarSupportsZstd, features, logger) { +async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, variant, tarSupportsZstd, features, logger, toolsInputFromRepositoryProperty = false) { if (toolsInput && !isReservedToolsValue(toolsInput) && !toolsInput.startsWith("http")) { logger.info(`Using CodeQL CLI from local path ${toolsInput}`); const compressionMethod2 = inferCompressionMethod(toolsInput); @@ -105704,7 +105816,7 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian ); } else { logger.info( - `Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.` + `Using the latest CodeQL CLI nightly, as requested.` ); } toolsInput = await getNightlyToolsUrl(logger); @@ -105726,11 +105838,18 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian const allowToolcacheValueFF = await features.getValue( "allow_toolcache_input" /* AllowToolcacheInput */ ); - const allowToolcacheValue = allowToolcacheValueFF && (isDynamicWorkflow() || isInTestMode()); + const allowToolcacheValue = toolsInputFromRepositoryProperty || // Repository properties bypass all restrictions + allowToolcacheValueFF && (isDynamicWorkflow() || isInTestMode()); if (allowToolcacheValue) { - logger.info( - `Attempting to use the latest CodeQL CLI version in the toolcache, as requested by 'tools: ${toolsInput}'.` - ); + if (toolsInputFromRepositoryProperty) { + logger.info( + `Attempting to use the latest CodeQL CLI version in the toolcache, as requested by the '${"github-codeql-tools" /* TOOLS */}' repository property.` + ); + } else { + logger.info( + `Attempting to use the latest CodeQL CLI version in the toolcache, as requested.` + ); + } latestToolcacheVersion = getLatestToolcacheVersion(logger); if (latestToolcacheVersion) { cliVersion2 = latestToolcacheVersion; @@ -105738,16 +105857,22 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian } if (latestToolcacheVersion === void 0) { if (allowToolcacheValue) { - logger.info( - `Found no CodeQL CLI in the toolcache, ignoring 'tools: ${toolsInput}'...` - ); + if (toolsInputFromRepositoryProperty) { + logger.info( + `Found no CodeQL CLI in the toolcache, ignoring the '${"github-codeql-tools" /* TOOLS */}' repository property...` + ); + } else { + logger.info( + `Found no CodeQL CLI in the toolcache, ignoring 'tools: ${toolsInput}'...` + ); + } } else { if (allowToolcacheValueFF) { logger.warning( `Ignoring 'tools: ${toolsInput}' because the workflow was not triggered dynamically.` ); } else { - logger.info( + logger.warning( `Ignoring 'tools: ${toolsInput}' because the feature is not enabled.` ); } @@ -105963,7 +106088,7 @@ function getCanonicalToolcacheVersion(cliVersion2, bundleVersion2, logger) { } return cliVersion2; } -async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, features, logger) { +async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, features, logger, toolsInputFromRepositoryProperty = false) { if (!await isBinaryAccessible("tar", logger)) { throw new ConfigurationError( "Could not find tar in PATH, so unable to extract CodeQL bundle." @@ -105977,7 +106102,8 @@ async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defau variant, zstdAvailability.available, features, - logger + logger, + toolsInputFromRepositoryProperty ); let codeqlFolder; let toolsVersion = source.toolsVersion; @@ -106940,7 +107066,7 @@ async function sendUnhandledErrorStatusReport(actionName, actionStartedAt, error } // src/setup-codeql-action.ts -async function sendCompletedStatusReport(startedAt, toolsDownloadStatusReport, toolsFeatureFlagsValid, toolsSource, toolsVersion, logger, error3) { +async function sendCompletedStatusReport(startedAt, toolsDownloadStatusReport, toolsFeatureFlagsValid, toolsSource, toolsVersion, effectiveToolsInput, logger, error3) { const statusReportBase = await createStatusReportBase( "setup-codeql" /* SetupCodeQL */, getActionsStatus(error3), @@ -106956,7 +107082,7 @@ async function sendCompletedStatusReport(startedAt, toolsDownloadStatusReport, t } const initStatusReport = { ...statusReportBase, - tools_input: getOptionalInput("tools") || "", + tools_input: effectiveToolsInput || "", tools_resolved_version: toolsVersion, tools_source: toolsSource || "UNKNOWN" /* Unknown */, workflow_languages: "" @@ -106977,6 +107103,7 @@ async function run(startedAt) { let toolsFeatureFlagsValid; let toolsSource; let toolsVersion; + let effectiveToolsInput; try { initializeEnvironment(getActionVersion()); const apiDetails = { @@ -106995,6 +107122,10 @@ async function run(startedAt) { getTemporaryDirectory(), logger ); + const repositoryPropertiesResult = await loadPropertiesFromApi( + logger, + repositoryNwo + ); const jobRunUuid = v4_default(); logger.info(`Job run UUID is ${jobRunUuid}.`); core14.exportVariable("JOB_RUN_UUID" /* JOB_RUN_UUID */, jobRunUuid); @@ -107013,8 +107144,13 @@ async function run(startedAt) { gitHubVersion.type ); toolsFeatureFlagsValid = codeQLDefaultVersionInfo.toolsFeatureFlagsValid; + effectiveToolsInput = resolveToolsInput( + repositoryPropertiesResult, + "github-codeql-tools" /* TOOLS */, + logger + ); const initCodeQLResult = await initCodeQL( - getOptionalInput("tools"), + effectiveToolsInput, apiDetails, getTemporaryDirectory(), gitHubVersion.type, @@ -107053,6 +107189,7 @@ async function run(startedAt) { toolsFeatureFlagsValid, toolsSource, toolsVersion, + effectiveToolsInput, logger ); } diff --git a/lib/start-proxy-action-post.js b/lib/start-proxy-action-post.js index 0160eecbd9..af44aea8b9 100644 --- a/lib/start-proxy-action-post.js +++ b/lib/start-proxy-action-post.js @@ -161668,6 +161668,7 @@ var RepositoryPropertyName = /* @__PURE__ */ ((RepositoryPropertyName2) => { RepositoryPropertyName2["DISABLE_OVERLAY"] = "github-codeql-disable-overlay"; RepositoryPropertyName2["EXTRA_QUERIES"] = "github-codeql-extra-queries"; RepositoryPropertyName2["FILE_COVERAGE_ON_PRS"] = "github-codeql-file-coverage-on-prs"; + RepositoryPropertyName2["TOOLS"] = "github-codeql-tools"; return RepositoryPropertyName2; })(RepositoryPropertyName || {}); var KNOWN_REPOSITORY_PROPERTY_NAMES = new Set( diff --git a/lib/start-proxy-action.js b/lib/start-proxy-action.js index a1b8a027f8..4a26b93a71 100644 --- a/lib/start-proxy-action.js +++ b/lib/start-proxy-action.js @@ -121774,6 +121774,7 @@ var RepositoryPropertyName = /* @__PURE__ */ ((RepositoryPropertyName2) => { RepositoryPropertyName2["DISABLE_OVERLAY"] = "github-codeql-disable-overlay"; RepositoryPropertyName2["EXTRA_QUERIES"] = "github-codeql-extra-queries"; RepositoryPropertyName2["FILE_COVERAGE_ON_PRS"] = "github-codeql-file-coverage-on-prs"; + RepositoryPropertyName2["TOOLS"] = "github-codeql-tools"; return RepositoryPropertyName2; })(RepositoryPropertyName || {}); var KNOWN_REPOSITORY_PROPERTY_NAMES = new Set( diff --git a/lib/upload-lib.js b/lib/upload-lib.js index 485021d43d..fb8c05c041 100644 --- a/lib/upload-lib.js +++ b/lib/upload-lib.js @@ -107132,6 +107132,7 @@ var RepositoryPropertyName = /* @__PURE__ */ ((RepositoryPropertyName2) => { RepositoryPropertyName2["DISABLE_OVERLAY"] = "github-codeql-disable-overlay"; RepositoryPropertyName2["EXTRA_QUERIES"] = "github-codeql-extra-queries"; RepositoryPropertyName2["FILE_COVERAGE_ON_PRS"] = "github-codeql-file-coverage-on-prs"; + RepositoryPropertyName2["TOOLS"] = "github-codeql-tools"; return RepositoryPropertyName2; })(RepositoryPropertyName || {}); var KNOWN_REPOSITORY_PROPERTY_NAMES = new Set( @@ -108347,7 +108348,7 @@ async function findOverridingToolsInCache(humanReadableVersion, logger) { } return void 0; } -async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, variant, tarSupportsZstd, features, logger) { +async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, variant, tarSupportsZstd, features, logger, toolsInputFromRepositoryProperty = false) { if (toolsInput && !isReservedToolsValue(toolsInput) && !toolsInput.startsWith("http")) { logger.info(`Using CodeQL CLI from local path ${toolsInput}`); const compressionMethod2 = inferCompressionMethod(toolsInput); @@ -108393,7 +108394,7 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian ); } else { logger.info( - `Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.` + `Using the latest CodeQL CLI nightly, as requested.` ); } toolsInput = await getNightlyToolsUrl(logger); @@ -108415,11 +108416,18 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian const allowToolcacheValueFF = await features.getValue( "allow_toolcache_input" /* AllowToolcacheInput */ ); - const allowToolcacheValue = allowToolcacheValueFF && (isDynamicWorkflow() || isInTestMode()); + const allowToolcacheValue = toolsInputFromRepositoryProperty || // Repository properties bypass all restrictions + allowToolcacheValueFF && (isDynamicWorkflow() || isInTestMode()); if (allowToolcacheValue) { - logger.info( - `Attempting to use the latest CodeQL CLI version in the toolcache, as requested by 'tools: ${toolsInput}'.` - ); + if (toolsInputFromRepositoryProperty) { + logger.info( + `Attempting to use the latest CodeQL CLI version in the toolcache, as requested by the '${"github-codeql-tools" /* TOOLS */}' repository property.` + ); + } else { + logger.info( + `Attempting to use the latest CodeQL CLI version in the toolcache, as requested.` + ); + } latestToolcacheVersion = getLatestToolcacheVersion(logger); if (latestToolcacheVersion) { cliVersion2 = latestToolcacheVersion; @@ -108427,16 +108435,22 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian } if (latestToolcacheVersion === void 0) { if (allowToolcacheValue) { - logger.info( - `Found no CodeQL CLI in the toolcache, ignoring 'tools: ${toolsInput}'...` - ); + if (toolsInputFromRepositoryProperty) { + logger.info( + `Found no CodeQL CLI in the toolcache, ignoring the '${"github-codeql-tools" /* TOOLS */}' repository property...` + ); + } else { + logger.info( + `Found no CodeQL CLI in the toolcache, ignoring 'tools: ${toolsInput}'...` + ); + } } else { if (allowToolcacheValueFF) { logger.warning( `Ignoring 'tools: ${toolsInput}' because the workflow was not triggered dynamically.` ); } else { - logger.info( + logger.warning( `Ignoring 'tools: ${toolsInput}' because the feature is not enabled.` ); } @@ -108652,7 +108666,7 @@ function getCanonicalToolcacheVersion(cliVersion2, bundleVersion2, logger) { } return cliVersion2; } -async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, features, logger) { +async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, features, logger, toolsInputFromRepositoryProperty = false) { if (!await isBinaryAccessible("tar", logger)) { throw new ConfigurationError( "Could not find tar in PATH, so unable to extract CodeQL bundle." @@ -108666,7 +108680,8 @@ async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defau variant, zstdAvailability.available, features, - logger + logger, + toolsInputFromRepositoryProperty ); let codeqlFolder; let toolsVersion = source.toolsVersion; @@ -111124,20 +111139,33 @@ async function waitForProcessing(repositoryNwo, sarifID, logger, options = { ); break; } + if (!response) { + logger.warning( + "Unable to check analysis status due to missing response. It should still be processed in the background." + ); + break; + } const status = response.data.processing_status; logger.info(`Analysis upload status is ${status}.`); if (status === "pending") { logger.debug("Analysis processing is still pending..."); } else if (options.isUnsuccessfulExecution) { - handleProcessingResultForUnsuccessfulExecution( - response, - status, - logger - ); + if (response) { + handleProcessingResultForUnsuccessfulExecution( + response, + status, + logger + ); + } break; } else if (status === "complete") { break; } else if (status === "failed") { + if (!response) { + throw new Error( + "Code Scanning could not process the submitted SARIF file: Unable to retrieve error details." + ); + } const message = `Code Scanning could not process the submitted SARIF file: ${response.data.errors}`; const processingErrors = response.data.errors; diff --git a/lib/upload-sarif-action-post.js b/lib/upload-sarif-action-post.js index 74a5b57fd9..a8f4d79cc1 100644 --- a/lib/upload-sarif-action-post.js +++ b/lib/upload-sarif-action-post.js @@ -161820,6 +161820,7 @@ var RepositoryPropertyName = /* @__PURE__ */ ((RepositoryPropertyName2) => { RepositoryPropertyName2["DISABLE_OVERLAY"] = "github-codeql-disable-overlay"; RepositoryPropertyName2["EXTRA_QUERIES"] = "github-codeql-extra-queries"; RepositoryPropertyName2["FILE_COVERAGE_ON_PRS"] = "github-codeql-file-coverage-on-prs"; + RepositoryPropertyName2["TOOLS"] = "github-codeql-tools"; return RepositoryPropertyName2; })(RepositoryPropertyName || {}); var KNOWN_REPOSITORY_PROPERTY_NAMES = new Set( diff --git a/lib/upload-sarif-action.js b/lib/upload-sarif-action.js index 484025f829..0cf5e88cb6 100644 --- a/lib/upload-sarif-action.js +++ b/lib/upload-sarif-action.js @@ -107858,6 +107858,7 @@ var RepositoryPropertyName = /* @__PURE__ */ ((RepositoryPropertyName2) => { RepositoryPropertyName2["DISABLE_OVERLAY"] = "github-codeql-disable-overlay"; RepositoryPropertyName2["EXTRA_QUERIES"] = "github-codeql-extra-queries"; RepositoryPropertyName2["FILE_COVERAGE_ON_PRS"] = "github-codeql-file-coverage-on-prs"; + RepositoryPropertyName2["TOOLS"] = "github-codeql-tools"; return RepositoryPropertyName2; })(RepositoryPropertyName || {}); var KNOWN_REPOSITORY_PROPERTY_NAMES = new Set( @@ -109009,7 +109010,7 @@ async function findOverridingToolsInCache(humanReadableVersion, logger) { } return void 0; } -async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, variant, tarSupportsZstd, features, logger) { +async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, variant, tarSupportsZstd, features, logger, toolsInputFromRepositoryProperty = false) { if (toolsInput && !isReservedToolsValue(toolsInput) && !toolsInput.startsWith("http")) { logger.info(`Using CodeQL CLI from local path ${toolsInput}`); const compressionMethod2 = inferCompressionMethod(toolsInput); @@ -109055,7 +109056,7 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian ); } else { logger.info( - `Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.` + `Using the latest CodeQL CLI nightly, as requested.` ); } toolsInput = await getNightlyToolsUrl(logger); @@ -109077,11 +109078,18 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian const allowToolcacheValueFF = await features.getValue( "allow_toolcache_input" /* AllowToolcacheInput */ ); - const allowToolcacheValue = allowToolcacheValueFF && (isDynamicWorkflow() || isInTestMode()); + const allowToolcacheValue = toolsInputFromRepositoryProperty || // Repository properties bypass all restrictions + allowToolcacheValueFF && (isDynamicWorkflow() || isInTestMode()); if (allowToolcacheValue) { - logger.info( - `Attempting to use the latest CodeQL CLI version in the toolcache, as requested by 'tools: ${toolsInput}'.` - ); + if (toolsInputFromRepositoryProperty) { + logger.info( + `Attempting to use the latest CodeQL CLI version in the toolcache, as requested by the '${"github-codeql-tools" /* TOOLS */}' repository property.` + ); + } else { + logger.info( + `Attempting to use the latest CodeQL CLI version in the toolcache, as requested.` + ); + } latestToolcacheVersion = getLatestToolcacheVersion(logger); if (latestToolcacheVersion) { cliVersion2 = latestToolcacheVersion; @@ -109089,16 +109097,22 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian } if (latestToolcacheVersion === void 0) { if (allowToolcacheValue) { - logger.info( - `Found no CodeQL CLI in the toolcache, ignoring 'tools: ${toolsInput}'...` - ); + if (toolsInputFromRepositoryProperty) { + logger.info( + `Found no CodeQL CLI in the toolcache, ignoring the '${"github-codeql-tools" /* TOOLS */}' repository property...` + ); + } else { + logger.info( + `Found no CodeQL CLI in the toolcache, ignoring 'tools: ${toolsInput}'...` + ); + } } else { if (allowToolcacheValueFF) { logger.warning( `Ignoring 'tools: ${toolsInput}' because the workflow was not triggered dynamically.` ); } else { - logger.info( + logger.warning( `Ignoring 'tools: ${toolsInput}' because the feature is not enabled.` ); } @@ -109314,7 +109328,7 @@ function getCanonicalToolcacheVersion(cliVersion2, bundleVersion2, logger) { } return cliVersion2; } -async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, features, logger) { +async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, features, logger, toolsInputFromRepositoryProperty = false) { if (!await isBinaryAccessible("tar", logger)) { throw new ConfigurationError( "Could not find tar in PATH, so unable to extract CodeQL bundle." @@ -109328,7 +109342,8 @@ async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defau variant, zstdAvailability.available, features, - logger + logger, + toolsInputFromRepositoryProperty ); let codeqlFolder; let toolsVersion = source.toolsVersion; @@ -111668,20 +111683,33 @@ async function waitForProcessing(repositoryNwo, sarifID, logger, options = { ); break; } + if (!response) { + logger.warning( + "Unable to check analysis status due to missing response. It should still be processed in the background." + ); + break; + } const status = response.data.processing_status; logger.info(`Analysis upload status is ${status}.`); if (status === "pending") { logger.debug("Analysis processing is still pending..."); } else if (options.isUnsuccessfulExecution) { - handleProcessingResultForUnsuccessfulExecution( - response, - status, - logger - ); + if (response) { + handleProcessingResultForUnsuccessfulExecution( + response, + status, + logger + ); + } break; } else if (status === "complete") { break; } else if (status === "failed") { + if (!response) { + throw new Error( + "Code Scanning could not process the submitted SARIF file: Unable to retrieve error details." + ); + } const message = `Code Scanning could not process the submitted SARIF file: ${response.data.errors}`; const processingErrors = response.data.errors; diff --git a/src/actions-util.ts b/src/actions-util.ts index e1a7adb8f0..da80b232c2 100644 --- a/src/actions-util.ts +++ b/src/actions-util.ts @@ -46,6 +46,38 @@ export const getOptionalInput = function (name: string): string | undefined { return value.length > 0 ? value : undefined; }; +/** + * Resolves the effective tools input by combining workflow input and repository properties. + * The explicit `tools` workflow input takes precedence. If none is provided, + * fall back to the repository property (if set). + * + * @param repositoryProperties - The loaded repository properties object + * @param toolsPropertyName - The name of the tools property to look up + * @param logger - Logger for outputting resolution messages + * @returns The effective tools input value + */ +export function resolveToolsInput( + repositoryProperties: Record, + toolsPropertyName: string, + logger: Logger, +): string | undefined { + const toolsWorkflowInput = getOptionalInput("tools"); + const toolsPropertyValue: string | undefined = + repositoryProperties[toolsPropertyName]; + const effectiveToolsInput = toolsWorkflowInput ?? toolsPropertyValue; + + // Log the source of the tools input for transparency + if (effectiveToolsInput) { + if (toolsWorkflowInput) { + logger.info(`Setting tools: ${effectiveToolsInput} based on workflow input.`); + } else { + logger.info(`Setting tools: ${effectiveToolsInput} based on the '${toolsPropertyName}' repository property.`); + } + } + + return effectiveToolsInput; +} + export function getTemporaryDirectory(): string { const value = process.env["CODEQL_ACTION_TEMP"]; return value !== undefined && value !== "" diff --git a/src/feature-flags/properties.test.ts b/src/feature-flags/properties.test.ts index 2676c2dcda..2bb9e10e4f 100644 --- a/src/feature-flags/properties.test.ts +++ b/src/feature-flags/properties.test.ts @@ -78,6 +78,7 @@ test.serial("loadPropertiesFromApi loads known properties", async (t) => { url: "", data: [ { property_name: "github-codeql-extra-queries", value: "+queries" }, + { property_name: "github-codeql-tools", value: "toolcache" }, { property_name: "unknown-property", value: "something" }, ] satisfies properties.GitHubPropertiesResponse, }); @@ -87,7 +88,10 @@ test.serial("loadPropertiesFromApi loads known properties", async (t) => { logger, mockRepositoryNwo, ); - t.deepEqual(response, { "github-codeql-extra-queries": "+queries" }); + t.deepEqual(response, { + "github-codeql-extra-queries": "+queries", + "github-codeql-tools": "toolcache", + }); }); test.serial("loadPropertiesFromApi parses true boolean property", async (t) => { diff --git a/src/feature-flags/properties.ts b/src/feature-flags/properties.ts index 12ba280bec..4fb929b53f 100644 --- a/src/feature-flags/properties.ts +++ b/src/feature-flags/properties.ts @@ -13,6 +13,7 @@ export enum RepositoryPropertyName { DISABLE_OVERLAY = "github-codeql-disable-overlay", EXTRA_QUERIES = "github-codeql-extra-queries", FILE_COVERAGE_ON_PRS = "github-codeql-file-coverage-on-prs", + TOOLS = "github-codeql-tools", } /** Parsed types of the known repository properties. */ @@ -20,6 +21,7 @@ export type AllRepositoryProperties = { [RepositoryPropertyName.DISABLE_OVERLAY]: boolean; [RepositoryPropertyName.EXTRA_QUERIES]: string; [RepositoryPropertyName.FILE_COVERAGE_ON_PRS]: boolean; + [RepositoryPropertyName.TOOLS]: string; }; /** Parsed repository properties. */ @@ -30,6 +32,7 @@ export type RepositoryPropertyApiType = { [RepositoryPropertyName.DISABLE_OVERLAY]: string; [RepositoryPropertyName.EXTRA_QUERIES]: string; [RepositoryPropertyName.FILE_COVERAGE_ON_PRS]: string; + [RepositoryPropertyName.TOOLS]: string; }; /** The type of functions which take the `value` from the API and try to convert it to the type we want. */ @@ -77,6 +80,7 @@ const repositoryPropertyParsers: { [RepositoryPropertyName.DISABLE_OVERLAY]: booleanProperty, [RepositoryPropertyName.EXTRA_QUERIES]: stringProperty, [RepositoryPropertyName.FILE_COVERAGE_ON_PRS]: booleanProperty, + [RepositoryPropertyName.TOOLS]: stringProperty, }; /** diff --git a/src/init-action.ts b/src/init-action.ts index 6fe89165bd..223f92b074 100644 --- a/src/init-action.ts +++ b/src/init-action.ts @@ -15,6 +15,7 @@ import { getRequiredInput, getTemporaryDirectory, persistInputs, + resolveToolsInput, } from "./actions-util"; import { AnalysisKind, getAnalysisKinds } from "./analyses"; import { getGitHubVersion, GitHubApiCombinedDetails } from "./api-client"; @@ -42,6 +43,7 @@ import { Feature, FeatureEnablement, initFeatures } from "./feature-flags"; import { loadPropertiesFromApi, RepositoryProperties, + RepositoryPropertyName, } from "./feature-flags/properties"; import { checkInstallPython311, @@ -140,6 +142,7 @@ async function sendCompletedStatusReport( toolsFeatureFlagsValid: boolean | undefined, toolsSource: ToolsSource, toolsVersion: string, + effectiveToolsInput: string | undefined, overlayBaseDatabaseStats: OverlayBaseDatabaseDownloadStats | undefined, dependencyCachingResults: DependencyCacheRestoreStatusReport | undefined, logger: Logger, @@ -164,7 +167,7 @@ async function sendCompletedStatusReport( const initStatusReport: InitStatusReport = { ...statusReportBase, - tools_input: getOptionalInput("tools") || "", + tools_input: effectiveToolsInput || "", tools_resolved_version: toolsVersion, tools_source: toolsSource || ToolsSource.Unknown, workflow_languages: workflowLanguages || "", @@ -219,6 +222,7 @@ async function run(startedAt: Date) { let toolsSource: ToolsSource; let toolsVersion: string; let zstdAvailability: ZstdAvailability | undefined; + let effectiveToolsInput: string | undefined; try { initializeEnvironment(getActionVersion()); @@ -297,8 +301,18 @@ async function run(startedAt: Date) { gitHubVersion.type, ); toolsFeatureFlagsValid = codeQLDefaultVersionInfo.toolsFeatureFlagsValid; + + // Determine the effective tools input. + // The explicit `tools` workflow input takes precedence. If none is provided, + // fall back to the 'github-codeql-tools' repository property (if set). + effectiveToolsInput = resolveToolsInput( + repositoryPropertiesResult.orElse({}), + RepositoryPropertyName.TOOLS, + logger, + ); + const initCodeQLResult = await initCodeQL( - getOptionalInput("tools"), + effectiveToolsInput, apiDetails, getTemporaryDirectory(), gitHubVersion.type, @@ -778,6 +792,7 @@ async function run(startedAt: Date) { toolsFeatureFlagsValid, toolsSource, toolsVersion, + effectiveToolsInput, overlayBaseDatabaseStats, dependencyCachingStatus, logger, @@ -795,6 +810,7 @@ async function run(startedAt: Date) { toolsFeatureFlagsValid, toolsSource, toolsVersion, + effectiveToolsInput, overlayBaseDatabaseStats, dependencyCachingStatus, logger, diff --git a/src/setup-codeql-action.ts b/src/setup-codeql-action.ts index bd504f3fd3..535528f428 100644 --- a/src/setup-codeql-action.ts +++ b/src/setup-codeql-action.ts @@ -6,11 +6,16 @@ import { getOptionalInput, getRequiredInput, getTemporaryDirectory, + resolveToolsInput, } from "./actions-util"; import { getGitHubVersion } from "./api-client"; import { CodeQL } from "./codeql"; import { EnvVar } from "./environment"; import { initFeatures } from "./feature-flags"; +import { + loadPropertiesFromApi, + RepositoryPropertyName, +} from "./feature-flags/properties"; import { initCodeQL } from "./init"; import { getActionsLogger, Logger } from "./logging"; import { getRepositoryNwo } from "./repository"; @@ -46,6 +51,7 @@ async function sendCompletedStatusReport( toolsFeatureFlagsValid: boolean | undefined, toolsSource: ToolsSource, toolsVersion: string, + effectiveToolsInput: string | undefined, logger: Logger, error?: Error, ): Promise { @@ -66,7 +72,7 @@ async function sendCompletedStatusReport( const initStatusReport: InitStatusReport = { ...statusReportBase, - tools_input: getOptionalInput("tools") || "", + tools_input: effectiveToolsInput || "", tools_resolved_version: toolsVersion, tools_source: toolsSource || ToolsSource.Unknown, workflow_languages: "", @@ -97,6 +103,7 @@ async function run(startedAt: Date): Promise { let toolsFeatureFlagsValid: boolean | undefined; let toolsSource: ToolsSource; let toolsVersion: string; + let effectiveToolsInput: string | undefined; try { initializeEnvironment(getActionVersion()); @@ -121,6 +128,11 @@ async function run(startedAt: Date): Promise { logger, ); + const repositoryPropertiesResult = await loadPropertiesFromApi( + logger, + repositoryNwo, + ); + const jobRunUuid = uuidV4(); logger.info(`Job run UUID is ${jobRunUuid}.`); core.exportVariable(EnvVar.JOB_RUN_UUID, jobRunUuid); @@ -140,8 +152,18 @@ async function run(startedAt: Date): Promise { gitHubVersion.type, ); toolsFeatureFlagsValid = codeQLDefaultVersionInfo.toolsFeatureFlagsValid; + + // Determine the effective tools input. + // The explicit `tools` workflow input takes precedence. If none is provided, + // fall back to the 'github-codeql-tools' repository property (if set). + effectiveToolsInput = resolveToolsInput( + repositoryPropertiesResult, + RepositoryPropertyName.TOOLS, + logger, + ); + const initCodeQLResult = await initCodeQL( - getOptionalInput("tools"), + effectiveToolsInput, apiDetails, getTemporaryDirectory(), gitHubVersion.type, @@ -183,6 +205,7 @@ async function run(startedAt: Date): Promise { toolsFeatureFlagsValid, toolsSource, toolsVersion, + effectiveToolsInput, logger, ); } diff --git a/src/setup-codeql.test.ts b/src/setup-codeql.test.ts index 555352bd21..413c9fcbd0 100644 --- a/src/setup-codeql.test.ts +++ b/src/setup-codeql.test.ts @@ -8,6 +8,7 @@ import * as sinon from "sinon"; import * as actionsUtil from "./actions-util"; import * as api from "./api-client"; import { Feature, FeatureEnablement } from "./feature-flags"; +import { RepositoryPropertyName } from "./feature-flags/properties"; import { getRunnerLogger } from "./logging"; import * as setupCodeql from "./setup-codeql"; import * as tar from "./tar"; @@ -338,7 +339,7 @@ test.serial( // 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'.", + "Using the latest CodeQL CLI nightly, as requested.", `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}`, @@ -455,7 +456,7 @@ test.serial( // Check that key messages we would expect to find in the log are present. const expectedMessages: string[] = [ - `Attempting to use the latest CodeQL CLI version in the toolcache, as requested by 'tools: toolcache'.`, + `Attempting to use the latest CodeQL CLI version in the toolcache, as requested.`, `CLI version ${latestToolcacheVersion} is the latest version in the toolcache.`, `Using CodeQL CLI version ${latestToolcacheVersion} from toolcache at ${latestVersionPath}`, ]; @@ -540,7 +541,7 @@ test.serial( { GITHUB_EVENT_NAME: "dynamic" }, [], [ - `Attempting to use the latest CodeQL CLI version in the toolcache, as requested by 'tools: toolcache'.`, + `Attempting to use the latest CodeQL CLI version in the toolcache, as requested.`, `Found no CodeQL CLI in the toolcache, ignoring 'tools: toolcache'...`, ], ); @@ -565,6 +566,115 @@ test.serial( [`Ignoring 'tools: toolcache' because the feature is not enabled.`], ); +test.serial( + "getCodeQLSource correctly returns latest version from toolcache when set via repository property", + async (t) => { + const loggedMessages: LoggedMessage[] = []; + const logger = getRecordingLogger(loggedMessages); + const features = createFeatures([]); + + const latestToolcacheVersion = "3.2.1"; + const latestVersionPath = "/path/to/latest"; + const testVersions = ["2.3.1", latestToolcacheVersion, "1.2.3"]; + const findAllVersionsStub = sinon + .stub(toolcache, "findAllVersions") + .returns(testVersions); + const findStub = sinon.stub(toolcache, "find"); + findStub + .withArgs("CodeQL", latestToolcacheVersion) + .returns(latestVersionPath); + + await withTmpDir(async (tmpDir) => { + // Note: GITHUB_EVENT_NAME is not "dynamic", and the feature flag is not enabled. + // The repository property should bypass these restrictions. + setupActionsVars(tmpDir, tmpDir, { GITHUB_EVENT_NAME: "pull_request" }); + + const source = await setupCodeql.getCodeQLSource( + "toolcache", + SAMPLE_DEFAULT_CLI_VERSION, + SAMPLE_DOTCOM_API_DETAILS, + GitHubVariant.DOTCOM, + false, + features, + logger, + true, // toolsInputFromRepositoryProperty + ); + + t.assert( + findAllVersionsStub.calledOnceWith("CodeQL"), + `toolcache.findAllVersions("CodeQL") wasn't called`, + ); + t.assert( + findStub.calledOnceWith("CodeQL", latestToolcacheVersion), + `toolcache.find("CodeQL", ${latestToolcacheVersion}) wasn't called`, + ); + + t.is(source.sourceType, "toolcache"); + t.is(source.toolsVersion, latestToolcacheVersion); + + const expectedMessages: string[] = [ + `Attempting to use the latest CodeQL CLI version in the toolcache, as requested by the '${RepositoryPropertyName.TOOLS}' repository property.`, + `CLI version ${latestToolcacheVersion} is the latest version in the toolcache.`, + `Using CodeQL CLI version ${latestToolcacheVersion} from toolcache at ${latestVersionPath}`, + ]; + for (const expectedMessage of expectedMessages) { + t.assert( + loggedMessages.some( + (msg) => + typeof msg.message === "string" && + msg.message.includes(expectedMessage), + ), + `Expected '${expectedMessage}' in the logger output, but didn't find it in:\n ${loggedMessages.map((m) => ` - '${m.message}'`).join("\n")}`, + ); + } + }); + }, +); + +test.serial( + "getCodeQLSource falls back to downloading CLI if toolcache is empty when set via repository property", + async (t) => { + const loggedMessages: LoggedMessage[] = []; + const logger = getRecordingLogger(loggedMessages); + const features = createFeatures([]); + + sinon.stub(toolcache, "findAllVersions").returns([]); + + await withTmpDir(async (tmpDir) => { + setupActionsVars(tmpDir, tmpDir, { GITHUB_EVENT_NAME: "pull_request" }); + + const source = await setupCodeql.getCodeQLSource( + "toolcache", + SAMPLE_DEFAULT_CLI_VERSION, + SAMPLE_DOTCOM_API_DETAILS, + GitHubVariant.DOTCOM, + false, + features, + logger, + true, // toolsInputFromRepositoryProperty + ); + + t.is(source.sourceType, "download"); + t.is(source.toolsVersion, SAMPLE_DEFAULT_CLI_VERSION.cliVersion); + + const expectedMessages: string[] = [ + `Attempting to use the latest CodeQL CLI version in the toolcache, as requested by the '${RepositoryPropertyName.TOOLS}' repository property.`, + `Found no CodeQL CLI in the toolcache, ignoring the '${RepositoryPropertyName.TOOLS}' repository property...`, + ]; + for (const expectedMessage of expectedMessages) { + t.assert( + loggedMessages.some( + (msg) => + typeof msg.message === "string" && + msg.message.includes(expectedMessage), + ), + `Expected '${expectedMessage}' in the logger output, but didn't find it in:\n ${loggedMessages.map((m) => ` - '${m.message}'`).join("\n")}`, + ); + } + }); + }, +); + test.serial( 'tryGetTagNameFromUrl extracts the right tag name for a repo name containing "codeql-bundle"', (t) => { diff --git a/src/setup-codeql.ts b/src/setup-codeql.ts index 4ca3302f95..25544e3f50 100644 --- a/src/setup-codeql.ts +++ b/src/setup-codeql.ts @@ -17,6 +17,7 @@ import { Feature, FeatureEnablement, } from "./feature-flags"; +import { RepositoryPropertyName } from "./feature-flags/properties"; import { Logger } from "./logging"; import * as tar from "./tar"; import { @@ -286,6 +287,7 @@ export async function getCodeQLSource( tarSupportsZstd: boolean, features: FeatureEnablement, logger: Logger, + toolsInputFromRepositoryProperty: boolean = false, ): 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. @@ -360,7 +362,7 @@ export async function getCodeQLSource( ); } else { logger.info( - `Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.`, + `Using the latest CodeQL CLI nightly, as requested.`, ); } toolsInput = await getNightlyToolsUrl(logger); @@ -401,19 +403,27 @@ export async function getCodeQLSource( // We only allow `toolsInput === "toolcache"` for `dynamic` events. In general, using `toolsInput === "toolcache"` // can lead to alert wobble and so it shouldn't be used for an analysis where results are intended to be uploaded. - // We also allow this in test mode. + // We also allow this in test mode or when the input comes from a repository property. const allowToolcacheValueFF = await features.getValue( Feature.AllowToolcacheInput, ); const allowToolcacheValue = - allowToolcacheValueFF && (isDynamicWorkflow() || util.isInTestMode()); + toolsInputFromRepositoryProperty || // Repository properties bypass all restrictions + (allowToolcacheValueFF && + (isDynamicWorkflow() || util.isInTestMode())); if (allowToolcacheValue) { // If `toolsInput === "toolcache"`, try to find the latest version of the CLI that's available in the toolcache // and use that. We perform this check here since we can set `cliVersion` directly and don't want to default to // the linked version. - logger.info( - `Attempting to use the latest CodeQL CLI version in the toolcache, as requested by 'tools: ${toolsInput}'.`, - ); + if (toolsInputFromRepositoryProperty) { + logger.info( + `Attempting to use the latest CodeQL CLI version in the toolcache, as requested by the '${RepositoryPropertyName.TOOLS}' repository property.`, + ); + } else { + logger.info( + `Attempting to use the latest CodeQL CLI version in the toolcache, as requested.`, + ); + } latestToolcacheVersion = getLatestToolcacheVersion(logger); if (latestToolcacheVersion) { @@ -423,16 +433,22 @@ export async function getCodeQLSource( if (latestToolcacheVersion === undefined) { if (allowToolcacheValue) { - logger.info( - `Found no CodeQL CLI in the toolcache, ignoring 'tools: ${toolsInput}'...`, - ); + if (toolsInputFromRepositoryProperty) { + logger.info( + `Found no CodeQL CLI in the toolcache, ignoring the '${RepositoryPropertyName.TOOLS}' repository property...`, + ); + } else { + logger.info( + `Found no CodeQL CLI in the toolcache, ignoring 'tools: ${toolsInput}'...`, + ); + } } else { if (allowToolcacheValueFF) { logger.warning( `Ignoring 'tools: ${toolsInput}' because the workflow was not triggered dynamically.`, ); } else { - logger.info( + logger.warning( `Ignoring 'tools: ${toolsInput}' because the feature is not enabled.`, ); } @@ -793,6 +809,7 @@ export async function setupCodeQLBundle( defaultCliVersion: CodeQLDefaultVersionInfo, features: FeatureEnablement, logger: Logger, + toolsInputFromRepositoryProperty: boolean = false, ): Promise { if (!(await util.isBinaryAccessible("tar", logger))) { throw new util.ConfigurationError( @@ -809,6 +826,7 @@ export async function setupCodeQLBundle( zstdAvailability.available, features, logger, + toolsInputFromRepositoryProperty, ); let codeqlFolder: string; diff --git a/src/upload-lib.ts b/src/upload-lib.ts index 2464fe5eaa..2aa8c17264 100644 --- a/src/upload-lib.ts +++ b/src/upload-lib.ts @@ -883,6 +883,12 @@ export async function waitForProcessing( ); break; } + if (!response) { + logger.warning( + "Unable to check analysis status due to missing response. It should still be processed in the background.", + ); + break; + } const status = response.data.processing_status as ProcessingStatus; logger.info(`Analysis upload status is ${status}.`); @@ -891,15 +897,22 @@ export async function waitForProcessing( } else if (options.isUnsuccessfulExecution) { // We expect a specific processing error for unsuccessful executions, so // handle these separately. - handleProcessingResultForUnsuccessfulExecution( - response, - status, - logger, - ); + if (response) { + handleProcessingResultForUnsuccessfulExecution( + response, + status, + logger, + ); + } break; } else if (status === "complete") { break; } else if (status === "failed") { + if (!response) { + throw new Error( + "Code Scanning could not process the submitted SARIF file: Unable to retrieve error details.", + ); + } const message = `Code Scanning could not process the submitted SARIF file:\n${response.data.errors}`; const processingErrors = response.data.errors as string[]; throw shouldConsiderConfigurationError(processingErrors)