Visitar URL original
Use per-language CodeQL bundles by henrymercer · Pull Request #4146 · github/codeql-action · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ec5eecb
Use per-language CodeQL bundles
henrymercer Sep 15, 2026
eb76062
Include failed bundle attempts in fallback timing
henrymercer Sep 15, 2026
289376d
Use per-language CodeQL bundles
henrymercer Sep 15, 2026
59ce3a2
Include failed bundle attempts in fallback timing
henrymercer Sep 15, 2026
2d47caf
Isolate per-language bundle eligibility state
henrymercer Sep 16, 2026
dfb9bf5
Share CodeQL bundle platform definitions
henrymercer Sep 16, 2026
f536ef4
Centralize CodeQL download telemetry fields
henrymercer Sep 16, 2026
2f552a9
Clarify bundle resolution and latest-nightly selection
henrymercer Sep 16, 2026
bd2ddba
Extract explicit CodeQL bundle URL classification
henrymercer Sep 16, 2026
89606bb
Return toolcache rejection reasons with Result
henrymercer Sep 16, 2026
f4fa111
Share elapsed-time rounding for bundle downloads
henrymercer Sep 16, 2026
69f4715
Use Result.orElse for bundle extraction paths
henrymercer Sep 16, 2026
5a2f29e
Merge published per-language bundle history
henrymercer Sep 16, 2026
28b8f59
Use shared test state for bundle eligibility
henrymercer Sep 17, 2026
ed3a24c
Group bundle eligibility test state overrides
henrymercer Sep 17, 2026
549d498
Simplify per-language platform eligibility checks
henrymercer Sep 17, 2026
ead1f7d
Rename the platform module
henrymercer Sep 17, 2026
79fe3a1
Move download telemetry into the status-report directory
henrymercer Sep 17, 2026
ecec9b5
Share per-language telemetry fields without renaming
henrymercer Sep 17, 2026
f18f353
Describe the bundle URL resolver
henrymercer Sep 17, 2026
07fa87d
Clarify the latest-nightly eligibility exception
henrymercer Sep 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Extract explicit CodeQL bundle URL classification
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
  • Loading branch information
henrymercer and Copilot committed Sep 16, 2026
commit bd2ddba96c5dd69626880a515d338955f4a855c6
56 changes: 28 additions & 28 deletions lib/entry-points.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 56 additions & 0 deletions src/codeql-bundle.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import test from "ava";

import { getCodeQLBundleFromUrl } from "./codeql-bundle";
import { BuiltInLanguage } from "./languages";

for (const [assetName, language] of [
["codeql-bundle-java-linux64.tar.zst", BuiltInLanguage.java],
["codeql-bundle-swift-osx64.tar.zst", BuiltInLanguage.swift],
// Recognize unpublished language/platform combinations to keep them out of the toolcache.
["codeql-bundle-csharp-win64.tar.gz", BuiltInLanguage.csharp],
["codeql-bundle-java-kotlin-linux64.tar.zst", BuiltInLanguage.java],
["codeql-bundle-%70ython-linux64.tar.zst", BuiltInLanguage.python],
] as const) {
test(`getCodeQLBundleFromUrl identifies ${assetName} without adding a fallback`, (t) => {
const url = `https://github.com/github/codeql-action/releases/download/codeql-bundle-v1.2.3/${assetName}`;
t.deepEqual(getCodeQLBundleFromUrl(url), {
kind: "per-language",
url,
language,
});
});
}

test("getCodeQLBundleFromUrl preserves encoding, query parameters and fragments", (t) => {
const url =
"https://github.com/github/codeql-action/releases/download/codeql-bundle-v1.2.3/codeql-bundle-%70ython-linux64.tar.zst?download=1#asset";
t.deepEqual(getCodeQLBundleFromUrl(url), {
kind: "per-language",
url,
language: BuiltInLanguage.python,
});
});

