diff --git a/lib/entry-points.js b/lib/entry-points.js index 9b527484d..8d51fab4c 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -179942,6 +179942,11 @@ var featureConfig = { envVar: "CODEQL_ACTION_START_PROXY_USE_FEATURES_RELEASE", minimumVersion: void 0 }, + ["skip_unsupported_traced_analysis" /* SkipUnsupportedTracedAnalysis */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_SKIP_UNSUPPORTED_TRACED_ANALYSIS", + minimumVersion: void 0 + }, ["tools_repository_property" /* ToolsRepositoryProperty */]: { defaultValue: false, envVar: "CODEQL_ACTION_TOOLS_REPOSITORY_PROPERTY", @@ -193872,13 +193877,15 @@ async function checkSwiftCompatibility(action, config, codeql) { ); return; } + const skipUnsupportedTracedAnalysis = await action.features.getValue( + "skip_unsupported_traced_analysis" /* SkipUnsupportedTracedAnalysis */ + ); if (version.major >= SWIFT_TRACED_UNSUPPORTED_MACOS) { const baseMessage = [ `Traced Swift analysis is not supported on macOS ${SWIFT_TRACED_UNSUPPORTED_MACOS} or above.`, `Configure your analysis to run on macOS ${SWIFT_TRACED_UNSUPPORTED_MACOS - 1} or below`, `and XCode ${SWIFT_TRACED_UNSUPPORTED_XCODE - 1} or below.` ].join(" "); - action.logger.warning(baseMessage); addDiagnostic( config, "swift" /* swift */, @@ -193891,7 +193898,7 @@ async function checkSwiftCompatibility(action, config, codeql) { macOSVersion: version.toString() }, markdownMessage: baseMessage, - severity: "warning", + severity: skipUnsupportedTracedAnalysis ? "error" : "warning", visibility: { cliSummaryTable: true, statusPage: true, @@ -193900,6 +193907,11 @@ async function checkSwiftCompatibility(action, config, codeql) { } ) ); + if (skipUnsupportedTracedAnalysis) { + throw new ConfigurationError(baseMessage); + } else { + action.logger.warning(baseMessage); + } } } @@ -194422,7 +194434,7 @@ async function run3(actionState) { enableFileCoverageInformation: fileCoverageResult.enabled, logger: logger2 }); - await checkSwiftCompatibility(actionState, config, codeql); + await checkSwiftCompatibility(actionStateWithFeatures, config, codeql); if (repositoryPropertiesResult.isFailure()) { addNoLanguageDiagnostic( config, diff --git a/src/feature-flags.ts b/src/feature-flags.ts index f0fbcd323..e326b08e6 100644 --- a/src/feature-flags.ts +++ b/src/feature-flags.ts @@ -144,6 +144,8 @@ export enum Feature { /** Controls whether overlay build failures on the default branch are stored in the Actions cache. */ OverlayAnalysisStatusSave = "overlay_analysis_status_save", QaTelemetryEnabled = "qa_telemetry_enabled", + /** Whether we should fail early if we detect that traced analysis is unsupported. */ + SkipUnsupportedTracedAnalysis = "skip_unsupported_traced_analysis", /** Routes (some) API requests through the registry proxy. */ ProxyApiRequests = "proxy_api_requests", /** Note that this currently only disables baseline file coverage information. */ @@ -410,6 +412,11 @@ export const featureConfig = { envVar: "CODEQL_ACTION_START_PROXY_USE_FEATURES_RELEASE", minimumVersion: undefined, }, + [Feature.SkipUnsupportedTracedAnalysis]: { + defaultValue: false, + envVar: "CODEQL_ACTION_SKIP_UNSUPPORTED_TRACED_ANALYSIS", + minimumVersion: undefined, + }, [Feature.ToolsRepositoryProperty]: { defaultValue: false, envVar: "CODEQL_ACTION_TOOLS_REPOSITORY_PROPERTY", diff --git a/src/init-action.ts b/src/init-action.ts index d0d06faaa..ae793e19b 100644 --- a/src/init-action.ts +++ b/src/init-action.ts @@ -419,7 +419,7 @@ async function run( logger, }); - await checkSwiftCompatibility(actionState, config, codeql); + await checkSwiftCompatibility(actionStateWithFeatures, config, codeql); if (repositoryPropertiesResult.isFailure()) { addNoLanguageDiagnostic( diff --git a/src/languages/swift.test.ts b/src/languages/swift.test.ts index 8cddd4d48..dd3bca80c 100644 --- a/src/languages/swift.test.ts +++ b/src/languages/swift.test.ts @@ -3,8 +3,10 @@ import sinon from "sinon"; import * as diagnostics from "../diagnostics"; import { ActionsEnvVars } from "../environment"; +import { Feature } from "../feature-flags"; import { checkExpectedLogMessages, + createFeatures, createTestConfig, getCodeQLForTesting, getTestEnv, @@ -183,6 +185,66 @@ test.serial( languages: [BuiltInLanguage.swift], macOSVersion: "27.0.0", }, + severity: "warning", + source: { + id: "codeql-action/unsupported-traced-swift-analysis-macos", + name: "Traced Swift analysis is not supported on this version of macOS", + }, + visibility: { + cliSummaryTable: true, + statusPage: true, + telemetry: true, + }, + } satisfies Partial, + ]); + }), +); + +// `addDiagnostic` changes global state and we must stub it, so this test must be serial. +test.serial( + "checkSwiftCompatibility throws if macOS version is unsupported and FF is enabled", + async (t) => + withTmpDir(async (tmpDir) => { + const logger = new RecordingLogger(); + const env = getTestEnv(); + env.set(ActionsEnvVars.RUNNER_TEMP, tmpDir); + + const features = createFeatures([Feature.SkipUnsupportedTracedAnalysis]); + + const codeql = await getCodeQLForTesting( + "codeql-for-testing", + logger, + env, + ); + sinon.stub(codeql, "getVersion").resolves(makeVersionInfo("2.27.0")); + + const addDiagnostic = sinon.stub(diagnostics, "addDiagnostic"); + + const config = createTestConfig({ languages: [BuiltInLanguage.swift] }); + await t.throwsAsync( + checkSwiftCompatibility( + initAllState({ + logger, + platform: "darwin", + osRelease: "27.0.0", + env, + features, + }), + config, + codeql, + ), + ); + + t.is(addDiagnostic.callCount, 1); + t.like(addDiagnostic.args[0], [ + config, + BuiltInLanguage.swift, + { + attributes: { + languages: [BuiltInLanguage.swift], + macOSVersion: "27.0.0", + }, + severity: "error", source: { id: "codeql-action/unsupported-traced-swift-analysis-macos", name: "Traced Swift analysis is not supported on this version of macOS", diff --git a/src/languages/swift.ts b/src/languages/swift.ts index 834fe9629..30a77eb42 100644 --- a/src/languages/swift.ts +++ b/src/languages/swift.ts @@ -2,6 +2,7 @@ import { ActionState } from "../action-common"; import { CodeQL } from "../codeql"; import { Config } from "../config-utils"; import { addDiagnostic, makeDiagnostic } from "../diagnostics"; +import { Feature } from "../feature-flags"; import { macOSVersion } from "../platform"; import { ToolsFeature } from "../tools-features"; import { ConfigurationError } from "../util"; @@ -23,7 +24,7 @@ export const SWIFT_TRACED_UNSUPPORTED_XCODE = 27; * @throws {ConfigurationError} If Swift analysis is not possible on the current runner. */ export async function checkSwiftCompatibility( - action: ActionState<["Base", "Logger"]>, + action: ActionState<["Base", "Logger", "FeatureFlags"]>, config: Config, codeql: CodeQL, ) { @@ -56,6 +57,9 @@ export async function checkSwiftCompatibility( return; } + const skipUnsupportedTracedAnalysis = await action.features.getValue( + Feature.SkipUnsupportedTracedAnalysis, + ); if (version.major >= SWIFT_TRACED_UNSUPPORTED_MACOS) { const baseMessage = [ `Traced Swift analysis is not supported on macOS ${SWIFT_TRACED_UNSUPPORTED_MACOS} or above.`, @@ -63,9 +67,6 @@ export async function checkSwiftCompatibility( `and XCode ${SWIFT_TRACED_UNSUPPORTED_XCODE - 1} or below.`, ].join(" "); - // Log the message. This will also show up as a workflow annotation. - action.logger.warning(baseMessage); - // Create a diagnostic that will show up on the TSP. addDiagnostic( config, @@ -79,7 +80,7 @@ export async function checkSwiftCompatibility( macOSVersion: version.toString(), }, markdownMessage: baseMessage, - severity: "warning", + severity: skipUnsupportedTracedAnalysis ? "error" : "warning", visibility: { cliSummaryTable: true, statusPage: true, @@ -88,5 +89,15 @@ export async function checkSwiftCompatibility( }, ), ); + + // Throw an error to abort the analysis if the FF is enabled or log the message. + if (skipUnsupportedTracedAnalysis) { + // ConfigurationErrors are converted to the "aborted" status by the exception handler + // in `init-action.ts` that guards the call to `isSwiftCompatible`. + throw new ConfigurationError(baseMessage); + } else { + // This will also show up as a workflow annotation. + action.logger.warning(baseMessage); + } } }