diff --git a/init/action.yml b/init/action.yml index 57d5a99402..d916cd49f7 100644 --- a/init/action.yml +++ b/init/action.yml @@ -159,6 +159,9 @@ inputs: description: >- Explicitly enable or disable caching of project build dependencies. required: false + repository-owner-type: + default: ${{ github.event.repository.owner.type }} + required: false outputs: codeql-path: description: The path of the CodeQL binary used for analysis diff --git a/lib/init-action.js b/lib/init-action.js index 34a3a1086f..8eab73d1ca 100644 --- a/lib/init-action.js +++ b/lib/init-action.js @@ -87193,6 +87193,27 @@ function joinAtMost(array, separator, limit) { } return array.join(separator); } +var Result = class _Result { + constructor(_ok, value) { + this._ok = _ok; + this.value = value; + } + static ok(value) { + return new _Result(true, value); + } + static error(error3) { + return new _Result(false, error3); + } + isOk() { + return this._ok; + } + isError() { + return !this._ok; + } + orElse(defaultValue) { + return this._ok ? this.value : defaultValue; + } +}; // src/actions-util.ts var pkg = require_package(); @@ -87778,11 +87799,17 @@ async function loadPropertiesFromApi(gitHubVersion, logger, repositoryNwo) { properties[property.property_name] = property.value; } } - 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 (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}`); + } } return properties; } catch (e) { @@ -92561,10 +92588,12 @@ async function run(startedAt) { getTemporaryDirectory(), logger ); - const enableRepoProps = await features.getValue( - "use_repository_properties" /* UseRepositoryProperties */ + const repositoryProperties = await loadRepositoryProperties( + repositoryNwo, + gitHubVersion, + features, + logger ); - const repositoryProperties = enableRepoProps ? await loadPropertiesFromApi(gitHubVersion, logger, repositoryNwo) : {}; const jobRunUuid = v4_default(); logger.info(`Job run UUID is ${jobRunUuid}.`); core13.exportVariable("JOB_RUN_UUID" /* JOB_RUN_UUID */, jobRunUuid); @@ -92653,9 +92682,24 @@ async function run(startedAt) { githubVersion: gitHubVersion, apiDetails, features, - repositoryProperties, + repositoryProperties: repositoryProperties.orElse({}), logger }); + if (repositoryProperties.isError()) { + addDiagnostic( + config, + // Arbitrarily choose the first language. We could also choose all languages, but that + // increases the risk of misinterpreting the data. + config.languages[0], + makeTelemetryDiagnostic( + "codeql-action/repository-properties-load-failure", + "Failed to load repository properties", + { + error: getErrorMessage(repositoryProperties.value) + } + ) + ); + } await checkInstallPython311(config.languages, codeql); } catch (unwrappedError) { const error3 = wrapError(unwrappedError); @@ -92940,6 +92984,31 @@ exec ${goBinaryPath} "$@"` logger ); } +async function loadRepositoryProperties(repositoryNwo, gitHubVersion, features, logger) { + const repositoryOwnerType = getOptionalInput("repository-owner-type"); + if (repositoryOwnerType === "User") { + logger.debug( + "Skipping loading repository properties because the repository is owned by a user and therefore cannot have repository properties." + ); + return Result.ok({}); + } + if (!await features.getValue("use_repository_properties" /* UseRepositoryProperties */)) { + logger.debug( + "Skipping loading repository properties because the UseRepositoryProperties feature flag is disabled." + ); + return Result.ok({}); + } + try { + return Result.ok( + await loadPropertiesFromApi(gitHubVersion, logger, repositoryNwo) + ); + } catch (error3) { + logger.warning( + `Failed to load repository properties: ${getErrorMessage(error3)}` + ); + return Result.error(error3); + } +} function getTrapCachingEnabled() { const trapCaching = getOptionalInput("trap-caching"); if (trapCaching !== void 0) return trapCaching === "true"; diff --git a/src/feature-flags/properties.ts b/src/feature-flags/properties.ts index 0104cddd91..d0eb233702 100644 --- a/src/feature-flags/properties.ts +++ b/src/feature-flags/properties.ts @@ -78,11 +78,17 @@ export async function loadPropertiesFromApi( } } - 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 (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}`); + } } return properties; diff --git a/src/init-action.ts b/src/init-action.ts index 8b6c200a05..53b5e6a0f9 100644 --- a/src/init-action.ts +++ b/src/init-action.ts @@ -36,8 +36,11 @@ import { makeTelemetryDiagnostic, } from "./diagnostics"; import { EnvVar } from "./environment"; -import { Feature, Features } from "./feature-flags"; -import { loadPropertiesFromApi } from "./feature-flags/properties"; +import { Feature, FeatureEnablement, Features } from "./feature-flags"; +import { + loadPropertiesFromApi, + RepositoryProperties, +} from "./feature-flags/properties"; import { checkInstallPython311, checkPacksForOverlayCompatibility, @@ -53,7 +56,7 @@ import { OverlayBaseDatabaseDownloadStats, OverlayDatabaseMode, } from "./overlay-database-utils"; -import { getRepositoryNwo } from "./repository"; +import { getRepositoryNwo, RepositoryNwo } from "./repository"; import { ToolsSource } from "./setup-codeql"; import { ActionName, @@ -87,6 +90,8 @@ import { checkActionVersion, getErrorMessage, BuildMode, + GitHubVersion, + Result, } from "./util"; import { checkWorkflow } from "./workflow"; @@ -237,12 +242,12 @@ async function run(startedAt: Date) { ); // Fetch the values of known repository properties that affect us. - const enableRepoProps = await features.getValue( - Feature.UseRepositoryProperties, + const repositoryProperties = await loadRepositoryProperties( + repositoryNwo, + gitHubVersion, + features, + logger, ); - const repositoryProperties = enableRepoProps - ? await loadPropertiesFromApi(gitHubVersion, logger, repositoryNwo) - : {}; // Create a unique identifier for this run. const jobRunUuid = uuidV4(); @@ -363,10 +368,26 @@ async function run(startedAt: Date) { githubVersion: gitHubVersion, apiDetails, features, - repositoryProperties, + repositoryProperties: repositoryProperties.orElse({}), logger, }); + if (repositoryProperties.isError()) { + addDiagnostic( + config, + // Arbitrarily choose the first language. We could also choose all languages, but that + // increases the risk of misinterpreting the data. + config.languages[0], + makeTelemetryDiagnostic( + "codeql-action/repository-properties-load-failure", + "Failed to load repository properties", + { + error: getErrorMessage(repositoryProperties.value), + }, + ), + ); + } + await checkInstallPython311(config.languages, codeql); } catch (unwrappedError) { const error = wrapError(unwrappedError); @@ -775,6 +796,41 @@ async function run(startedAt: Date) { ); } +async function loadRepositoryProperties( + repositoryNwo: RepositoryNwo, + gitHubVersion: GitHubVersion, + features: FeatureEnablement, + logger: Logger, +): Promise> { + const repositoryOwnerType = getOptionalInput("repository-owner-type"); + if (repositoryOwnerType === "User") { + // Users cannot have repository properties, so skip the API call. + logger.debug( + "Skipping loading repository properties because the repository is owned by a user and " + + "therefore cannot have repository properties.", + ); + return Result.ok({}); + } + + if (!(await features.getValue(Feature.UseRepositoryProperties))) { + logger.debug( + "Skipping loading repository properties because the UseRepositoryProperties feature flag is disabled.", + ); + return Result.ok({}); + } + + try { + return Result.ok( + await loadPropertiesFromApi(gitHubVersion, logger, repositoryNwo), + ); + } catch (error) { + logger.warning( + `Failed to load repository properties: ${getErrorMessage(error)}`, + ); + return Result.error(error); + } +} + function getTrapCachingEnabled(): boolean { // If the workflow specified something always respect that const trapCaching = getOptionalInput("trap-caching"); diff --git a/src/util.ts b/src/util.ts index 327fce0c77..fc11b86b2e 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1292,3 +1292,33 @@ export function joinAtMost( return array.join(separator); } + +type Success = Result; +type Failure = Result; + +export class Result { + private constructor( + private readonly _ok: boolean, + public readonly value: T | E, + ) {} + + static ok(value: T): Success { + return new Result(true, value) as Success; + } + + static error(error: E): Failure { + return new Result(false, error) as Failure; + } + + isOk(): this is Success { + return this._ok; + } + + isError(): this is Failure { + return !this._ok; + } + + orElse(defaultValue: T): T { + return this._ok ? (this.value as T) : defaultValue; + } +}