test("getCodeQLBundleFromUrl treats unrecognized assets as combined bundles", (t) => {
for (const name of [
"codeql-bundle-linux64.tar.zst",
"codeql-bundle-osx64.tar.gz",
"codeql-bundle-win64.tar.zst",
// The all-platform bundle.
"codeql-bundle.tar.gz",
// A platform we do not publish per-language bundles for, whose name also contains a hyphen.
"codeql-bundle-linux-arm64.tar.zst",
// Not a language we know about.
"codeql-bundle-cobol-linux64.tar.zst",
// A name we cannot decode must not be mistaken for a language either.
"codeql-bundle-%zz-linux64.tar.zst",
]) {
const url = `https://github.com/github/codeql-action/releases/download/codeql-bundle-v1.2.3/${name}`;
t.deepEqual(getCodeQLBundleFromUrl(url), { kind: "combined", url });
}
});

test("getCodeQLBundleFromUrl preserves URLs it cannot parse", (t) => {
const url = "not a url";
t.deepEqual(getCodeQLBundleFromUrl(url), { kind: "combined", url });
});
33 changes: 33 additions & 0 deletions src/codeql-bundle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { BuiltInLanguage, parseBuiltInLanguage } from "./languages";

/** Describes the contents and location of a downloadable CodeQL bundle. */
export type CodeQLBundle =
| { kind: "combined"; url: string }
| {
kind: "per-language";
url: string;
language: BuiltInLanguage;
/** Only set when the Action selected the bundle, allowing a same-version fallback. */
combinedBundleURL?: string;
};

const PER_LANGUAGE_BUNDLE_NAME =
/^codeql-bundle-(.+)-(?:linux64|osx64|win64)\.tar\.(?:gz|zst)$/;

/** Classifies an explicit tools URL without changing it or adding a fallback. */
export function getCodeQLBundleFromUrl(url: string): CodeQLBundle {
let assetName: string;
try {
const pathname = new URL(url).pathname;
// URL-encoded names must not bypass the toolcache safeguard.
assetName = decodeURIComponent(pathname.split("/").pop() ?? "");
} catch {
return { kind: "combined", url };
}

const match = assetName.match(PER_LANGUAGE_BUNDLE_NAME);
const language = match ? parseBuiltInLanguage(match[1]) : undefined;
return language === undefined
? { kind: "combined", url }
: { kind: "per-language", url, language };
}
49 changes: 0 additions & 49 deletions src/per-language-bundles.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
getPerLanguageBundleLanguage,
MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION,
PerLanguageBundleOptions,
tryGetBundleLanguageFromUrl,
} from "./per-language-bundles";
import {
createFeatures,
Expand Down Expand Up @@ -172,51 +171,3 @@ test("getPerLanguageBundleLanguage skips only the release version check for the
undefined,
);
});

test("tryGetBundleLanguageFromUrl recognizes per-language bundle URLs", (t) => {
const url = (name: string) =>
`https://github.com/github/codeql-action/releases/download/codeql-bundle-v1.2.3/${name}`;

t.is(
tryGetBundleLanguageFromUrl(url("codeql-bundle-java-linux64.tar.zst")),
BuiltInLanguage.java,
);
t.is(
tryGetBundleLanguageFromUrl(url("codeql-bundle-swift-osx64.tar.zst")),
BuiltInLanguage.swift,
);
// We do not publish these, but should still recognize them if we ever do.
t.is(
tryGetBundleLanguageFromUrl(url("codeql-bundle-csharp-win64.tar.gz")),
BuiltInLanguage.csharp,
);
// A percent-encoded name resolves to the same asset, so it must not let a bundle that contains a
// single language pass for one that contains them all and end up in the toolcache.
t.is(
tryGetBundleLanguageFromUrl(url("codeql-bundle-%70ython-linux64.tar.zst")),
BuiltInLanguage.python,
);
});

