Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
80 changes: 80 additions & 0 deletions .github/workflows/validate-resolved-languages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: Validate Resolved Languages File
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GO111MODULE: auto
on:
push:
branches:
- main
- releases/v*
pull_request:
types:
- opened
- synchronize
- reopened
- ready_for_review
schedule:
- cron: '0 5 * * *'
workflow_dispatch:
inputs: {}
workflow_call:
inputs: {}
defaults:
run:
shell: bash
jobs:
validate-resolved-languages:
name: Validate Resolved Languages File
permissions:
contents: read
security-events: read
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v5
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
cache: npm
- name: Install dependencies
run: npm ci
- name: Prepare test
id: prepare-test
uses: ./.github/actions/prepare-test
with:
version: linked
use-all-platform-bundle: 'false'
setup-kotlin: 'false'
- name: Initialize CodeQL
id: init
uses: ./../action/init
with:
tools: ${{ steps.prepare-test.outputs.tools-url }}
- name: Verify that the resolved languages file is up to date
env:
CODEQL: ${{ steps.init.outputs.codeql-path }}
RESOLVED_LANGUAGES_FILE: ../action/src/resolved-languages.json
run: |
if [ ! -f "${RESOLVED_LANGUAGES_FILE}" ]; then
echo "::error file=src/resolved-languages.json::The resolved-languages.json file is missing."
exit 1
fi
#
# Please update getStoredSupportedLanguageMap() when you update the
# following call to use --filter-to-languages-with-queries.
#
"${CODEQL}" resolve languages --format=betterjson --extractor-include-aliases > resolved-languages.json
if ! diff -u resolved-languages.json "${RESOLVED_LANGUAGES_FILE}"; then
echo "::error file=src/resolved-languages.json::The saved resolved-languages.json file is out of date."
exit 1
fi
- name: Upload resolved languages file for debugging
if: ${{ failure() }}
uses: actions/upload-artifact@v4
with:
name: resolved-languages
path: resolved-languages.json
env:
CODEQL_ACTION_TEST_MODE: true
13 changes: 9 additions & 4 deletions lib/init-action.js

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

10 changes: 4 additions & 6 deletions src/codeql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,12 +237,10 @@ export interface BetterResolveLanguagesOutput {
[alias: string]: string;
};
extractors: {
[language: string]: [
{
extractor_root: string;
extractor_options?: any;
},
];
[language: string]: Array<{
extractor_root: string;
extractor_options?: any;
}>;
};
}

Expand Down
11 changes: 7 additions & 4 deletions src/config-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1163,14 +1163,18 @@ const mockRepositoryNwo = parseRepositoryNwo("owner/repo");
}),
});

const languageMap = await configUtils.getSupportedLanguageMap(
codeQL,
features,
mockLogger,
);
if (args.expectedLanguages) {
// happy path
const actualLanguages = await configUtils.getLanguages(
codeQL,
languageMap,
args.languagesInput,
mockRepositoryNwo,
".",
features,
mockLogger,
);

Expand All @@ -1180,11 +1184,10 @@ const mockRepositoryNwo = parseRepositoryNwo("owner/repo");
await t.throwsAsync(
async () =>
await configUtils.getLanguages(
codeQL,
languageMap,
args.languagesInput,
mockRepositoryNwo,
".",
features,
mockLogger,
),
{ message: args.expectedError },
Expand Down
36 changes: 29 additions & 7 deletions src/config-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
} from "./analyses";
import * as api from "./api-client";
import { CachingKind, getCachingKind } from "./caching-utils";
import { type CodeQL } from "./codeql";
import { type BetterResolveLanguagesOutput, type CodeQL } from "./codeql";
import { shouldPerformDiffInformedAnalysis } from "./diff-informed-analysis-utils";
import { Feature, FeatureEnablement } from "./feature-flags";
import { getGitRoot, isAnalyzingDefaultBranch } from "./git-utils";
Expand All @@ -27,6 +27,7 @@ import {
OverlayDatabaseMode,
} from "./overlay-database-utils";
import { RepositoryNwo } from "./repository";
import * as resolvedLanguages from "./resolved-languages.json";
Copy link
Preview

Copilot AI Sep 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import statement assumes the JSON file will always be valid. Consider adding runtime validation to ensure the imported data conforms to the expected BetterResolveLanguagesOutput structure, or handling potential parsing errors gracefully.

Copilot uses AI. Check for mistakes.

import { downloadTrapCaches } from "./trap-caching";
import {
GitHubVersion,
Expand Down Expand Up @@ -331,6 +332,30 @@ export async function getSupportedLanguageMap(
`The CodeQL CLI supports the following languages: ${Object.keys(resolveResult.extractors).join(", ")}`,
);
}
return buildSupportedLanguageMap(
resolveResult,
resolveSupportedLanguagesUsingCli,
);
}

// This function serves the same purpose as getSupportedLanguageMap(), but it
// uses the stored resolved-languages.json file instead of calling out to the
// CodeQL CLI.
export function getStoredSupportedLanguageMap(): Record<string, string> {
return buildSupportedLanguageMap(
resolvedLanguages,
// resolveSupportedLanguagesUsingCli is false because we currently generate
// resolved-languages.json without the --filter-to-languages-with-queries
// flag (see .github/workflows/validate-resolved-languages.yml). Once that
// changes, we should set this to true.
false,
);
}

function buildSupportedLanguageMap(
resolveResult: BetterResolveLanguagesOutput,
resolveSupportedLanguagesUsingCli: boolean,
): Record<string, string> {
const supportedLanguages: Record<string, string> = {};
// Populate canonical language names
for (const extractor of Object.keys(resolveResult.extractors)) {
Expand Down Expand Up @@ -418,11 +443,10 @@ export async function getRawLanguagesInRepo(
* then throw an error.
*/
export async function getLanguages(
codeql: CodeQL,
languageMap: Record<string, string>,
languagesInput: string | undefined,
repository: RepositoryNwo,
sourceRoot: string,
features: FeatureEnablement,
logger: Logger,
): Promise<Language[]> {
// Obtain languages without filtering them.
Expand All @@ -432,8 +456,6 @@ export async function getLanguages(
sourceRoot,
logger,
);

const languageMap = await getSupportedLanguageMap(codeql, features, logger);
const languagesSet = new Set<Language>();
const unknownLanguages: string[] = [];

Expand Down Expand Up @@ -575,12 +597,12 @@ export async function initActionState(
analysisKinds.push(AnalysisKind.CodeQuality);
}

const languageMap = await getSupportedLanguageMap(codeql, features, logger);
const languages = await getLanguages(
codeql,
languageMap,
languagesInput,
repository,
sourceRoot,
features,
logger,
);

Expand Down
Loading
Loading