Store all built-in languages

While we want the CodeQL Action to work with third-party language support, having a list of all built-in languages can help us create better type-level checks to ensure that we don't miss things that we want to customize for each of our built-in languages.
This commit is contained in:
Henry Mercer
2026-04-10 17:33:06 +01:00
parent a26cb68cc7
commit bad0a744dd
24 changed files with 1494 additions and 1066 deletions

11
.github/workflows/script/tsconfig.json vendored Normal file
View File

@@ -0,0 +1,11 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"lib": ["esnext"],
"rootDir": "../../..",
"sourceMap": false,
"noEmit": true,
},
"include": ["./*.ts"],
"exclude": ["node_modules"]
}

View File

@@ -0,0 +1,88 @@
#!/usr/bin/env npx tsx
/**
* Updates src/languages/builtin.json by querying the CodeQL CLI for:
* - Languages that have default queries (via codeql-extractor.yml)
* - Language aliases (via `codeql resolve languages --format=betterjson --extractor-include-aliases`)
*
* Usage:
* npx tsx .github/workflows/script/update-builtin-languages.ts [path-to-codeql]
*
* If no path is given, falls back to "codeql".
*/
import { execFileSync } from "node:child_process";
import * as fs from "node:fs";
import * as path from "node:path";
import * as yaml from "yaml";
const codeqlPath = process.argv[2] || "codeql";
// Step 1: Resolve all language extractor directories.
const resolveJson: Record<string, string[]> = JSON.parse(
execFileSync(
codeqlPath,
["resolve", "languages", "--format=json"],
{ encoding: "utf8" },
),
);
// Step 2: For each language, read codeql-extractor.yml and check default_queries.
const languages: string[] = [];
for (const [language, dirs] of Object.entries(resolveJson)) {
const extractorDir = dirs[0];
const extractorYmlPath = path.join(extractorDir, "codeql-extractor.yml");
if (!fs.existsSync(extractorYmlPath)) {
throw new Error(`Extractor YAML not found for language '${language}' at expected path: ${extractorYmlPath}`);
}
const extractorYml = yaml.parse(fs.readFileSync(extractorYmlPath, "utf8"));
const defaultQueries: unknown[] | undefined = extractorYml.default_queries;
if (Array.isArray(defaultQueries) && defaultQueries.length > 0) {
console.log(` ✅ ${language}: included (default_queries: ${JSON.stringify(defaultQueries)})`);
languages.push(language);
} else {
console.log(` ❌ ${language}: excluded (no default queries)`);
}
}
languages.sort();
// Step 3: Resolve aliases, filtered to only those targeting included languages.
const betterjsonOutput = JSON.parse(
execFileSync(
codeqlPath,
["resolve", "languages", "--format=betterjson", "--extractor-include-aliases"],
{ encoding: "utf8" },
),
);
const languageSet = new Set(languages);
const aliases: Record<string, string> = Object.fromEntries(
Object.entries((betterjsonOutput.aliases ?? {}) as Record<string, string>)
.filter(([, target]) => languageSet.has(target))
.sort(([a], [b]) => a.localeCompare(b)),
);
// Step 4: Write builtin.json.
const outputPath = path.join(
__dirname,
"..",
"..",
"..",
"src",
"languages",
"builtin.json",
);
const content = JSON.stringify({ languages, aliases }, null, 2) + "\n";
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
fs.writeFileSync(outputPath, content);
console.log(`\nWrote ${outputPath}`);
console.log(` Languages: ${languages.join(", ")}`);
console.log(` Aliases: ${Object.keys(aliases).join(", ")}`);

View File

@@ -63,13 +63,10 @@ jobs:
with:
tools: https://github.com/github/codeql-action/releases/download/${{ github.event.release.tag_name }}/codeql-bundle-linux64.tar.gz
- name: Update language aliases
- name: Update built-in languages
run: npx tsx .github/workflows/script/update-builtin-languages.ts "$CODEQL_PATH"
env:
CODEQL_PATH: ${{ steps.setup-codeql.outputs.codeql-path }}
run: |
"$CODEQL_PATH" resolve languages --format=betterjson --extractor-include-aliases \
| jq -S '.aliases // {}' \
> src/known-language-aliases.json
- name: Bump Action minor version if new CodeQL minor version series
id: bump-action-version

View File

