Merge pull request #3110 from github/mbg/proxy/fetch-from-release

Fetch proxy binaries from `defaults.json` release
This commit is contained in:
Michael B. Gale
2025-09-12 12:38:15 +01:00
committed by GitHub
4 changed files with 1887 additions and 80 deletions

View File

@@ -7,13 +7,14 @@ import { pki } from "node-forge";
import * as actionsUtil from "./actions-util";
import { getActionsLogger, Logger } from "./logging";
import { Credential, getCredentials } from "./start-proxy";
import {
Credential,
getCredentials,
getDownloadUrl,
UPDATEJOB_PROXY,
} from "./start-proxy";
import * as util from "./util";
const UPDATEJOB_PROXY = "update-job-proxy";
const UPDATEJOB_PROXY_VERSION = "v2.0.20250624110901";
const UPDATEJOB_PROXY_URL_PREFIX =
"https://github.com/github/codeql-action/releases/download/codeql-bundle-v2.22.0/";
const KEY_SIZE = 2048;
const KEY_EXPIRY_YEARS = 2;
@@ -119,7 +120,7 @@ async function runWrapper() {
};
// Start the Proxy
const proxyBin = await getProxyBinaryPath();
const proxyBin = await getProxyBinaryPath(logger);
await startProxy(proxyBin, proxyConfig, proxyLogFilePath, logger);
}
@@ -184,26 +185,19 @@ async function startProxy(
}
}
async function getProxyBinaryPath(): Promise<string> {
async function getProxyBinaryPath(logger: Logger): Promise<string> {
const proxyFileName =
process.platform === "win32" ? `${UPDATEJOB_PROXY}.exe` : UPDATEJOB_PROXY;
const platform =
process.platform === "win32"
? "win64"
: process.platform === "darwin"
? "osx64"
: "linux64";
const proxyPackage = `${UPDATEJOB_PROXY}-${platform}.tar.gz`;
const proxyURL = `${UPDATEJOB_PROXY_URL_PREFIX}${proxyPackage}`;
const proxyInfo = await getDownloadUrl(logger);
let proxyBin = toolcache.find(proxyFileName, UPDATEJOB_PROXY_VERSION);
let proxyBin = toolcache.find(proxyFileName, proxyInfo.version);
if (!proxyBin) {
const temp = await toolcache.downloadTool(proxyURL);
const temp = await toolcache.downloadTool(proxyInfo.url);
const extracted = await toolcache.extractTar(temp);
proxyBin = await toolcache.cacheDir(
extracted,
proxyFileName,
UPDATEJOB_PROXY_VERSION,
proxyInfo.version,
);
}
proxyBin = path.join(proxyBin, proxyFileName);

View File

@@ -1,5 +1,8 @@
import test from "ava";
import sinon from "sinon";
import * as apiClient from "./api-client";
import * as defaults from "./defaults.json";
import { KnownLanguage } from "./languages";
import { getRunnerLogger } from "./logging";
import * as startProxyExports from "./start-proxy";
@@ -197,3 +200,68 @@ test("parseLanguage", async (t) => {
t.deepEqual(parseLanguage(" "), undefined);
t.deepEqual(parseLanguage(""), undefined);
});
function mockGetReleaseByTag(assets?: Array<{ name: string; url?: string }>) {
const mockClient = sinon.stub(apiClient, "getApiClient");
const getReleaseByTag =
assets === undefined
? sinon.stub().rejects()
: sinon.stub().resolves({
status: 200,
data: { assets },
headers: {},
url: "GET /repos/:owner/:repo/releases/tags/:tag",
});
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
mockClient.returns({
rest: {
repos: {
getReleaseByTag,
},
},
} as any);
return mockClient;
}
test("getDownloadUrl returns fallback when `getLinkedRelease` rejects", async (t) => {
mockGetReleaseByTag();
const info = await startProxyExports.getDownloadUrl(getRunnerLogger(true));
t.is(info.version, startProxyExports.UPDATEJOB_PROXY_VERSION);
t.is(
info.url,
startProxyExports.getFallbackUrl(startProxyExports.getProxyPackage()),
);
});
test("getDownloadUrl returns fallback when there's no matching release asset", async (t) => {
const testAssets = [[], [{ name: "foo" }]];
for (const assets of testAssets) {
const stub = mockGetReleaseByTag(assets);
const info = await startProxyExports.getDownloadUrl(getRunnerLogger(true));
t.is(info.version, startProxyExports.UPDATEJOB_PROXY_VERSION);
t.is(
info.url,
startProxyExports.getFallbackUrl(startProxyExports.getProxyPackage()),
);
stub.restore();
}
});
test("getDownloadUrl returns matching release asset", async (t) => {
const assets = [
{ name: "foo", url: "other-url" },
{ name: startProxyExports.getProxyPackage(), url: "url-we-want" },
];
mockGetReleaseByTag(assets);
const info = await startProxyExports.getDownloadUrl(getRunnerLogger(true));
t.is(info.version, defaults.cliVersion);
t.is(info.url, "url-we-want");
});

View File

@@ -1,8 +1,15 @@
import * as core from "@actions/core";
import { getApiClient } from "./api-client";
import * as defaults from "./defaults.json";
import { KnownLanguage } from "./languages";
import { Logger } from "./logging";
import { ConfigurationError, isDefined } from "./util";
import { ConfigurationError, getErrorMessage, isDefined } from "./util";
export const UPDATEJOB_PROXY = "update-job-proxy";
export const UPDATEJOB_PROXY_VERSION = "v2.0.20250624110901";
export const UPDATEJOB_PROXY_URL_PREFIX =
"https://github.com/github/codeql-action/releases/download/codeql-bundle-v2.22.0/";
export type Credential = {
type: string;
@@ -166,3 +173,87 @@ export function getCredentials(
}
return out;
}
/**
* Gets the name of the proxy release asset for the current platform.
*/
export function getProxyPackage(): string {
const platform =
process.platform === "win32"
? "win64"
: process.platform === "darwin"
? "osx64"
: "linux64";
return `${UPDATEJOB_PROXY}-${platform}.tar.gz`;
}
/**
* Gets the fallback URL for downloading the proxy release asset.
*
* @param proxyPackage The asset name.
* @returns The full URL to download the specified asset from the fallback release.
*/
export function getFallbackUrl(proxyPackage: string): string {
return `${UPDATEJOB_PROXY_URL_PREFIX}${proxyPackage}`;
}
/**
* Uses the GitHub API to obtain information about the CodeQL CLI bundle release
* that is pointed at by `defaults.json`.
*
* @returns The response from the GitHub API.
*/
export async function getLinkedRelease() {
return getApiClient().rest.repos.getReleaseByTag({
owner: "github",
repo: "codeql-action",
tag: defaults.bundleVersion,
});
}
/**
* Determines the URL of the proxy release asset that we should download if its not
* already in the toolcache, and its version.
*
* @param logger The logger to use.
* @returns Returns the download URL and version of the proxy package we plan to use.
*/
export async function getDownloadUrl(
logger: Logger,
): Promise<{ url: string; version: string }> {
const proxyPackage = getProxyPackage();
try {
// Try to retrieve information about the CLI bundle release pointed at by `defaults.json`.
const cliRelease = await getLinkedRelease();
// Search the release's assets to find the one we are looking for.
for (const asset of cliRelease.data.assets) {
if (asset.name === proxyPackage) {
logger.info(
`Found '${proxyPackage}' in release '${defaults.bundleVersion}' at '${asset.url}'`,
);
return {
url: asset.url,
// The `update-job-proxy` doesn't have a version as such. Since we now bundle it
// with CodeQL CLI bundle releases, we use the corresponding CLI version to
// differentiate between (potentially) different versions of `update-job-proxy`.
version: defaults.cliVersion,
};
}
}
} catch (ex) {
logger.warning(
`Failed to retrieve information about the linked release: ${getErrorMessage(ex)}`,
);
}
// Fallback to the hard-coded URL.
logger.info(
`Did not find '${proxyPackage}' in the linked release, falling back to hard-coded version.`,
);
return {
url: getFallbackUrl(proxyPackage),
version: UPDATEJOB_PROXY_VERSION,
};
}