diff --git a/lib/entry-points.js b/lib/entry-points.js index a50c5450f6..49d5c55c06 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -92812,10 +92812,10 @@ var require_util12 = __commonJS({ return objectToString(arg) === "[object Array]"; } exports2.isArray = isArray2; - function isBoolean(arg) { + function isBoolean2(arg) { return typeof arg === "boolean"; } - exports2.isBoolean = isBoolean; + exports2.isBoolean = isBoolean2; function isNull(arg) { return arg === null; } @@ -96885,7 +96885,7 @@ var require_validators = __commonJS({ throw new ERR_INVALID_ARG_TYPE(name, "a dictionary", value); } }); - var validateArray = hideStackFrames((value, name, minLength = 0) => { + var validateArray2 = hideStackFrames((value, name, minLength = 0) => { if (!ArrayIsArray(value)) { throw new ERR_INVALID_ARG_TYPE(name, "Array", value); } @@ -96895,19 +96895,19 @@ var require_validators = __commonJS({ } }); function validateStringArray(value, name) { - validateArray(value, name); + validateArray2(value, name); for (let i = 0; i < value.length; i++) { validateString(value[i], `${name}[${i}]`); } } function validateBooleanArray(value, name) { - validateArray(value, name); + validateArray2(value, name); for (let i = 0; i < value.length; i++) { validateBoolean(value[i], `${name}[${i}]`); } } function validateAbortSignalArray(value, name) { - validateArray(value, name); + validateArray2(value, name); for (let i = 0; i < value.length; i++) { const signal = value[i]; const indexedName = `${name}[${i}]`; @@ -97003,7 +97003,7 @@ var require_validators = __commonJS({ isInt32, isUint32, parseFileMode, - validateArray, + validateArray: validateArray2, validateStringArray, validateBooleanArray, validateAbortSignalArray, @@ -144596,21 +144596,25 @@ function isString(value) { function isNumber(value) { return typeof value === "number"; } +function isBoolean(value) { + return typeof value === "boolean"; +} function isStringOrUndefined(value) { return value === void 0 || isString(value); } function defaultCheck(validate2) { return (arg) => ({ unknownKeys: [], invalidKeys: [], valid: validate2(arg) }); } -function makeValidator(validate2, required = true) { +function makeValidator(validate2) { return { validate: validate2, check: defaultCheck(validate2), - required + required: true }; } var string = makeValidator(isString); var number = makeValidator(isNumber); +var boolean = makeValidator(isBoolean); function array(validator) { const validate2 = (val) => { return isArray(val) && val.every((e) => validator.validate(e)); @@ -144692,6 +144696,10 @@ function validateSchema(schema, obj) { const result = checkSchema(schema, obj, { failFast: true }); return result.valid; } +function validateArray(elementSchema, arr) { + const elementValidator = object(elementSchema); + return array(elementValidator).validate(arr); +} function successfulCheckSchema() { return { valid: true, @@ -146400,6 +146408,148 @@ async function getGeneratedFiles(workingDirectory) { return generatedFiles; } +// src/start-proxy/types.ts +var usernameSchema = { + /** The username needed to authenticate to the package registry, if any. */ + username: optionalOrNull(string) +}; +function hasUsername(config) { + return "username" in config; +} +var usernamePasswordSchema = { + /** The password needed to authenticate to the package registry, if any. */ + password: optionalOrNull(string), + ...usernameSchema +}; +function hasUsernameAndPassword(config) { + return hasUsername(config) && "password" in config; +} +var tokenSchema = { + /** The token needed to authenticate to the package registry, if any. */ + token: optionalOrNull(string), + ...usernameSchema +}; +function hasToken(config) { + return "token" in config; +} +function isToken(config) { + return "token" in config && validateSchema(tokenSchema, config); +} +var azureConfigSchema = { + "tenant-id": string, + "client-id": string +}; +function isAzureConfig(config) { + return validateSchema(azureConfigSchema, config); +} +var awsConfigSchema = { + "aws-region": string, + "account-id": string, + "role-name": string, + domain: string, + "domain-owner": string, + audience: optionalOrNull(string) +}; +function isAWSConfig(config) { + return validateSchema(awsConfigSchema, config); +} +var jfrogConfigSchema = { + "jfrog-oidc-provider-name": string, + audience: optionalOrNull(string), + "identity-mapping-name": optionalOrNull(string) +}; +function isJFrogConfig(config) { + return validateSchema(jfrogConfigSchema, config); +} +var cloudsmithConfigSchema = { + namespace: string, + "service-slug": string, + "api-host": string +}; +function isCloudsmithConfig(config) { + return validateSchema(cloudsmithConfigSchema, config); +} +var gcpConfigSchema = { + "workload-identity-provider": string, + "service-account": optionalOrNull(string), + audience: optionalOrNull(string) +}; +function isGCPConfig(config) { + return validateSchema(gcpConfigSchema, config); +} +var oidcSchemas = [ + { schema: azureConfigSchema, name: "Azure" }, + { schema: awsConfigSchema, name: "AWS" }, + { schema: jfrogConfigSchema, name: "JFrog" }, + { schema: cloudsmithConfigSchema, name: "Cloudsmith" }, + { schema: gcpConfigSchema, name: "GCP" } +]; +function credentialToStr(credential) { + let result = `Type: ${credential.type};`; + const appendIfDefined = (name, val) => { + if (isDefined2(val)) { + result += ` ${name}: ${val};`; + } + }; + appendIfDefined("Url", credential.url); + appendIfDefined("Host", credential.host); + if (hasUsername(credential)) { + appendIfDefined("Username", credential.username); + } + if ("password" in credential) { + appendIfDefined( + "Password", + isDefined2(credential.password) ? "***" : void 0 + ); + } + if (hasToken(credential)) { + appendIfDefined("Token", isDefined2(credential.token) ? "***" : void 0); + } + if (isAzureConfig(credential)) { + appendIfDefined("Tenant", credential["tenant-id"]); + appendIfDefined("Client", credential["client-id"]); + } else if (isAWSConfig(credential)) { + appendIfDefined("AWS Region", credential["aws-region"]); + appendIfDefined("AWS Account", credential["account-id"]); + appendIfDefined("AWS Role", credential["role-name"]); + appendIfDefined("AWS Domain", credential.domain); + appendIfDefined("AWS Domain Owner", credential["domain-owner"]); + appendIfDefined("AWS Audience", credential.audience); + } else if (isJFrogConfig(credential)) { + appendIfDefined("JFrog Provider", credential["jfrog-oidc-provider-name"]); + appendIfDefined( + "JFrog Identity Mapping", + credential["identity-mapping-name"] + ); + appendIfDefined("JFrog Audience", credential.audience); + } else if (isCloudsmithConfig(credential)) { + appendIfDefined("Cloudsmith Namespace", credential.namespace); + appendIfDefined("Cloudsmith Service Slug", credential["service-slug"]); + appendIfDefined("Cloudsmith API Host", credential["api-host"]); + } else if (isGCPConfig(credential)) { + appendIfDefined( + "GCP Workload Identity Provider", + credential["workload-identity-provider"] + ); + appendIfDefined("GCP Service Account", credential["service-account"]); + appendIfDefined("GCP Audience", credential.audience); + } + return result; +} +var registryBaseSchema = { + /** The type of the package registry. */ + type: string, + /** Whether the registry replaces the base registry for the ecosystem. */ + "replaces-base": optional(boolean) +}; +function getAddressString(address) { + if (address.url === void 0) { + return address.host; + } else { + return address.url; + } +} + // src/status-report.ts function getDisplayActionName(actionName) { if (actionName === "finish" /* Analyze */) { @@ -146468,6 +146618,34 @@ function setJobStatusIfUnsuccessful(actionStatus) { ); } } +function getRegistryTypesFromEnv(logger, env = getEnv()) { + const value = env.getOptional("CODEQL_PROXY_URLS" /* PROXY_URLS */); + if (value === void 0) { + return void 0; + } + try { + const data = JSON.parse(value); + if (!isArray(data)) { + logger.debug( + `Expected '${"CODEQL_PROXY_URLS" /* PROXY_URLS */}' to contain a JSON array, but got '${typeof data}'.` + ); + return void 0; + } + if (!validateArray(registryBaseSchema, data)) { + logger.debug( + `Expected '${"CODEQL_PROXY_URLS" /* PROXY_URLS */}' to contain a JSON array of registry objects, but got something else.` + ); + return void 0; + } + const types2 = new Set(data.map((r) => r.type)); + return Array.from(types2).sort().join(","); + } catch (err) { + logger.debug( + `Failed to parse '${"CODEQL_PROXY_URLS" /* PROXY_URLS */}': ${getErrorMessage(err)}.` + ); + return void 0; + } +} async function createStatusReportBase(actionName, status, actionStartedAt, config, diskInfo, logger, cause, exception) { try { const commitOid = getOptionalInput("sha") || process.env["GITHUB_SHA"] || ""; @@ -146507,6 +146685,7 @@ async function createStatusReportBase(actionName, status, actionStartedAt, confi job_name: jobName, job_run_uuid: jobRunUUID, ref, + registry_types: getRegistryTypesFromEnv(logger), runner_os: runnerOs, started_at: workflowStartedAt, status, @@ -157415,7 +157594,7 @@ var import_async = __toESM(require_async(), 1); var import_path6 = require("path"); // node_modules/archiver/lib/error.js -var import_util33 = __toESM(require("util"), 1); +var import_util34 = __toESM(require("util"), 1); var ERROR_CODES = { ABORTED: "archive was aborted", DIRECTORYDIRPATHREQUIRED: "diretory dirpath argument must be a non-empty string value", @@ -157440,7 +157619,7 @@ function ArchiverError(code, data) { this.code = code; this.data = data; } -import_util33.default.inherits(ArchiverError, Error); +import_util34.default.inherits(ArchiverError, Error); // node_modules/archiver/lib/core.js var import_readable_stream2 = __toESM(require_ours(), 1); @@ -161870,142 +162049,6 @@ var path26 = __toESM(require("path")); var core26 = __toESM(require_core()); var toolcache4 = __toESM(require_tool_cache()); -// src/start-proxy/types.ts -var usernameSchema = { - /** The username needed to authenticate to the package registry, if any. */ - username: optionalOrNull(string) -}; -function hasUsername(config) { - return "username" in config; -} -var usernamePasswordSchema = { - /** The password needed to authenticate to the package registry, if any. */ - password: optionalOrNull(string), - ...usernameSchema -}; -function hasUsernameAndPassword(config) { - return hasUsername(config) && "password" in config; -} -var tokenSchema = { - /** The token needed to authenticate to the package registry, if any. */ - token: optionalOrNull(string), - ...usernameSchema -}; -function hasToken(config) { - return "token" in config; -} -function isToken(config) { - return "token" in config && validateSchema(tokenSchema, config); -} -var azureConfigSchema = { - "tenant-id": string, - "client-id": string -}; -function isAzureConfig(config) { - return validateSchema(azureConfigSchema, config); -} -var awsConfigSchema = { - "aws-region": string, - "account-id": string, - "role-name": string, - domain: string, - "domain-owner": string, - audience: optionalOrNull(string) -}; -function isAWSConfig(config) { - return validateSchema(awsConfigSchema, config); -} -var jfrogConfigSchema = { - "jfrog-oidc-provider-name": string, - audience: optionalOrNull(string), - "identity-mapping-name": optionalOrNull(string) -}; -function isJFrogConfig(config) { - return validateSchema(jfrogConfigSchema, config); -} -var cloudsmithConfigSchema = { - namespace: string, - "service-slug": string, - "api-host": string -}; -function isCloudsmithConfig(config) { - return validateSchema(cloudsmithConfigSchema, config); -} -var gcpConfigSchema = { - "workload-identity-provider": string, - "service-account": optionalOrNull(string), - audience: optionalOrNull(string) -}; -function isGCPConfig(config) { - return validateSchema(gcpConfigSchema, config); -} -var oidcSchemas = [ - { schema: azureConfigSchema, name: "Azure" }, - { schema: awsConfigSchema, name: "AWS" }, - { schema: jfrogConfigSchema, name: "JFrog" }, - { schema: cloudsmithConfigSchema, name: "Cloudsmith" }, - { schema: gcpConfigSchema, name: "GCP" } -]; -function credentialToStr(credential) { - let result = `Type: ${credential.type};`; - const appendIfDefined = (name, val) => { - if (isDefined2(val)) { - result += ` ${name}: ${val};`; - } - }; - appendIfDefined("Url", credential.url); - appendIfDefined("Host", credential.host); - if (hasUsername(credential)) { - appendIfDefined("Username", credential.username); - } - if ("password" in credential) { - appendIfDefined( - "Password", - isDefined2(credential.password) ? "***" : void 0 - ); - } - if (hasToken(credential)) { - appendIfDefined("Token", isDefined2(credential.token) ? "***" : void 0); - } - if (isAzureConfig(credential)) { - appendIfDefined("Tenant", credential["tenant-id"]); - appendIfDefined("Client", credential["client-id"]); - } else if (isAWSConfig(credential)) { - appendIfDefined("AWS Region", credential["aws-region"]); - appendIfDefined("AWS Account", credential["account-id"]); - appendIfDefined("AWS Role", credential["role-name"]); - appendIfDefined("AWS Domain", credential.domain); - appendIfDefined("AWS Domain Owner", credential["domain-owner"]); - appendIfDefined("AWS Audience", credential.audience); - } else if (isJFrogConfig(credential)) { - appendIfDefined("JFrog Provider", credential["jfrog-oidc-provider-name"]); - appendIfDefined( - "JFrog Identity Mapping", - credential["identity-mapping-name"] - ); - appendIfDefined("JFrog Audience", credential.audience); - } else if (isCloudsmithConfig(credential)) { - appendIfDefined("Cloudsmith Namespace", credential.namespace); - appendIfDefined("Cloudsmith Service Slug", credential["service-slug"]); - appendIfDefined("Cloudsmith API Host", credential["api-host"]); - } else if (isGCPConfig(credential)) { - appendIfDefined( - "GCP Workload Identity Provider", - credential["workload-identity-provider"] - ); - appendIfDefined("GCP Service Account", credential["service-account"]); - appendIfDefined("GCP Audience", credential.audience); - } - return result; -} -function getAddressString(address) { - if (address.url === void 0) { - return address.host; - } else { - return address.url; - } -} - // src/start-proxy/validation.ts var core25 = __toESM(require_core()); function cloneCredential(schema, obj) { diff --git a/src/json/index.ts b/src/json/index.ts index 78923f8bac..d8764ec478 100644 --- a/src/json/index.ts +++ b/src/json/index.ts @@ -35,6 +35,11 @@ export function isNumber(value: unknown): value is number { return typeof value === "number"; } +/** Asserts that `value` is a boolean. */ +export function isBoolean(value: unknown): value is boolean { + return typeof value === "boolean"; +} + /** Asserts that `value` is either a string or undefined. */ export function isStringOrUndefined( value: unknown, @@ -62,14 +67,11 @@ function defaultCheck( return (arg) => ({ unknownKeys: [], invalidKeys: [], valid: validate(arg) }); } -function makeValidator( - validate: (arg: unknown) => arg is T, - required: boolean = true, -) { +function makeValidator(validate: (arg: unknown) => arg is T) { return { validate, check: defaultCheck(validate), - required, + required: true, } as const satisfies Validator; } @@ -82,6 +84,9 @@ export const string = makeValidator(isString); /** A validator for number fields in schemas. */ export const number = makeValidator(isNumber); +/** A validator for boolean fields in schemas. */ +export const boolean = makeValidator(isBoolean); + /** A validator for arrays. */ export function array(validator: Validator) { const validate = (val: unknown) => { @@ -221,6 +226,23 @@ export function validateSchema< return result.valid; } +/** + * Validates that `arr` is an array whose elements satisfy at least `elementSchema`. + * Additional keys are accepted in each element. + * + * @param elementSchema The schema to validate the elements against. + * @param arr The array to validate. + * @returns Asserts that `arr` has elements of `schema`'s type if validation is successful. + */ +export function validateArray< + S extends Schema, + T extends UnvalidatedArray = Array>, +>(elementSchema: S, arr: UnvalidatedArray): arr is T { + const elementValidator = object(elementSchema); + + return array(elementValidator).validate(arr); +} + export interface CheckSchemaOptions { /** Whether to stop validation after the first error. */ failFast?: boolean; diff --git a/src/start-proxy.ts b/src/start-proxy.ts index 74e0b498c4..caa1b3054a 100644 --- a/src/start-proxy.ts +++ b/src/start-proxy.ts @@ -83,12 +83,6 @@ export class StartProxyError extends Error { } } -interface StartProxyStatus extends StatusReportBase { - // A comma-separated list of registry types which are configured for CodeQL. - // This only includes registry types we support, not all that are configured. - registry_types: string; -} - /** * Sends a status report for the `start-proxy` action indicating a successful outcome. * @@ -112,7 +106,7 @@ export async function sendSuccessStatusReport( logger, ); if (statusReportBase !== undefined) { - const statusReport: StartProxyStatus = { + const statusReport: StatusReportBase = { ...statusReportBase, registry_types: registry_types.join(","), }; diff --git a/src/start-proxy/types.ts b/src/start-proxy/types.ts index 13369edbfa..17803e9126 100644 --- a/src/start-proxy/types.ts +++ b/src/start-proxy/types.ts @@ -254,13 +254,19 @@ export function credentialToStr(credential: Credential): string { return result; } -/** A package registry is identified by its type and address. */ -export type Registry = { +/** The schema for `RegistryBase` objects. */ +export const registryBaseSchema = { /** The type of the package registry. */ - type: string; + type: json.string, /** Whether the registry replaces the base registry for the ecosystem. */ - "replaces-base"?: boolean; -} & Address; + "replaces-base": json.optional(json.boolean), +} as const satisfies json.Schema; + +/** Information about a registry, other than its address. */ +export type RegistryBase = json.FromSchema; + +/** A package registry is identified by its type and address. */ +export type Registry = RegistryBase & Address; // If a registry has an `url`, then that takes precedence over the `host` which may or may // not be defined. diff --git a/src/status-report.test.ts b/src/status-report.test.ts index 17490a60cb..2b763da700 100644 --- a/src/status-report.test.ts +++ b/src/status-report.test.ts @@ -4,15 +4,17 @@ import * as uuid from "uuid"; import * as actionsUtil from "./actions-util"; import { Config } from "./config-utils"; -import { EnvVar } from "./environment"; +import { EnvVar, RegistryProxyVars } from "./environment"; import { BuiltInLanguage } from "./languages"; import { getRunnerLogger } from "./logging"; import { ToolsSource } from "./setup-codeql"; +import type { Registry } from "./start-proxy"; import { ActionName, createInitWithConfigStatusReport, createStatusReportBase, getActionsStatus, + getRegistryTypesFromEnv, getJobUUID, InitStatusReport, InitWithConfigStatusReport, @@ -22,12 +24,69 @@ import { setupActionsVars, createTestConfig, makeMacro, + getTestEnv, + RecordingLogger, callee, } from "./testing-utils"; import { BuildMode, ConfigurationError, withTmpDir, wrapError } from "./util"; setupTests(test); +test("getRegistryTypesFromEnv - gets unique registry types from environment", async (t) => { + const logger = new RecordingLogger(true); + const env = getTestEnv({ + [RegistryProxyVars.PROXY_URLS]: JSON.stringify([ + { type: "git_source", url: "https://example.com" }, + { type: "git_source", url: "https://github.com" }, + { type: "docker_registry", url: "https://registry.example.com" }, + ] satisfies Array>), + }); + + const result = getRegistryTypesFromEnv(logger, env); + t.deepEqual(result, ["git_source", "docker_registry"].sort().join(",")); +}); + +test("getRegistryTypesFromEnv - returns undefined if the env var is not set", async (t) => { + const logger = new RecordingLogger(true); + const env = getTestEnv({}); + + const result = getRegistryTypesFromEnv(logger, env); + t.is(result, undefined); +}); + +test("getRegistryTypesFromEnv - returns undefined if the env var is not valid JSON", async (t) => { + const logger = new RecordingLogger(true); + const env = getTestEnv({ [RegistryProxyVars.PROXY_URLS]: "[" }); + + const result = getRegistryTypesFromEnv(logger, env); + t.is(result, undefined); +}); + +test("getRegistryTypesFromEnv - returns undefined if the env var is unexpected JSON", async (t) => { + const logger = new RecordingLogger(true); + + t.is( + getRegistryTypesFromEnv( + logger, + getTestEnv({ + // Top-level object rather than an array of objects. + [RegistryProxyVars.PROXY_URLS]: JSON.stringify({ type: "git_source" }), + }), + ), + undefined, + ); + t.is( + getRegistryTypesFromEnv( + logger, + getTestEnv({ + // Object has no "type" key. + [RegistryProxyVars.PROXY_URLS]: JSON.stringify([{}]), + }), + ), + undefined, + ); +}); + test("getJobUUID - generates valid UUIDs", async (t) => { await callee(getJobUUID) .withArgs() @@ -74,6 +133,9 @@ function setupEnvironmentAndStub(tmpDir: string) { process.env[EnvVar.ANALYSIS_KEY] = "analysis-key"; process.env["ImageVersion"] = "2023.05.19.1"; + process.env[RegistryProxyVars.PROXY_URLS] = JSON.stringify([ + { type: "maven_repository" }, + ] satisfies Array>); const getRequiredInput = sinon.stub(actionsUtil, "getRequiredInput"); getRequiredInput.withArgs("matrix").resolves("input/matrix"); @@ -117,6 +179,7 @@ test.serial("createStatusReportBase", async (t) => { t.is(typeof statusReport.job_run_uuid, "string"); t.is(statusReport.languages, "java,swift"); t.is(statusReport.ref, process.env["GITHUB_REF"]!); + t.is(statusReport.registry_types, "maven_repository"); t.is(statusReport.runner_available_disk_space_bytes, 100); t.is(statusReport.runner_image_version, process.env["ImageVersion"]); t.is(statusReport.runner_os, process.env["RUNNER_OS"]!); diff --git a/src/status-report.ts b/src/status-report.ts index 69cac8a05d..b471bfa971 100644 --- a/src/status-report.ts +++ b/src/status-report.ts @@ -19,12 +19,14 @@ import type { ComputedInput, InputName } from "./config/inputs"; import { parseRegistriesWithoutCredentials } from "./config/pack-registries"; import type { DependencyCacheRestoreStatusReport } from "./dependency-caching"; import { DocUrl } from "./doc-url"; -import { EnvVar } from "./environment"; +import { EnvVar, getEnv, ReadOnlyEnv, RegistryProxyVars } from "./environment"; import { getRef } from "./git-utils"; +import * as json from "./json"; import type { Logger } from "./logging"; import type { OverlayBaseDatabaseDownloadStats } from "./overlay/caching"; import { getRepositoryNwo } from "./repository"; import type { ToolsSource } from "./setup-codeql"; +import { registryBaseSchema } from "./start-proxy/types"; import { ConfigurationError, getRequiredEnvParam, @@ -185,6 +187,12 @@ export interface StatusReportBase { ml_powered_javascript_queries?: string; /** Ref that the workflow was triggered on. */ ref: string; + /** + * A comma-separated list of private registry types which are configured for CodeQL. + * This only includes registry types we support (as determined by the `start-proxy` action), + * not all that are configured. + */ + registry_types?: string; /** Action runner hardware architecture (context runner.arch). */ runner_arch?: string; /** Available disk space on the runner, in bytes. */ @@ -288,6 +296,50 @@ export interface EventReport { started_at: string; } +/** + * Attempts to retrieve a list of private registry types from the `CODEQL_PROXY_URLS` environment + * variable and returns it as a comma-separated string if successful. Returns `undefined` otherwise. + */ +export function getRegistryTypesFromEnv( + logger: Logger, + env: ReadOnlyEnv = getEnv(), +): string | undefined { + // Try to get the value of the environment variable. + const value = env.getOptional(RegistryProxyVars.PROXY_URLS); + + if (value === undefined) { + return undefined; + } + + // Try to parse the JSON we expect to find in it and return the comma-separated list of + // (unique) registry types. + try { + const data = JSON.parse(value) as unknown; + + // Check that the parsed JSON meets our expectations. + if (!json.isArray(data)) { + logger.debug( + `Expected '${RegistryProxyVars.PROXY_URLS}' to contain a JSON array, but got '${typeof data}'.`, + ); + return undefined; + } + if (!json.validateArray(registryBaseSchema, data)) { + logger.debug( + `Expected '${RegistryProxyVars.PROXY_URLS}' to contain a JSON array of registry objects, but got something else.`, + ); + return undefined; + } + + const types = new Set(data.map((r) => r.type)); + return Array.from(types).sort().join(","); + } catch (err) { + logger.debug( + `Failed to parse '${RegistryProxyVars.PROXY_URLS}': ${getErrorMessage(err)}.`, + ); + return undefined; + } +} + /** * Compose a StatusReport. * @@ -350,6 +402,7 @@ export async function createStatusReportBase( job_name: jobName, job_run_uuid: jobRunUUID, ref, + registry_types: getRegistryTypesFromEnv(logger), runner_os: runnerOs, started_at: workflowStartedAt, status,