@@ -162606,6 +162606,36 @@ var featureConfig = {
}
};
// src/languages/builtin.json
var builtin_default = {
languages: [
"actions",
"cpp",
"csharp",
"go",
"java",
"javascript",
"python",
"ruby",
"rust",
"swift"
],
aliases: {
c: "cpp",
"c-c++": "cpp",
"c-cpp": "cpp",
"c#": "csharp",
"c++": "cpp",
"java-kotlin": "java",
"javascript-typescript": "javascript",
kotlin: "java",
typescript: "javascript"
}
};
// src/languages/index.ts
var knownLanguageSet = new Set(builtin_default.languages);
// src/overlay/status.ts
var actionsCache2 = __toESM(require_cache5());

30
lib/analyze-action.js generated
View File

@@ -108680,6 +108680,36 @@ ${jsonContents}`
}
}
// src/languages/builtin.json
var builtin_default = {
languages: [
"actions",
"cpp",
"csharp",
"go",
"java",
"javascript",
"python",
"ruby",
"rust",
"swift"
],
aliases: {
c: "cpp",
"c-c++": "cpp",
"c-cpp": "cpp",
"c#": "csharp",
"c++": "cpp",
"java-kotlin": "java",
"javascript-typescript": "javascript",
kotlin: "java",
typescript: "javascript"
}
};
// src/languages/index.ts
var knownLanguageSet = new Set(builtin_default.languages);
// src/overlay/status.ts
var actionsCache2 = __toESM(require_cache5());

View File

@@ -104981,6 +104981,36 @@ function initFeatures(gitHubVersion, repositoryNwo, tempDir, logger) {
}
}
// src/languages/builtin.json
var builtin_default = {
languages: [
"actions",
"cpp",
"csharp",
"go",
"java",
"javascript",
"python",
"ruby",
"rust",
"swift"
],
aliases: {
c: "cpp",
"c-c++": "cpp",
"c-cpp": "cpp",
"c#": "csharp",
"c++": "cpp",
"java-kotlin": "java",
"javascript-typescript": "javascript",
kotlin: "java",
typescript: "javascript"
}
};
// src/languages/index.ts
var knownLanguageSet = new Set(builtin_default.languages);
// src/overlay/status.ts
var actionsCache2 = __toESM(require_cache5());

View File

@@ -166466,6 +166466,36 @@ ${jsonContents}`
}
}
// src/languages/builtin.json
var builtin_default = {
languages: [
"actions",
"cpp",
"csharp",
"go",
"java",
"javascript",
"python",
"ruby",
"rust",
"swift"
],
aliases: {
c: "cpp",
"c-c++": "cpp",
"c-cpp": "cpp",
"c#": "csharp",
"c++": "cpp",
"java-kotlin": "java",
"javascript-typescript": "javascript",
kotlin: "java",
typescript: "javascript"
}
};
// src/languages/index.ts
var knownLanguageSet = new Set(builtin_default.languages);
// src/overlay/status.ts
var fs7 = __toESM(require("fs"));
var path7 = __toESM(require("path"));

30
lib/init-action.js generated
View File

@@ -106373,7 +106373,34 @@ function getDiffRanges(fileDiff, logger) {
return diffRanges;
}
// src/languages.ts
// src/languages/builtin.json
var builtin_default = {
languages: [
"actions",
"cpp",
"csharp",
"go",
"java",
"javascript",
"python",
"ruby",
"rust",
"swift"
],
aliases: {
c: "cpp",
"c-c++": "cpp",
"c-cpp": "cpp",
"c#": "csharp",
"c++": "cpp",
"java-kotlin": "java",
"javascript-typescript": "javascript",
kotlin: "java",
typescript: "javascript"
}
};
// src/languages/index.ts
var KnownLanguage = /* @__PURE__ */ ((KnownLanguage2) => {
KnownLanguage2["actions"] = "actions";
KnownLanguage2["cpp"] = "cpp";
@@ -106387,6 +106414,7 @@ var KnownLanguage = /* @__PURE__ */ ((KnownLanguage2) => {
KnownLanguage2["swift"] = "swift";
return KnownLanguage2;
})(KnownLanguage || {});
var knownLanguageSet = new Set(builtin_default.languages);
// src/overlay/diagnostics.ts
async function addOverlayDisablementDiagnostics(config, codeql, overlayDisabledReason) {

View File

@@ -104649,6 +104649,36 @@ var featureConfig = {
}
};
// src/languages/builtin.json
var builtin_default = {
languages: [
"actions",
"cpp",
"csharp",
"go",
"java",
"javascript",
"python",
"ruby",
"rust",
"swift"
],
aliases: {
c: "cpp",
"c-c++": "cpp",
"c-cpp": "cpp",
"c#": "csharp",
"c++": "cpp",
"java-kotlin": "java",
"javascript-typescript": "javascript",
kotlin: "java",
typescript: "javascript"
}
};
// src/languages/index.ts
var knownLanguageSet = new Set(builtin_default.languages);
// src/overlay/status.ts
var actionsCache2 = __toESM(require_cache5());

View File

@@ -105214,6 +105214,36 @@ function writeDiagnostic(config, language, diagnostic) {
}
}
// src/languages/builtin.json
var builtin_default = {
languages: [
"actions",
"cpp",
"csharp",
"go",
"java",
"javascript",
"python",
"ruby",
"rust",
"swift"
],
aliases: {
c: "cpp",
"c-c++": "cpp",
"c-cpp": "cpp",
"c#": "csharp",
"c++": "cpp",
"java-kotlin": "java",
"javascript-typescript": "javascript",
kotlin: "java",
typescript: "javascript"
}
};
// src/languages/index.ts
var knownLanguageSet = new Set(builtin_default.languages);
// src/overlay/status.ts
var actionsCache2 = __toESM(require_cache5());

View File

@@ -161933,6 +161933,36 @@ var featureConfig = {
}
};
// src/languages/builtin.json
var builtin_default = {
languages: [
"actions",
"cpp",
"csharp",
"go",
"java",
"javascript",
"python",
"ruby",
"rust",
"swift"
],
aliases: {
c: "cpp",
"c-c++": "cpp",
"c-cpp": "cpp",
"c#": "csharp",
"c++": "cpp",
"java-kotlin": "java",
"javascript-typescript": "javascript",
kotlin: "java",
typescript: "javascript"
}
};
// src/languages/index.ts
var knownLanguageSet = new Set(builtin_default.languages);
// src/overlay/status.ts
var actionsCache2 = __toESM(require_cache5());

