Merge pull request #4233 from github/mbg/start-proxy/report-version

Report release version the proxy is taken from in `start-proxy` telemetry
This commit is contained in:
Michael B. Gale
2026-10-15 10:15:24 +00:00
committed by GitHub
6 changed files with 402 additions and 304 deletions

137
lib/entry-points.js generated
View File

@@ -187602,11 +187602,73 @@ 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());
async function sendSuccessStatusReport(startedAt, config, registry_types, status, 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.sort().join(","),
start_proxy: status
};
await sendStatusReport(statusReport);
}
}
function getStartProxyErrorMessage(errorType) {
switch (errorType) {
case 0 /* DownloadFailed */:
return "Failed to download proxy archive.";
case 1 /* ExtractionFailed */:
return "Failed to extract proxy archive.";
case 2 /* CacheFailed */:
return "Failed to add proxy to toolcache";
}
}
var StartProxyError = class extends Error {
errorType;
constructor(errorType) {
super();
this.errorType = errorType;
}
};
function getSafeErrorMessage(error3) {
if (error3 instanceof StartProxyError) {
return getStartProxyErrorMessage(error3.errorType);
}
return `Error from start-proxy Action omitted (${error3.constructor.name}).`;
}
async function sendFailedStatusReport(logger2, startedAt, language, unwrappedError) {
const error3 = wrapError(unwrappedError);
core23.setFailed(`start-proxy action failed: ${error3.message}`);
const statusReportMessage = getSafeErrorMessage(error3);
const errorStatusReportBase = await createStatusReportBase(
"start-proxy" /* StartProxy */,
getActionsStatus(error3),
startedAt,
{
languages: language === void 0 ? void 0 : [language]
},
await checkDiskUsage(logger2),
logger2,
statusReportMessage
);
if (errorStatusReportBase !== void 0) {
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)) {
@@ -187625,14 +187687,14 @@ function getAuthConfig(config) {
}
if (isToken(config)) {
if (isDefined2(config.token)) {
core23.setSecret(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)) {
core23.setSecret(config.password);
core24.setSecret(config.password);
password = config.password;
}
if ("username" in config && isString(config.username)) {
@@ -187646,65 +187708,6 @@ function getAuthConfig(config) {
}
// src/start-proxy.ts
function getStartProxyErrorMessage(errorType) {
switch (errorType) {
case 0 /* DownloadFailed */:
return "Failed to download proxy archive.";
case 1 /* ExtractionFailed */:
return "Failed to extract proxy archive.";
case 2 /* CacheFailed */:
return "Failed to add proxy to toolcache";
}
}
var StartProxyError = class extends Error {
errorType;
constructor(errorType) {
super();
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);
}
return `Error from start-proxy Action omitted (${error3.constructor.name}).`;
}
async function sendFailedStatusReport(logger2, startedAt, language, unwrappedError) {
const error3 = wrapError(unwrappedError);
core24.setFailed(`start-proxy action failed: ${error3.message}`);
const statusReportMessage = getSafeErrorMessage(error3);
const errorStatusReportBase = await createStatusReportBase(
"start-proxy" /* StartProxy */,
getActionsStatus(error3),
startedAt,
{
languages: language === void 0 ? void 0 : [language]
},
await checkDiskUsage(logger2),
logger2,
statusReportMessage
);
if (errorStatusReportBase !== void 0) {
await sendStatusReport(errorStatusReportBase);
}
}
var UPDATEJOB_PROXY = "update-job-proxy";
function isPAT(value) {
return isAuthToken(value, [
@@ -187935,7 +187938,10 @@ async function getProxyBinaryPath(action, apiDetails) {
);
logger2.info(`Extracted the authentication proxy to '${proxyDir}'.`);
}
return path27.join(proxyDir, proxyFileName);
return {
path: path27.join(proxyDir, proxyFileName),
version: proxyInfo.version
};
} finally {
logger2.endGroup();
}
@@ -188306,7 +188312,7 @@ async function run7(action) {
getApiDetails(action.env)
);
const proxyInfo = await startProxy(
proxyBin,
proxyBin.path,
proxyConfig,
proxyLogFilePath,
logger2
@@ -188318,6 +188324,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) {

View File

@@ -109,7 +109,7 @@ async function run(action: ActionState<["Base", "Logger", "Env", "Actions"]>) {
getApiDetails(action.env),
);
const proxyInfo = await startProxy(
proxyBin,
proxyBin.path,
proxyConfig,
proxyLogFilePath,
logger,
@@ -125,6 +125,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) {

View File

@@ -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";
@@ -18,7 +17,6 @@ import { getRunnerLogger, Logger } from "./logging";
import { BundlePlatform, getBundlePlatform } from "./platform";
import * as repository from "./setup/repository";
import * as startProxyExports from "./start-proxy";
import * as statusReport from "./status-report";
import {
addSinonAssertions,
assertNotLogged,
@@ -32,94 +30,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<unknown>,
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");
@@ -1021,16 +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,
result.path,
filepath.join(toolcachePath, startProxyExports.getProxyFilename()),
);
t.is(result.version, defaults.cliVersion);
});
},
);
@@ -1067,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([]),
@@ -1084,11 +999,12 @@ test.serial(
st.onceWith(extractTar, archivePath);
st.onceWith(cacheDir, extractedPath);
t.assert(path);
t.assert(result);
t.is(
path,
result.path,
filepath.join(toolcachePath, startProxyExports.getProxyFilename()),
);
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}`,
@@ -1101,7 +1017,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: "test",
repo: "repo",
@@ -1154,14 +1071,16 @@ test.serial(
const getDefaultCliVersion = sinon
.stub(features, "getEnabledDefaultCliVersions")
.resolves({
enabledVersions: [{ cliVersion: "2.20.1", tagName: expectedTag }],
enabledVersions: [
{ cliVersion: expectedVersion, tagName: expectedTag },
],
});
// Stub this to always return `false` so we force `getCodeQLAssetDownloadURL` to use the (mocked) API.
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,
@@ -1179,11 +1098,12 @@ test.serial(
st.onceWith(extractTar, archivePath);
st.onceWith(cacheDir, extractedPath);
t.assert(path);
t.assert(result);
t.is(
path,
result.path,
filepath.join(toolcachePath, startProxyExports.getProxyFilename()),
);
t.is(result.version, expectedVersion);
});
checkExpectedLogMessages(t, logger.messages, [

View File

@@ -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";
@@ -10,7 +9,6 @@ import {
GitHubApiDetails,
} 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 { BuiltInLanguage } from "./languages";
import { Logger } from "./logging";
import { BundlePlatform, getBundlePlatform } from "./platform";
import { getCodeQLAssetDownloadURL } from "./setup/repository";
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<Config>,
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";
/** The basename of the proxy artifacts. */
export const UPDATEJOB_PROXY = "update-job-proxy";
@@ -529,18 +397,28 @@ 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", "ReadOnlyEnv", "Logger", "FeatureFlags"]>,
apiDetails: GitHubApiDetails,
): Promise<string> {
): Promise<ProxyBinary> {
const logger = action.logger;
logger.startGroup("Setting up authentication proxy");
@@ -578,7 +456,10 @@ export async function getProxyBinaryPath(
logger.info(`Extracted the authentication proxy to '${proxyDir}'.`);
}
return path.join(proxyDir, proxyFileName);
return {
path: path.join(proxyDir, proxyFileName),
version: proxyInfo.version,
};
} finally {
logger.endGroup();
}

View File

@@ -0,0 +1,136 @@
import * as core from "@actions/core";
import test, { ExecutionContext } from "ava";
import sinon from "sinon";
import * as statusReport from "../status-report";
import {
createTestConfig,
makeMacro,
RecordingLogger,
setupTests,
withRecordingLoggerAsync,
} from "../testing-utils";
import { ConfigurationError } from "../util";
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<unknown>,
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",
);

View File

@@ -0,0 +1,153 @@
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";
/** 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<Config>,
registry_types: string[],
status: StartProxyStatus,
logger: Logger,
) {
const statusReportBase = await createStatusReportBase(
ActionName.StartProxy,
"success",
startedAt,
config,
await util.checkDiskUsage(logger),
logger,
);
if (statusReportBase !== undefined) {
const statusReport: StartProxySuccessReport = {
...statusReportBase,
registry_types: registry_types.sort().join(","),
start_proxy: status,
};
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);
}
}