From d09e9c0eb96daf5572166e3d746fd3f00fc18433 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Fri, 9 Oct 2026 16:01:15 +0100 Subject: [PATCH] Add FF to fail if macOS version is unsupported --- lib/entry-points.js | 18 +++++++++-- src/feature-flags.ts | 7 ++++ src/init-action.ts | 2 +- src/languages/swift.test.ts | 64 +++++++++++++++++++++++++++++++++++++ src/languages/swift.ts | 21 +++++++++--- 5 files changed, 103 insertions(+), 9 deletions(-) diff --git a/lib/entry-points.js b/lib/entry-points.js index 96f9e46b8..f6df3572b 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -172115,6 +172115,11 @@ var featureConfig = { envVar: "CODEQL_ACTION_START_PROXY_USE_FEATURES_RELEASE", minimumVersion: void 0 }, + ["swift_skip_unsupported_traced_analysis" /* SwiftSkipUnsupportedTracedAnalysis */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_SWIFT_SKIP_UNSUPPORTED_TRACED_ANALYSIS", + minimumVersion: void 0 + }, ["tools_repository_property" /* ToolsRepositoryProperty */]: { defaultValue: false, envVar: "CODEQL_ACTION_TOOLS_REPOSITORY_PROPERTY", @@ -185973,13 +185978,15 @@ async function isSwiftCompatible(action, config, codeql) { ); return; } + const skipUnsupportedTracedAnalysis = await action.features.getValue( + "swift_skip_unsupported_traced_analysis" /* SwiftSkipUnsupportedTracedAnalysis */ + ); 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 */, @@ -185992,7 +185999,7 @@ async function isSwiftCompatible(action, config, codeql) { macOSVersion: version.toString() }, markdownMessage: baseMessage, - severity: "warning", + severity: skipUnsupportedTracedAnalysis ? "error" : "warning", visibility: { cliSummaryTable: true, statusPage: true, @@ -186001,6 +186008,11 @@ async function isSwiftCompatible(action, config, codeql) { } ) ); + if (skipUnsupportedTracedAnalysis) { + throw new ConfigurationError(baseMessage); + } else { + action.logger.warning(baseMessage); + } } } @@ -186522,7 +186534,7 @@ async function run3(actionState) { enableFileCoverageInformation: fileCoverageResult.enabled, logger: logger2 }); - await isSwiftCompatible(actionState, config, codeql); + await isSwiftCompatible(actionStateWithFeatures, config, codeql); if (repositoryPropertiesResult.isFailure()) { addNoLanguageDiagnostic( config, diff --git a/src/feature-flags.ts b/src/feature-flags.ts index 59b50e2df..8db78b1dd 100644 --- a/src/feature-flags.ts +++ b/src/feature-flags.ts @@ -170,6 +170,8 @@ export enum Feature { */ PerLanguageBundles = "per_language_bundles_v2", QaTelemetryEnabled = "qa_telemetry_enabled", + /** Whether we should fail early if we detect that traced Swift analysis is unsupported. */ + SwiftSkipUnsupportedTracedAnalysis = "swift_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. */ @@ -466,6 +468,11 @@ export const featureConfig = { envVar: "CODEQL_ACTION_START_PROXY_USE_FEATURES_RELEASE", minimumVersion: undefined, }, + [Feature.SwiftSkipUnsupportedTracedAnalysis]: { + defaultValue: false, + envVar: "CODEQL_ACTION_SWIFT_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 6c62adfbb..37a2c0179 100644 --- a/src/init-action.ts +++ b/src/init-action.ts @@ -419,7 +419,7 @@ async function run( logger, }); - await isSwiftCompatible(actionState, config, codeql); + await isSwiftCompatible(actionStateWithFeatures, config, codeql); if (repositoryPropertiesResult.isFailure()) { addNoLanguageDiagnostic( diff --git a/src/languages/swift.test.ts b/src/languages/swift.test.ts index 152775682..c9e90ad45 100644 --- a/src/languages/swift.test.ts +++ b/src/languages/swift.test.ts @@ -4,8 +4,10 @@ import * as sinon from "sinon"; import { getCodeQLForTesting } from "../codeql"; import * as diagnostics from "../diagnostics"; import { ActionsEnvVars } from "../environment"; +import { Feature } from "../feature-flags"; import { checkExpectedLogMessages, + createFeatures, createTestConfig, getTestEnv, initAllState, @@ -176,6 +178,68 @@ 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( + "isSwiftCompatible 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.SwiftSkipUnsupportedTracedAnalysis, + ]); + + 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( + isSwiftCompatible( + 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 21172951b..e73e40e59 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"; @@ -24,7 +25,7 @@ export const SWIFT_TRACED_UNSUPPORTED_XCODE = 27; * @returns True if we can run a Swift analysis. */ export async function isSwiftCompatible( - action: ActionState<["Base", "Logger"]>, + action: ActionState<["Base", "Logger", "FeatureFlags"]>, config: Config, codeql: CodeQL, ) { @@ -57,6 +58,9 @@ export async function isSwiftCompatible( return; } + const skipUnsupportedTracedAnalysis = await action.features.getValue( + Feature.SwiftSkipUnsupportedTracedAnalysis, + ); if (version.major >= SWIFT_TRACED_UNSUPPORTED_MACOS) { const baseMessage = [ `Traced Swift analysis is not supported on macOS ${SWIFT_TRACED_UNSUPPORTED_MACOS} or above.`, @@ -64,9 +68,6 @@ export async function isSwiftCompatible( `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, @@ -80,7 +81,7 @@ export async function isSwiftCompatible( macOSVersion: version.toString(), }, markdownMessage: baseMessage, - severity: "warning", + severity: skipUnsupportedTracedAnalysis ? "error" : "warning", visibility: { cliSummaryTable: true, statusPage: true, @@ -89,5 +90,15 @@ export async function isSwiftCompatible( }, ), ); + + // 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); + } } }