import { getRepositoryProperties } from "../api-client"; import { Logger } from "../logging"; import { RepositoryNwo } from "../repository"; import { GitHubVariant, GitHubVersion } from "../util"; /** * Enumerates repository property names that have some meaning to us. */ export enum RepositoryPropertyName { DISABLE_OVERLAY = "github-codeql-disable-overlay", EXTRA_QUERIES = "github-codeql-extra-queries", } function isKnownPropertyName(value: string): value is RepositoryPropertyName { return Object.values(RepositoryPropertyName).includes( value as RepositoryPropertyName, ); } type AllRepositoryProperties = { [RepositoryPropertyName.DISABLE_OVERLAY]: boolean; [RepositoryPropertyName.EXTRA_QUERIES]: string; }; export type RepositoryProperties = Partial; const mapRepositoryProperties: { [K in RepositoryPropertyName]: (value: string) => AllRepositoryProperties[K]; } = { [RepositoryPropertyName.DISABLE_OVERLAY]: (value) => value === "true", [RepositoryPropertyName.EXTRA_QUERIES]: (value) => value, }; function setProperty( properties: RepositoryProperties, name: K, value: string, ): void { properties[name] = mapRepositoryProperties[name](value); } /** * A repository property has a name and a value. */ interface GitHubRepositoryProperty { property_name: string; value: string; } /** * The API returns a list of `RepositoryProperty` objects. */ export type GitHubPropertiesResponse = GitHubRepositoryProperty[]; /** * Retrieves all known repository properties from the API. * * @param logger The logger to use. * @param repositoryNwo Information about the repository for which to load properties. * @returns Returns a partial mapping from `RepositoryPropertyName` to values. */ export async function loadPropertiesFromApi( gitHubVersion: GitHubVersion, logger: Logger, repositoryNwo: RepositoryNwo, ): Promise { // TODO: To be safe for now; later we should replace this with a version check once we know // which version of GHES we expect this to be supported by. if (gitHubVersion.type === GitHubVariant.GHES) { return {}; } try { const response = await getRepositoryProperties(repositoryNwo); const remoteProperties = response.data as GitHubPropertiesResponse; 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: RepositoryProperties = {}; for (const property of remoteProperties) { if (property.property_name === undefined) { throw new Error( `Expected property object to have a 'property_name', but got: ${JSON.stringify(property)}`, ); } if (isKnownPropertyName(property.property_name)) { setProperty(properties, property.property_name, 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) { throw new Error( `Encountered an error while trying to determine repository properties: ${e}`, ); } }