Merge pull request #3171 from github/mbg/start-proxy/telemetry

Add basic telemetry for `start-proxy` Action
This commit is contained in:
Michael B. Gale
2025-10-02 11:23:10 +01:00
committed by GitHub
3 changed files with 46993 additions and 755 deletions
+46852 -674
View File
File diff suppressed because it is too large Load Diff
+139 -80
View File
@@ -14,6 +14,13 @@ import {
getDownloadUrl,
UPDATEJOB_PROXY,
} from "./start-proxy";
import {
ActionName,
createStatusReportBase,
getActionsStatus,
sendStatusReport,
StatusReportBase,
} from "./status-report";
import * as util from "./util";
const KEY_SIZE = 2048;
@@ -83,46 +90,102 @@ function generateCertificateAuthority(): CertificateAuthority {
return { cert: pem, key };
}
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;
}
async function sendSuccessStatusReport(
startedAt: Date,
registry_types: string[],
logger: Logger,
) {
const statusReportBase = await createStatusReportBase(
ActionName.StartProxy,
"success",
startedAt,
undefined,
await util.checkDiskUsage(logger),
logger,
);
if (statusReportBase !== undefined) {
const statusReport: StartProxyStatus = {
...statusReportBase,
registry_types: registry_types.join(","),
};
await sendStatusReport(statusReport);
}
}
async function runWrapper() {
const startedAt = new Date();
// Make inputs accessible in the `post` step.
actionsUtil.persistInputs();
const logger = getActionsLogger();
// Setup logging for the proxy
const tempDir = actionsUtil.getTemporaryDirectory();
const proxyLogFilePath = path.resolve(tempDir, "proxy.log");
core.saveState("proxy-log-file", proxyLogFilePath);
try {
// Setup logging for the proxy
const tempDir = actionsUtil.getTemporaryDirectory();
const proxyLogFilePath = path.resolve(tempDir, "proxy.log");
core.saveState("proxy-log-file", proxyLogFilePath);
// Get the configuration options
const credentials = getCredentials(
logger,
actionsUtil.getOptionalInput("registry_secrets"),
actionsUtil.getOptionalInput("registries_credentials"),
actionsUtil.getOptionalInput("language"),
);
// Get the configuration options
const credentials = getCredentials(
logger,
actionsUtil.getOptionalInput("registry_secrets"),
actionsUtil.getOptionalInput("registries_credentials"),
actionsUtil.getOptionalInput("language"),
);
if (credentials.length === 0) {
logger.info("No credentials found, skipping proxy setup.");
return;
if (credentials.length === 0) {
logger.info("No credentials found, skipping proxy setup.");
return;
}
logger.info(
`Credentials loaded for the following registries:\n ${credentials
.map((c) => credentialToStr(c))
.join("\n")}`,
);
const ca = generateCertificateAuthority();
const proxyConfig: ProxyConfig = {
all_credentials: credentials,
ca,
};
// Start the Proxy
const proxyBin = await getProxyBinaryPath(logger);
await startProxy(proxyBin, proxyConfig, proxyLogFilePath, logger);
// Report success if we have reached this point.
await sendSuccessStatusReport(
startedAt,
proxyConfig.all_credentials.map((c) => c.type),
logger,
);
} catch (unwrappedError) {
const error = util.wrapError(unwrappedError);
core.setFailed(`start-proxy action failed: ${error.message}`);
// We skip sending the error message and stack trace here to avoid the possibility
// of leaking any sensitive information into the telemetry.
const errorStatusReportBase = await createStatusReportBase(
ActionName.StartProxy,
getActionsStatus(error),
startedAt,
undefined,
await util.checkDiskUsage(logger),
logger,
);
if (errorStatusReportBase !== undefined) {
await sendStatusReport(errorStatusReportBase);
}
}
logger.info(
`Credentials loaded for the following registries:\n ${credentials
.map((c) => credentialToStr(c))
.join("\n")}`,
);
const ca = generateCertificateAuthority();
const proxyConfig: ProxyConfig = {
all_credentials: credentials,
ca,
};
// Start the Proxy
const proxyBin = await getProxyBinaryPath(logger);
await startProxy(proxyBin, proxyConfig, proxyLogFilePath, logger);
}
async function startProxy(
@@ -133,57 +196,53 @@ async function startProxy(
) {
const host = "127.0.0.1";
let port = 49152;
try {
let subprocess: ChildProcess | undefined = undefined;
let tries = 5;
let subprocessError: Error | undefined = undefined;
while (tries-- > 0 && !subprocess && !subprocessError) {
subprocess = spawn(
binPath,
["-addr", `${host}:${port}`, "-config", "-", "-logfile", logFilePath],
{
detached: true,
stdio: ["pipe", "ignore", "ignore"],
},
);
subprocess.unref();
if (subprocess.pid) {
core.saveState("proxy-process-pid", `${subprocess.pid}`);
let subprocess: ChildProcess | undefined = undefined;
let tries = 5;
let subprocessError: Error | undefined = undefined;
while (tries-- > 0 && !subprocess && !subprocessError) {
subprocess = spawn(
binPath,
["-addr", `${host}:${port}`, "-config", "-", "-logfile", logFilePath],
{
detached: true,
stdio: ["pipe", "ignore", "ignore"],
},
);
subprocess.unref();
if (subprocess.pid) {
core.saveState("proxy-process-pid", `${subprocess.pid}`);
}
subprocess.on("error", (error) => {
subprocessError = error;
});
subprocess.on("exit", (code) => {
if (code !== 0) {
// If the proxy failed to start, try a different port from the ephemeral range [49152, 65535]
port = Math.floor(Math.random() * (65535 - 49152) + 49152);
subprocess = undefined;
}
subprocess.on("error", (error) => {
subprocessError = error;
});
subprocess.on("exit", (code) => {
if (code !== 0) {
// If the proxy failed to start, try a different port from the ephemeral range [49152, 65535]
port = Math.floor(Math.random() * (65535 - 49152) + 49152);
subprocess = undefined;
}
});
subprocess.stdin?.write(JSON.stringify(config));
subprocess.stdin?.end();
// Wait a little to allow the proxy to start
await util.delay(1000);
}
if (subprocessError) {
// eslint-disable-next-line @typescript-eslint/only-throw-error
throw subprocessError;
}
logger.info(`Proxy started on ${host}:${port}`);
core.setOutput("proxy_host", host);
core.setOutput("proxy_port", port.toString());
core.setOutput("proxy_ca_certificate", config.ca.cert);
const registry_urls = config.all_credentials
.filter((credential) => credential.url !== undefined)
.map((credential) => ({
type: credential.type,
url: credential.url,
}));
core.setOutput("proxy_urls", JSON.stringify(registry_urls));
} catch (error) {
core.setFailed(`start-proxy action failed: ${util.getErrorMessage(error)}`);
});
subprocess.stdin?.write(JSON.stringify(config));
subprocess.stdin?.end();
// Wait a little to allow the proxy to start
await util.delay(1000);
}
if (subprocessError) {
// eslint-disable-next-line @typescript-eslint/only-throw-error
throw subprocessError;
}
logger.info(`Proxy started on ${host}:${port}`);
core.setOutput("proxy_host", host);
core.setOutput("proxy_port", port.toString());
core.setOutput("proxy_ca_certificate", config.ca.cert);
const registry_urls = config.all_credentials
.filter((credential) => credential.url !== undefined)
.map((credential) => ({
type: credential.type,
url: credential.url,
}));
core.setOutput("proxy_urls", JSON.stringify(registry_urls));
}
async function getProxyBinaryPath(logger: Logger): Promise<string> {
+2 -1
View File
@@ -36,11 +36,12 @@ import {
} from "./util";
export enum ActionName {
Autobuild = "autobuild",
Analyze = "finish",
Autobuild = "autobuild",
Init = "init",
InitPost = "init-post",
ResolveEnvironment = "resolve-environment",
StartProxy = "start-proxy",
UploadSarif = "upload-sarif",
}