1946
lib/start-proxy-action.js generated

File diff suppressed because it is too large Load Diff

30
lib/upload-lib.js generated
View File

@@ -107828,6 +107828,36 @@ ${jsonContents}`
}
}
// src/languages/builtin.json
var builtin_default = {
languages: [
"actions",
"cpp",
"csharp",
"go",
"java",
"javascript",
"python",
"ruby",
"rust",
"swift"
],
aliases: {
c: "cpp",
"c-c++": "cpp",
"c-cpp": "cpp",
"c#": "csharp",
"c++": "cpp",
"java-kotlin": "java",
"javascript-typescript": "javascript",
kotlin: "java",
typescript: "javascript"
}
};
// src/languages/index.ts
var knownLanguageSet = new Set(builtin_default.languages);
// src/overlay/status.ts
var actionsCache2 = __toESM(require_cache5());

View File

@@ -162103,6 +162103,36 @@ var featureConfig = {
}
};
// src/languages/builtin.json
var builtin_default = {
languages: [
"actions",
"cpp",
"csharp",
"go",
"java",
"javascript",
"python",
"ruby",
"rust",
"swift"
],
aliases: {
c: "cpp",
"c-c++": "cpp",
"c-cpp": "cpp",
"c#": "csharp",
"c++": "cpp",
"java-kotlin": "java",
"javascript-typescript": "javascript",
kotlin: "java",
typescript: "javascript"
}
};
// src/languages/index.ts
var knownLanguageSet = new Set(builtin_default.languages);
// src/overlay/status.ts
var actionsCache2 = __toESM(require_cache6());

View File

@@ -108020,6 +108020,36 @@ ${jsonContents}`
}
}
// src/languages/builtin.json
var builtin_default = {
languages: [
"actions",
"cpp",
"csharp",
"go",
"java",
"javascript",
"python",
"ruby",
"rust",
"swift"
],
aliases: {
c: "cpp",
"c-c++": "cpp",
"c-cpp": "cpp",
"c#": "csharp",
"c++": "cpp",
"java-kotlin": "java",
"javascript-typescript": "javascript",
kotlin: "java",
typescript: "javascript"
}
};
// src/languages/index.ts
var knownLanguageSet = new Set(builtin_default.languages);
// src/overlay/status.ts
var actionsCache2 = __toESM(require_cache5());

View File