test("tryGetBundleLanguageFromUrl rejects other bundle URLs", (t) => {
const url = (name: string) =>
`https://github.com/github/codeql-action/releases/download/codeql-bundle-v1.2.3/${name}`;

for (const name of [
"codeql-bundle-linux64.tar.zst",
"codeql-bundle-osx64.tar.gz",
"codeql-bundle-win64.tar.zst",
// The all-platform bundle.
"codeql-bundle.tar.gz",
// A platform we do not publish per-language bundles for, whose name also contains a hyphen.
"codeql-bundle-linux-arm64.tar.zst",
// Not a language we know about.
"codeql-bundle-cobol-linux64.tar.zst",
// A name we cannot decode must not be mistaken for a language either.
"codeql-bundle-%zz-linux64.tar.zst",
]) {
t.is(tryGetBundleLanguageFromUrl(url(name)), undefined, name);
}

t.is(tryGetBundleLanguageFromUrl("not a url"), undefined);
});
20 changes: 0 additions & 20 deletions src/per-language-bundles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,6 @@ import { GitHubVariant } from "./util";
/** Minimum CLI version for selecting a per-language release bundle. */
export const MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION = "2.27.1";

const PER_LANGUAGE_BUNDLE_NAME =
/^codeql-bundle-(.+)-(?:linux64|osx64|win64)\.tar\.(?:gz|zst)$/;

/** Returns the language in a per-language tools URL, or undefined for other URLs. */
export function tryGetBundleLanguageFromUrl(
url: string,
): BuiltInLanguage | undefined {
let assetName: string;
try {
const pathname = new URL(url).pathname;
// URL-encoded names must not bypass the toolcache safeguard.
assetName = decodeURIComponent(pathname.split("/").pop() ?? "");
} catch {
return undefined;
}

const match = assetName.match(PER_LANGUAGE_BUNDLE_NAME);
return match ? parseBuiltInLanguage(match[1]) : undefined;
}

/** Languages with per-language bundles published for each platform. */
const PER_LANGUAGE_BUNDLE_LANGUAGES: Readonly<
Record<BundlePlatform, ReadonlySet<BuiltInLanguage>>
Expand Down
26 changes: 3 additions & 23 deletions src/setup-codeql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
} from "./actions-util";
import * as api from "./api-client";
import { getBundlePlatform } from "./bundle-platform";
import { CodeQLBundle, getCodeQLBundleFromUrl } from "./codeql-bundle";
import * as defaults from "./defaults.json";
import {
addNoLanguageDiagnostic,
Expand All @@ -35,10 +36,7 @@ import {
import { BuiltInLanguage } from "./languages";
import { Logger } from "./logging";
import { getCodeQlVersionsForOverlayBaseDatabases } from "./overlay/caching";
import {
getPerLanguageBundleLanguage,
tryGetBundleLanguageFromUrl,
} from "./per-language-bundles";
import { getPerLanguageBundleLanguage } from "./per-language-bundles";
import * as tar from "./tar";
import {
deleteToolcacheBundles,
Expand Down Expand Up @@ -225,17 +223,6 @@ export function convertToSemVer(version: string, logger: Logger): string {
return s;
}

/** Describes the contents and location of a downloadable CodeQL bundle. */
type CodeQLBundle =
| { kind: "combined"; url: string }
| {
kind: "per-language";
url: string;
language: BuiltInLanguage;
/** Only set when the Action selected the bundle, allowing a same-version fallback. */
combinedBundleURL?: string;
};

/** A resolved download, including its bundle identity and version. */
export interface CodeQLDownloadSource {
/** Distinguishes downloads from local archives and cached installations. */
Expand Down Expand Up @@ -816,14 +803,7 @@ export async function getCodeQLSource(
}
compressionMethod = method;

if (bundle === undefined) {
// Explicit per-language URLs must also stay out of the toolcache, but have no fallback.
const language = tryGetBundleLanguageFromUrl(url);
bundle =
language === undefined
? { kind: "combined", url }
: { kind: "per-language", url, language };
}
bundle ??= getCodeQLBundleFromUrl(url);
if (bundle.kind === "per-language") {
logger.info(
`${url} appears to be a CodeQL bundle that contains only ${bundle.language}.`,
Expand Down