From aa80ca0686300d00833eb4f65641ac310b892784 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Tue, 13 Oct 2026 18:59:11 +0100 Subject: [PATCH 1/6] Move `start-proxy` status report handling into its own file --- lib/entry-points.js | 114 +++++++++++---------- src/start-proxy.test.ts | 88 +--------------- src/start-proxy.ts | 142 +------------------------- src/start-proxy/status-report.test.ts | 94 +++++++++++++++++ src/start-proxy/status-report.ts | 140 +++++++++++++++++++++++++ 5 files changed, 298 insertions(+), 280 deletions(-) create mode 100644 src/start-proxy/status-report.test.ts create mode 100644 src/start-proxy/status-report.ts diff --git a/lib/entry-points.js b/lib/entry-points.js index 373cdedc01..f3d3022b0d 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -187524,50 +187524,27 @@ var core25 = __toESM(require_core()); // src/start-proxy.ts var path27 = __toESM(require("path")); -var core24 = __toESM(require_core()); var toolcache4 = __toESM(require_tool_cache()); -// src/start-proxy/validation.ts +// src/start-proxy/status-report.ts var core23 = __toESM(require_core()); -function cloneCredential(schema, obj) { - const result = {}; - for (const key of Object.keys(schema)) { - if (!isDefined2(obj[key])) { - continue; - } - result[key] = obj[key]; - } - return result; -} -function getAuthConfig(config) { - for (const oidcSchema of oidcSchemas) { - if (validateSchema(oidcSchema.schema, config)) { - return cloneCredential(oidcSchema.schema, config); - } - } - if (isToken(config)) { - if (isDefined2(config.token)) { - core23.setSecret(config.token); - } - return cloneCredential(tokenSchema, config); - } else { - let username = void 0; - let password = void 0; - if ("password" in config && isString(config.password)) { - core23.setSecret(config.password); - password = config.password; - } - if ("username" in config && isString(config.username)) { - username = config.username; - } - return { - username, - password +async function sendSuccessStatusReport(startedAt, config, registry_types, logger2) { + const statusReportBase = await createStatusReportBase( + "start-proxy" /* StartProxy */, + "success", + startedAt, + config, + await checkDiskUsage(logger2), + logger2 + ); + if (statusReportBase !== void 0) { + const statusReport = { + ...statusReportBase, + registry_types: registry_types.join(",") }; + await sendStatusReport(statusReport); } } - -// src/start-proxy.ts function getStartProxyErrorMessage(errorType) { switch (errorType) { case 0 /* DownloadFailed */: @@ -187585,23 +187562,6 @@ var StartProxyError = class extends Error { this.errorType = errorType; } }; -async function sendSuccessStatusReport(startedAt, config, registry_types, logger2) { - const statusReportBase = await createStatusReportBase( - "start-proxy" /* StartProxy */, - "success", - startedAt, - config, - await checkDiskUsage(logger2), - logger2 - ); - if (statusReportBase !== void 0) { - const statusReport = { - ...statusReportBase, - registry_types: registry_types.join(",") - }; - await sendStatusReport(statusReport); - } -} function getSafeErrorMessage(error3) { if (error3 instanceof StartProxyError) { return getStartProxyErrorMessage(error3.errorType); @@ -187610,7 +187570,7 @@ function getSafeErrorMessage(error3) { } async function sendFailedStatusReport(logger2, startedAt, language, unwrappedError) { const error3 = wrapError(unwrappedError); - core24.setFailed(`start-proxy action failed: ${error3.message}`); + core23.setFailed(`start-proxy action failed: ${error3.message}`); const statusReportMessage = getSafeErrorMessage(error3); const errorStatusReportBase = await createStatusReportBase( "start-proxy" /* StartProxy */, @@ -187627,6 +187587,48 @@ async function sendFailedStatusReport(logger2, startedAt, language, unwrappedErr await sendStatusReport(errorStatusReportBase); } } + +// src/start-proxy/validation.ts +var core24 = __toESM(require_core()); +function cloneCredential(schema, obj) { + const result = {}; + for (const key of Object.keys(schema)) { + if (!isDefined2(obj[key])) { + continue; + } + result[key] = obj[key]; + } + return result; +} +function getAuthConfig(config) { + for (const oidcSchema of oidcSchemas) { + if (validateSchema(oidcSchema.schema, config)) { + return cloneCredential(oidcSchema.schema, config); + } + } + if (isToken(config)) { + if (isDefined2(config.token)) { + core24.setSecret(config.token); + } + return cloneCredential(tokenSchema, config); + } else { + let username = void 0; + let password = void 0; + if ("password" in config && isString(config.password)) { + core24.setSecret(config.password); + password = config.password; + } + if ("username" in config && isString(config.username)) { + username = config.username; + } + return { + username, + password + }; + } +} + +// src/start-proxy.ts var UPDATEJOB_PROXY = "update-job-proxy"; var UPDATEJOB_PROXY_VERSION = "v2.0.20250624110901"; var UPDATEJOB_PROXY_URL_PREFIX = "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/github/codeql-action/releases/download/codeql-bundle-v2.22.0/"; diff --git a/src/start-proxy.test.ts b/src/start-proxy.test.ts index bd24897136..86bb993481 100644 --- a/src/start-proxy.test.ts +++ b/src/start-proxy.test.ts @@ -1,6 +1,5 @@ import * as filepath from "path"; -import * as core from "@actions/core"; import * as toolcache from "@actions/tool-cache"; import test, { ExecutionContext } from "ava"; import sinon from "sinon"; @@ -15,7 +14,6 @@ import { BuiltInLanguage } from "./languages"; import { getRunnerLogger, Logger } from "./logging"; import { BundlePlatform, getBundlePlatform } from "./platform"; import * as startProxyExports from "./start-proxy"; -import * as statusReport from "./status-report"; import { assertNotLogged, checkExpectedLogMessages, @@ -27,94 +25,10 @@ import { setupTests, withRecordingLoggerAsync, } from "./testing-utils"; -import { - ConfigurationError, - GitHubVariant, - GitHubVersion, - withTmpDir, -} from "./util"; +import { GitHubVariant, GitHubVersion, withTmpDir } from "./util"; setupTests(test); -const sendFailedStatusReportTest = makeMacro({ - exec: async ( - t: ExecutionContext, - err: Error, - expectedMessage: string, - expectedStatus: statusReport.ActionStatus = "failure", - ) => { - const now = new Date(); - - // Override core.setFailed to avoid it setting the program's exit code - sinon.stub(core, "setFailed").returns(); - - const createStatusReportBase = sinon.stub( - statusReport, - "createStatusReportBase", - ); - createStatusReportBase.resolves(undefined); - - await withRecordingLoggerAsync(async (logger) => { - await startProxyExports.sendFailedStatusReport( - logger, - now, - undefined, - err, - ); - - // Check that the stub has been called exactly once, with the expected arguments, - // but not with the message from the error. - sinon.assert.calledOnceWithExactly( - createStatusReportBase, - statusReport.ActionName.StartProxy, - expectedStatus, - now, - sinon.match.any, - sinon.match.any, - sinon.match.any, - expectedMessage, - ); - t.false( - createStatusReportBase.calledWith( - statusReport.ActionName.StartProxy, - expectedStatus, - now, - sinon.match.any, - sinon.match.any, - sinon.match.any, - sinon.match((msg: string) => msg.includes(err.message)), - ), - "createStatusReportBase was called with the error message", - ); - }); - }, - - title: (providedTitle = "") => `sendFailedStatusReport - ${providedTitle}`, -}); - -sendFailedStatusReportTest.serial( - "reports generic error message for non-StartProxyError error", - new Error("Something went wrong today"), - "Error from start-proxy Action omitted (Error).", -); - -sendFailedStatusReportTest.serial( - "reports generic error message for non-StartProxyError error with safe error message", - new Error( - startProxyExports.getStartProxyErrorMessage( - startProxyExports.StartProxyErrorType.DownloadFailed, - ), - ), - "Error from start-proxy Action omitted (Error).", -); - -sendFailedStatusReportTest.serial( - "reports generic error message for ConfigurationError error", - new ConfigurationError("Something went wrong today"), - "Error from start-proxy Action omitted (ConfigurationError).", - "user-error", -); - const toEncodedJSON = (data: any) => Buffer.from(JSON.stringify(data)).toString("base64"); diff --git a/src/start-proxy.ts b/src/start-proxy.ts index 6221c9940f..84ed5fb174 100644 --- a/src/start-proxy.ts +++ b/src/start-proxy.ts @@ -1,6 +1,5 @@ import * as path from "path"; -import * as core from "@actions/core"; import * as toolcache from "@actions/tool-cache"; import { ActionState } from "./action-common"; @@ -11,7 +10,6 @@ import { getGitHubVersion, } from "./api-client"; import * as artifactScanner from "./artifact-scanner"; -import { Config } from "./config-utils"; import * as defaults from "./defaults.json"; import { CodeQLDefaultVersionInfo, @@ -22,6 +20,10 @@ import * as json from "./json"; import { BuiltInLanguage } from "./languages"; import { Logger } from "./logging"; import { BundlePlatform, getBundlePlatform } from "./platform"; +import { + StartProxyError, + StartProxyErrorType, +} from "./start-proxy/status-report"; import { Address, Registry, @@ -32,144 +34,10 @@ import { RawCredential, } from "./start-proxy/types"; import { getAuthConfig } from "./start-proxy/validation"; -import { - ActionName, - createStatusReportBase, - getActionsStatus, - sendStatusReport, - StatusReportBase, -} from "./status-report"; -import * as util from "./util"; import { ConfigurationError, getErrorMessage, isDefined } from "./util"; export * from "./start-proxy/types"; - -/** - * Enumerates specific error types for which we have corresponding error messages that - * are safe to include in status reports. - */ -export enum StartProxyErrorType { - DownloadFailed, - ExtractionFailed, - CacheFailed, -} - -/** - * @returns The error message corresponding to the error type. - */ -export function getStartProxyErrorMessage( - errorType: StartProxyErrorType, -): string { - switch (errorType) { - case StartProxyErrorType.DownloadFailed: - return "Failed to download proxy archive."; - case StartProxyErrorType.ExtractionFailed: - return "Failed to extract proxy archive."; - case StartProxyErrorType.CacheFailed: - return "Failed to add proxy to toolcache"; - } -} - -/** - * We want to avoid accidentally leaking secrets that may be contained in exception - * messages in the `start-proxy` action. Consequently, we don't report the messages - * of arbitrary exceptions. This type of error ensures that the message is one from - * `StartProxyErrorType` and therefore safe to include in a status report. - */ -export class StartProxyError extends Error { - public readonly errorType: StartProxyErrorType; - - constructor(errorType: StartProxyErrorType) { - super(); - this.errorType = errorType; - } -} - -/** - * Sends a status report for the `start-proxy` action indicating a successful outcome. - * - * @param startedAt When the action was started. - * @param config The configuration used. - * @param registry_types The types of registries that are configured. - * @param logger The logger to use. - */ -export async function sendSuccessStatusReport( - startedAt: Date, - config: Partial, - registry_types: string[], - logger: Logger, -) { - const statusReportBase = await createStatusReportBase( - ActionName.StartProxy, - "success", - startedAt, - config, - await util.checkDiskUsage(logger), - logger, - ); - if (statusReportBase !== undefined) { - const statusReport: StatusReportBase = { - ...statusReportBase, - registry_types: registry_types.join(","), - }; - await sendStatusReport(statusReport); - } -} - -/** - * Returns an error message for `error` that can safely be reported in a status report, - * i.e. that does not contain sensitive information. - * - * @param error The error for which to get an error message. - */ -export function getSafeErrorMessage(error: Error): string { - // If the error is a `StartProxyError`, resolve the error type to the corresponding - // error message. - if (error instanceof StartProxyError) { - return getStartProxyErrorMessage(error.errorType); - } - - // Otherwise, omit the actual error message. - return `Error from start-proxy Action omitted (${error.constructor.name}).`; -} - -/** - * Sends a status report for the `start-proxy` action indicating a failure. - * - * @param logger The logger to use. - * @param startedAt When the action was started. - * @param language The language provided as input, if any. - * @param unwrappedError The exception that was thrown. - */ -export async function sendFailedStatusReport( - logger: Logger, - startedAt: Date, - language: BuiltInLanguage | undefined, - unwrappedError: unknown, -) { - const error = util.wrapError(unwrappedError); - core.setFailed(`start-proxy action failed: ${error.message}`); - - // To avoid the possibility of leaking sensitive information into the telemetry, - // we don't include arbitrary error messages. Instead, `getSafeErrorMessage` will - // return a generic message that includes the type of the error, unless it can decide - // that the message is safe to include. - const statusReportMessage = getSafeErrorMessage(error); - const errorStatusReportBase = await createStatusReportBase( - ActionName.StartProxy, - getActionsStatus(error), - startedAt, - { - languages: language === undefined ? undefined : [language], - }, - await util.checkDiskUsage(logger), - logger, - statusReportMessage, - ); - if (errorStatusReportBase !== undefined) { - await sendStatusReport(errorStatusReportBase); - } -} +export * from "./start-proxy/status-report"; export const UPDATEJOB_PROXY = "update-job-proxy"; export const UPDATEJOB_PROXY_VERSION = "v2.0.20250624110901"; diff --git a/src/start-proxy/status-report.test.ts b/src/start-proxy/status-report.test.ts new file mode 100644 index 0000000000..15772872bc --- /dev/null +++ b/src/start-proxy/status-report.test.ts @@ -0,0 +1,94 @@ +import * as core from "@actions/core"; +import test, { ExecutionContext } from "ava"; +import sinon from "sinon"; + +import * as statusReport from "../status-report"; +import { + makeMacro, + setupTests, + withRecordingLoggerAsync, +} from "../testing-utils"; +import { ConfigurationError } from "../util"; + +import * as startProxyExports from "./status-report"; + +setupTests(test); + +const sendFailedStatusReportTest = makeMacro({ + exec: async ( + t: ExecutionContext, + err: Error, + expectedMessage: string, + expectedStatus: statusReport.ActionStatus = "failure", + ) => { + const now = new Date(); + + // Override core.setFailed to avoid it setting the program's exit code + sinon.stub(core, "setFailed").returns(); + + const createStatusReportBase = sinon.stub( + statusReport, + "createStatusReportBase", + ); + createStatusReportBase.resolves(undefined); + + await withRecordingLoggerAsync(async (logger) => { + await startProxyExports.sendFailedStatusReport( + logger, + now, + undefined, + err, + ); + + // Check that the stub has been called exactly once, with the expected arguments, + // but not with the message from the error. + sinon.assert.calledOnceWithExactly( + createStatusReportBase, + statusReport.ActionName.StartProxy, + expectedStatus, + now, + sinon.match.any, + sinon.match.any, + sinon.match.any, + expectedMessage, + ); + t.false( + createStatusReportBase.calledWith( + statusReport.ActionName.StartProxy, + expectedStatus, + now, + sinon.match.any, + sinon.match.any, + sinon.match.any, + sinon.match((msg: string) => msg.includes(err.message)), + ), + "createStatusReportBase was called with the error message", + ); + }); + }, + + title: (providedTitle = "") => `sendFailedStatusReport - ${providedTitle}`, +}); + +sendFailedStatusReportTest.serial( + "reports generic error message for non-StartProxyError error", + new Error("Something went wrong today"), + "Error from start-proxy Action omitted (Error).", +); + +sendFailedStatusReportTest.serial( + "reports generic error message for non-StartProxyError error with safe error message", + new Error( + startProxyExports.getStartProxyErrorMessage( + startProxyExports.StartProxyErrorType.DownloadFailed, + ), + ), + "Error from start-proxy Action omitted (Error).", +); + +sendFailedStatusReportTest.serial( + "reports generic error message for ConfigurationError error", + new ConfigurationError("Something went wrong today"), + "Error from start-proxy Action omitted (ConfigurationError).", + "user-error", +); diff --git a/src/start-proxy/status-report.ts b/src/start-proxy/status-report.ts new file mode 100644 index 0000000000..0810780343 --- /dev/null +++ b/src/start-proxy/status-report.ts @@ -0,0 +1,140 @@ +import * as core from "@actions/core"; + +import type { Config } from "../config-utils"; +import type { BuiltInLanguage } from "../languages"; +import type { Logger } from "../logging"; +import { + ActionName, + createStatusReportBase, + getActionsStatus, + sendStatusReport, + type StatusReportBase, +} from "../status-report"; +import * as util from "../util"; + +/** + * Sends a status report for the `start-proxy` action indicating a successful outcome. + * + * @param startedAt When the action was started. + * @param config The configuration used. + * @param registry_types The types of registries that are configured. + * @param logger The logger to use. + */ +export async function sendSuccessStatusReport( + startedAt: Date, + config: Partial, + registry_types: string[], + logger: Logger, +) { + const statusReportBase = await createStatusReportBase( + ActionName.StartProxy, + "success", + startedAt, + config, + await util.checkDiskUsage(logger), + logger, + ); + if (statusReportBase !== undefined) { + const statusReport: StatusReportBase = { + ...statusReportBase, + registry_types: registry_types.join(","), + }; + await sendStatusReport(statusReport); + } +} + +/** + * Enumerates specific error types for which we have corresponding error messages that + * are safe to include in status reports. + */ +export enum StartProxyErrorType { + DownloadFailed, + ExtractionFailed, + CacheFailed, +} + +/** + * @returns The error message corresponding to the error type. + */ +export function getStartProxyErrorMessage( + errorType: StartProxyErrorType, +): string { + switch (errorType) { + case StartProxyErrorType.DownloadFailed: + return "Failed to download proxy archive."; + case StartProxyErrorType.ExtractionFailed: + return "Failed to extract proxy archive."; + case StartProxyErrorType.CacheFailed: + return "Failed to add proxy to toolcache"; + } +} + +/** + * We want to avoid accidentally leaking secrets that may be contained in exception + * messages in the `start-proxy` action. Consequently, we don't report the messages + * of arbitrary exceptions. This type of error ensures that the message is one from + * `StartProxyErrorType` and therefore safe to include in a status report. + */ +export class StartProxyError extends Error { + public readonly errorType: StartProxyErrorType; + + constructor(errorType: StartProxyErrorType) { + super(); + this.errorType = errorType; + } +} + +/** + * Returns an error message for `error` that can safely be reported in a status report, + * i.e. that does not contain sensitive information. + * + * @param error The error for which to get an error message. + */ +export function getSafeErrorMessage(error: Error): string { + // If the error is a `StartProxyError`, resolve the error type to the corresponding + // error message. + if (error instanceof StartProxyError) { + return getStartProxyErrorMessage(error.errorType); + } + + // Otherwise, omit the actual error message. + return `Error from start-proxy Action omitted (${error.constructor.name}).`; +} + +/** + * Sends a status report for the `start-proxy` action indicating a failure. + * + * @param logger The logger to use. + * @param startedAt When the action was started. + * @param language The language provided as input, if any. + * @param unwrappedError The exception that was thrown. + */ +export async function sendFailedStatusReport( + logger: Logger, + startedAt: Date, + language: BuiltInLanguage | undefined, + unwrappedError: unknown, +) { + const error = util.wrapError(unwrappedError); + core.setFailed(`start-proxy action failed: ${error.message}`); + + // To avoid the possibility of leaking sensitive information into the telemetry, + // we don't include arbitrary error messages. Instead, `getSafeErrorMessage` will + // return a generic message that includes the type of the error, unless it can decide + // that the message is safe to include. + const statusReportMessage = getSafeErrorMessage(error); + const errorStatusReportBase = await createStatusReportBase( + ActionName.StartProxy, + getActionsStatus(error), + startedAt, + { + languages: language === undefined ? undefined : [language], + }, + await util.checkDiskUsage(logger), + logger, + statusReportMessage, + ); + if (errorStatusReportBase !== undefined) { + await sendStatusReport(errorStatusReportBase); + } +} From 6308e366c9d998b0750856f929c0c17ed1014b89 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Tue, 13 Oct 2026 19:06:14 +0100 Subject: [PATCH 2/6] Return release version from `getProxyBinaryPath` --- lib/entry-points.js | 7 +++++-- src/start-proxy-action.ts | 2 +- src/start-proxy.test.ts | 16 +++++++++++----- src/start-proxy.ts | 20 +++++++++++++++++--- 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/lib/entry-points.js b/lib/entry-points.js index f3d3022b0d..00309ef50d 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -187870,7 +187870,10 @@ async function getProxyBinaryPath(action) { proxyInfo.version ); } - return path27.join(proxyBin, proxyFileName); + return { + path: path27.join(proxyBin, proxyFileName), + version: proxyInfo.version + }; } // src/start-proxy/ca.ts @@ -188235,7 +188238,7 @@ async function run7(action) { const actionWithFeatures = extendActionState(action, { features }); const proxyBin = await getProxyBinaryPath(actionWithFeatures); const proxyInfo = await startProxy( - proxyBin, + proxyBin.path, proxyConfig, proxyLogFilePath, logger2 diff --git a/src/start-proxy-action.ts b/src/start-proxy-action.ts index bc3351da53..9f96e1503c 100644 --- a/src/start-proxy-action.ts +++ b/src/start-proxy-action.ts @@ -106,7 +106,7 @@ async function run(action: ActionState<["Base", "Logger", "Env", "Actions"]>) { const actionWithFeatures = extendActionState(action, { features }); const proxyBin = await getProxyBinaryPath(actionWithFeatures); const proxyInfo = await startProxy( - proxyBin, + proxyBin.path, proxyConfig, proxyLogFilePath, logger, diff --git a/src/start-proxy.test.ts b/src/start-proxy.test.ts index 86bb993481..66edd0c62f 100644 --- a/src/start-proxy.test.ts +++ b/src/start-proxy.test.ts @@ -886,9 +886,10 @@ test.serial( t.assert(path); t.is( - path, + path.path, filepath.join(toolcachePath, startProxyExports.getProxyFilename()), ); + t.is(path.version, startProxyExports.UPDATEJOB_PROXY_VERSION); }); }, ); @@ -939,9 +940,10 @@ test.serial( t.assert(cacheDir.calledOnceWith(extractedPath)); t.assert(path); t.is( - path, + path.path, filepath.join(toolcachePath, startProxyExports.getProxyFilename()), ); + t.is(path.version, defaults.cliVersion); checkExpectedLogMessages(t, logger.messages, [ `Found '${startProxyExports.getProxyPackage(getTestPlatform(state))}' in release '${defaults.bundleVersion}' at '${downloadUrl}'`, @@ -954,7 +956,8 @@ test.serial( async (t) => { const logger = new RecordingLogger(); const state = initAllState({ logger }); - const expectedTag = "codeql-bundle-v2.20.1"; + const expectedVersion = "2.20.1"; + const expectedTag = `codeql-bundle-v${expectedVersion}`; const expectedParams = { owner: "github", repo: "codeql-action", @@ -1012,7 +1015,9 @@ test.serial( const getDefaultCliVersion = sinon .stub(features, "getEnabledDefaultCliVersions") .resolves({ - enabledVersions: [{ cliVersion: "2.20.1", tagName: expectedTag }], + enabledVersions: [ + { cliVersion: expectedVersion, tagName: expectedTag }, + ], }); const path = await startProxyExports.getProxyBinaryPath({ ...state, @@ -1033,9 +1038,10 @@ test.serial( t.assert(path); t.is( - path, + path.path, filepath.join(toolcachePath, startProxyExports.getProxyFilename()), ); + t.is(path.version, expectedVersion); }); checkExpectedLogMessages(t, logger.messages, [ diff --git a/src/start-proxy.ts b/src/start-proxy.ts index 84ed5fb174..e22346d3ac 100644 --- a/src/start-proxy.ts +++ b/src/start-proxy.ts @@ -428,17 +428,27 @@ export function getProxyFilename() { : UPDATEJOB_PROXY; } +export type ProxyBinary = { + /** The path to the proxy binary to run. */ + path: string; + /** + * The version associated with the proxy binary. + * This usually corresponds to the CodeQL CLI release that the binary was obtained from. + */ + version: string; +}; + /** * Gets a path to the proxy binary. If possible, this function will find the proxy in the * runner's tool cache. Otherwise, it downloads and extracts the proxy binary, * and stores it in the tool cache. * * @param action The action state. - * @returns The path to the proxy binary. + * @returns The path to the proxy binary and its version. */ export async function getProxyBinaryPath( action: ActionState<["Base", "Logger", "FeatureFlags"]>, -): Promise { +): Promise { const logger = action.logger; const proxyFileName = getProxyFilename(); const proxyInfo = await getDownloadUrl(action); @@ -460,5 +470,9 @@ export async function getProxyBinaryPath( proxyInfo.version, ); } - return path.join(proxyBin, proxyFileName); + + return { + path: path.join(proxyBin, proxyFileName), + version: proxyInfo.version, + }; } From 43db2c064ca6c19f843f4791c5cfa3fc73138669 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Tue, 13 Oct 2026 19:12:13 +0100 Subject: [PATCH 3/6] Include proxy version in telemetry --- lib/entry-points.js | 6 ++++-- src/start-proxy-action.ts | 1 + src/start-proxy/status-report.ts | 15 ++++++++++++++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/entry-points.js b/lib/entry-points.js index 00309ef50d..d4c353c9f3 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -187528,7 +187528,7 @@ var toolcache4 = __toESM(require_tool_cache()); // src/start-proxy/status-report.ts var core23 = __toESM(require_core()); -async function sendSuccessStatusReport(startedAt, config, registry_types, logger2) { +async function sendSuccessStatusReport(startedAt, config, registry_types, status, logger2) { const statusReportBase = await createStatusReportBase( "start-proxy" /* StartProxy */, "success", @@ -187540,7 +187540,8 @@ async function sendSuccessStatusReport(startedAt, config, registry_types, logger if (statusReportBase !== void 0) { const statusReport = { ...statusReportBase, - registry_types: registry_types.join(",") + registry_types: registry_types.join(","), + start_proxy: status }; await sendStatusReport(statusReport); } @@ -188250,6 +188251,7 @@ async function run7(action) { languages: language === void 0 ? void 0 : [language] }, proxyConfig.all_credentials.map((c) => c.type), + { proxy_version: proxyBin.version }, logger2 ); } catch (unwrappedError) { diff --git a/src/start-proxy-action.ts b/src/start-proxy-action.ts index 9f96e1503c..8a5f5af146 100644 --- a/src/start-proxy-action.ts +++ b/src/start-proxy-action.ts @@ -122,6 +122,7 @@ async function run(action: ActionState<["Base", "Logger", "Env", "Actions"]>) { languages: language === undefined ? undefined : [language], }, proxyConfig.all_credentials.map((c) => c.type), + { proxy_version: proxyBin.version }, logger, ); } catch (unwrappedError) { diff --git a/src/start-proxy/status-report.ts b/src/start-proxy/status-report.ts index 0810780343..685be3dbee 100644 --- a/src/start-proxy/status-report.ts +++ b/src/start-proxy/status-report.ts @@ -12,18 +12,30 @@ import { } from "../status-report"; import * as util from "../util"; +/** Additional status report data from `start-proxy`. */ +export interface StartProxyStatus { + proxy_version: string; +} + +/** The type of successful status reports from `start-proxy`. */ +type StartProxySuccessReport = StatusReportBase & { + start_proxy: StartProxyStatus; +}; + /** * Sends a status report for the `start-proxy` action indicating a successful outcome. * * @param startedAt When the action was started. * @param config The configuration used. * @param registry_types The types of registries that are configured. + * @param status Status information. * @param logger The logger to use. */ export async function sendSuccessStatusReport( startedAt: Date, config: Partial, registry_types: string[], + status: StartProxyStatus, logger: Logger, ) { const statusReportBase = await createStatusReportBase( @@ -35,9 +47,10 @@ export async function sendSuccessStatusReport( logger, ); if (statusReportBase !== undefined) { - const statusReport: StatusReportBase = { + const statusReport: StartProxySuccessReport = { ...statusReportBase, registry_types: registry_types.join(","), + start_proxy: status, }; await sendStatusReport(statusReport); } From 6aee1f8ef9c4cb021dafe1c97ea41122dd03b98b Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Wed, 14 Oct 2026 10:30:14 +0100 Subject: [PATCH 4/6] Sort `registry_types` in status report --- lib/entry-points.js | 2 +- src/start-proxy/status-report.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/entry-points.js b/lib/entry-points.js index d4c353c9f3..1b67d38e13 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -187540,7 +187540,7 @@ async function sendSuccessStatusReport(startedAt, config, registry_types, status if (statusReportBase !== void 0) { const statusReport = { ...statusReportBase, - registry_types: registry_types.join(","), + registry_types: registry_types.sort().join(","), start_proxy: status }; await sendStatusReport(statusReport); diff --git a/src/start-proxy/status-report.ts b/src/start-proxy/status-report.ts index 685be3dbee..76ad3c8da3 100644 --- a/src/start-proxy/status-report.ts +++ b/src/start-proxy/status-report.ts @@ -49,7 +49,7 @@ export async function sendSuccessStatusReport( if (statusReportBase !== undefined) { const statusReport: StartProxySuccessReport = { ...statusReportBase, - registry_types: registry_types.join(","), + registry_types: registry_types.sort().join(","), start_proxy: status, }; await sendStatusReport(statusReport); From 15800587e38e5a271757432b15bc7ab06517df43 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Wed, 14 Oct 2026 10:31:29 +0100 Subject: [PATCH 5/6] Add unit test for `sendSuccessStatusReport` --- src/start-proxy/status-report.test.ts | 42 +++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/start-proxy/status-report.test.ts b/src/start-proxy/status-report.test.ts index 15772872bc..c734783964 100644 --- a/src/start-proxy/status-report.test.ts +++ b/src/start-proxy/status-report.test.ts @@ -4,7 +4,9 @@ import sinon from "sinon"; import * as statusReport from "../status-report"; import { + createTestConfig, makeMacro, + RecordingLogger, setupTests, withRecordingLoggerAsync, } from "../testing-utils"; @@ -14,6 +16,46 @@ import * as startProxyExports from "./status-report"; setupTests(test); +test.serial("sendSuccessStatusReport", async (t) => { + const logger = new RecordingLogger(); + const now = new Date(); + const config = createTestConfig({}); + const proxy_version = "1.2.3"; + const registry_types = ["git_source", "docker_registry"]; + + const createStatusReportBase = sinon.stub( + statusReport, + "createStatusReportBase", + ); + createStatusReportBase.resolves({ + action_name: statusReport.ActionName.StartProxy, + } as statusReport.StatusReportBase); + + const sendStatusReport = sinon + .stub(statusReport, "sendStatusReport") + .resolves(); + + await startProxyExports.sendSuccessStatusReport( + now, + config, + registry_types, + { proxy_version }, + logger, + ); + + t.is(createStatusReportBase.callCount, 1); + + t.is(sendStatusReport.callCount, 1); + t.is(sendStatusReport.args[0].length, 1); + + const arg = sendStatusReport.args[0][0]; + t.like(arg, { + action_name: statusReport.ActionName.StartProxy, + registry_types: "docker_registry,git_source", + start_proxy: { proxy_version }, + }); +}); + const sendFailedStatusReportTest = makeMacro({ exec: async ( t: ExecutionContext, From 8dec0b9f88b61a76f5fc97056ee8e7dc8a1a8022 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Thu, 15 Oct 2026 11:03:10 +0100 Subject: [PATCH 6/6] Rename `path` to `result` in tests --- src/start-proxy.test.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/start-proxy.test.ts b/src/start-proxy.test.ts index 22a9537cd4..78704a38a6 100644 --- a/src/start-proxy.test.ts +++ b/src/start-proxy.test.ts @@ -935,17 +935,17 @@ test.serial( sinon.stub(actionsUtil, "isRunningLocalAction").returns(true); const features = mockOfflineFeatures(tempDir, logger); - const path = await startProxyExports.getProxyBinaryPath( + const result = await startProxyExports.getProxyBinaryPath( initAllState({ logger, features }), SAMPLE_DOTCOM_API_DETAILS, ); - t.assert(path); + t.assert(result); t.is( - path.path, + result.path, filepath.join(toolcachePath, startProxyExports.getProxyFilename()), ); - t.is(path.version, defaults.cliVersion); + t.is(result.version, defaults.cliVersion); }); }, ); @@ -982,7 +982,7 @@ test.serial( sinon.stub(actionsUtil, "isRunningLocalAction").returns(false); state.env.set(ActionsEnvVars.GITHUB_ACTION_REPOSITORY, "test/repo"); - const path = await startProxyExports.getProxyBinaryPath( + const result = await startProxyExports.getProxyBinaryPath( { ...state, features: createFeatures([]), @@ -999,12 +999,12 @@ test.serial( st.onceWith(extractTar, archivePath); st.onceWith(cacheDir, extractedPath); - t.assert(path); + t.assert(result); t.is( - path.path, + result.path, filepath.join(toolcachePath, startProxyExports.getProxyFilename()), ); - t.is(path.version, defaults.cliVersion); + t.is(result.version, defaults.cliVersion); checkExpectedLogMessages(t, logger.messages, [ `Found private registry proxy ${startProxyExports.getProxyPackage(getTestPlatform(state))} in test/repo on https://github.com with URL ${downloadUrl}`, @@ -1080,7 +1080,7 @@ test.serial( sinon.stub(actionsUtil, "isRunningLocalAction").returns(false); state.env.set(ActionsEnvVars.GITHUB_ACTION_REPOSITORY, "test/repo"); - const path = await startProxyExports.getProxyBinaryPath( + const result = await startProxyExports.getProxyBinaryPath( { ...state, features, @@ -1098,12 +1098,12 @@ test.serial( st.onceWith(extractTar, archivePath); st.onceWith(cacheDir, extractedPath); - t.assert(path); + t.assert(result); t.is( - path.path, + result.path, filepath.join(toolcachePath, startProxyExports.getProxyFilename()), ); - t.is(path.version, expectedVersion); + t.is(result.version, expectedVersion); }); checkExpectedLogMessages(t, logger.messages, [