@@ -12,7 +12,7 @@
"ava": "npm run transpile && ava --verbose",
"test": "npm run ava -- src/",
"test-debug": "npm run test -- --timeout=20m",
"transpile": "tsc --build --verbose"
"transpile": "tsc --build --verbose tsconfig.json .github/workflows/script/tsconfig.json"
},
"license": "MIT",
"workspaces": [

View File

@@ -1090,7 +1090,7 @@ const checkOverlayEnablementMacro = test.macro({
sinon
.stub(codeql, "isTracedLanguage")
.callsFake(async (lang: Language) => {
return [KnownLanguage.java].includes(lang as KnownLanguage);
return lang === KnownLanguage.java;
});
// Mock git root detection

View File

@@ -1,11 +0,0 @@
{
"c": "cpp",
"c-c++": "cpp",
"c-cpp": "cpp",
"c#": "csharp",
"c++": "cpp",
"java-kotlin": "java",
"javascript-typescript": "javascript",
"kotlin": "java",
"typescript": "javascript"
}

View File

@@ -0,0 +1,25 @@
{
"languages": [
"actions",
"cpp",
"csharp",
"go",
"java",
"javascript",
"python",
"ruby",
"rust",
"swift"
],
"aliases": {
"c": "cpp",
"c-c++": "cpp",
"c-cpp": "cpp",
"c#": "csharp",
"c++": "cpp",
"java-kotlin": "java",
"javascript-typescript": "javascript",
"kotlin": "java",
"typescript": "javascript"
}
}

View File

@@ -0,0 +1,46 @@
import test from "ava";
import { setupTests } from "../testing-utils";
import knownLanguagesData from "./builtin.json";
import { isKnownLanguage, KnownLanguage, parseBuiltInLanguage } from ".";
setupTests(test);
test("parseBuiltInLanguage", (t) => {
// Exact matches
t.is(parseBuiltInLanguage("csharp"), KnownLanguage.csharp);
t.is(parseBuiltInLanguage("cpp"), KnownLanguage.cpp);
t.is(parseBuiltInLanguage("go"), KnownLanguage.go);
t.is(parseBuiltInLanguage("java"), KnownLanguage.java);
t.is(parseBuiltInLanguage("javascript"), KnownLanguage.javascript);
t.is(parseBuiltInLanguage("python"), KnownLanguage.python);
t.is(parseBuiltInLanguage("rust"), KnownLanguage.rust);
// Aliases
t.is(parseBuiltInLanguage(" \t\nCsHaRp\t\t"), KnownLanguage.csharp);
t.is(parseBuiltInLanguage("c"), KnownLanguage.cpp);
t.is(parseBuiltInLanguage("c++"), KnownLanguage.cpp);
t.is(parseBuiltInLanguage("kotlin"), KnownLanguage.java);
t.is(parseBuiltInLanguage("typescript"), KnownLanguage.javascript);
// spaces and case-insensitivity
t.is(parseBuiltInLanguage(" \t\nkOtLin\t\t"), KnownLanguage.java);
// Not matches
t.is(parseBuiltInLanguage(KnownLanguage.python), KnownLanguage.python);
t.is(parseBuiltInLanguage("foo"), undefined);
t.is(parseBuiltInLanguage(" "), undefined);
t.is(parseBuiltInLanguage(""), undefined);
});
test("isKnownLanguage matches the curated known-language set", (t) => {
t.true(isKnownLanguage(KnownLanguage.actions));
t.true(isKnownLanguage(KnownLanguage.swift));
t.false(isKnownLanguage("typescript"));
});
test("KnownLanguage enum matches builtin.json", (t) => {
t.deepEqual(Object.values(KnownLanguage), knownLanguagesData.languages);
});

View File

@@ -1,3 +1,5 @@
import knownLanguagesData from "./builtin.json";
/** A language to analyze with CodeQL. */
export type Language = string;
@@ -27,3 +29,26 @@ export enum JavaEnvVars {
JDK_JAVA_OPTIONS = "JDK_JAVA_OPTIONS",
_JAVA_OPTIONS = "_JAVA_OPTIONS",
}
const knownLanguageSet = new Set<string>(knownLanguagesData.languages);
export function isKnownLanguage(language: string): language is KnownLanguage {
return knownLanguageSet.has(language);
}
/**
* Parses a built-in language name or alias.
*/
export function parseBuiltInLanguage(
language: string,
): KnownLanguage | undefined {
language = language.trim().toLowerCase();
language =
knownLanguagesData.aliases[
language as keyof typeof knownLanguagesData.aliases
] ?? language;
if (isKnownLanguage(language)) {
return language;
}
return undefined;
}

View File

@@ -6,7 +6,7 @@ import * as core from "@actions/core";
import * as actionsUtil from "./actions-util";
import { getGitHubVersion } from "./api-client";
import { Feature, FeatureEnablement, initFeatures } from "./feature-flags";
import { KnownLanguage } from "./languages";
import { KnownLanguage, parseBuiltInLanguage } from "./languages";
import { getActionsLogger, Logger } from "./logging";
import { getRepositoryNwo } from "./repository";
import {
@@ -14,7 +14,6 @@ import {
getCredentials,
getProxyBinaryPath,
getSafeErrorMessage,
parseLanguage,
ProxyInfo,
sendFailedStatusReport,
sendSuccessStatusReport,
@@ -56,7 +55,7 @@ async function run(startedAt: Date) {
// Get the language input.
const languageInput = actionsUtil.getOptionalInput("language");
language = languageInput ? parseLanguage(languageInput) : undefined;
language = languageInput ? parseBuiltInLanguage(languageInput) : undefined;
// Query the FF for whether we should use the reduced registry mapping.
const skipUnusedRegistries = await features.getValue(
@@ -119,7 +118,7 @@ async function run(startedAt: Date) {
await sendSuccessStatusReport(
startedAt,
{
languages: language && [language],
languages: language === undefined ? undefined : [language],
},
proxyConfig.all_credentials.map((c) => c.type),
logger,

View File

@@ -11,7 +11,6 @@ import { setUpFeatureFlagTests } from "./feature-flags/testing-util";
import { KnownLanguage } from "./languages";
import { getRunnerLogger, Logger } from "./logging";
import * as startProxyExports from "./start-proxy";
import { parseLanguage } from "./start-proxy";
import * as statusReport from "./status-report";
import {
assertNotLogged,
@@ -666,33 +665,6 @@ test("getCredentials returns no credentials for Actions when using NEW_LANGUAGE_
t.deepEqual(credentials, []);
});
test("parseLanguage", async (t) => {
// Exact matches
t.deepEqual(parseLanguage("csharp"), KnownLanguage.csharp);
t.deepEqual(parseLanguage("cpp"), KnownLanguage.cpp);
t.deepEqual(parseLanguage("go"), KnownLanguage.go);
t.deepEqual(parseLanguage("java"), KnownLanguage.java);
t.deepEqual(parseLanguage("javascript"), KnownLanguage.javascript);
t.deepEqual(parseLanguage("python"), KnownLanguage.python);
t.deepEqual(parseLanguage("rust"), KnownLanguage.rust);
// Aliases
t.deepEqual(parseLanguage("c"), KnownLanguage.cpp);
t.deepEqual(parseLanguage("c++"), KnownLanguage.cpp);
t.deepEqual(parseLanguage("c#"), KnownLanguage.csharp);
t.deepEqual(parseLanguage("kotlin"), KnownLanguage.java);
t.deepEqual(parseLanguage("typescript"), KnownLanguage.javascript);
// spaces and case-insensitivity
t.deepEqual(parseLanguage(" \t\nCsHaRp\t\t"), KnownLanguage.csharp);
t.deepEqual(parseLanguage(" \t\nkOtLin\t\t"), KnownLanguage.java);
// Not matches
t.deepEqual(parseLanguage("foo"), undefined);
t.deepEqual(parseLanguage(" "), undefined);
t.deepEqual(parseLanguage(""), undefined);
});
function mockGetApiClient(endpoints: any) {
return (
sinon

View File

@@ -18,7 +18,6 @@ import {
FeatureEnablement,
} from "./feature-flags";
import * as json from "./json";
import * as knownLanguageAliases from "./known-language-aliases.json";
import { KnownLanguage } from "./languages";
import { Logger } from "./logging";
import {
@@ -173,7 +172,7 @@ export async function sendFailedStatusReport(
getActionsStatus(error),
startedAt,
{
languages: language && [language],
languages: language === undefined ? undefined : [language],
},
await util.checkDiskUsage(logger),
logger,
@@ -189,35 +188,6 @@ export 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/";
/**
* Parse the start-proxy language input into its canonical CodeQL language name.
*
* This uses the language aliases shipped with the Action and will not be able to resolve aliases
* added by versions of the CodeQL CLI newer than the one mentioned in `defaults.json`. However,
* this is sufficient for the start-proxy Action since we are already specifying proxy
* configurations on a per-language basis.
*/
export function parseLanguage(language: string): KnownLanguage | undefined {
// Normalize to lower case
language = language.trim().toLowerCase();
// See if it's an exact match
if (Object.hasOwn(KnownLanguage, language)) {
return language as KnownLanguage;
}
// Check language aliases
if (Object.hasOwn(knownLanguageAliases, language)) {
language =
knownLanguageAliases[language as keyof typeof knownLanguageAliases];
if (Object.hasOwn(KnownLanguage, language)) {
return language as KnownLanguage;
}
}
return undefined;
}
function isPAT(value: string) {
return artifactScanner.isAuthToken(value, [
artifactScanner.GITHUB_PAT_CLASSIC_PATTERN,