Add API call for languages if java in input

If a user explicitly includes java in their language inputs, always
make an api call to check for kotlin in the repo.

Also, add some suggestions from code reviews.
This commit is contained in:
Andrew Eisenberg 2022-11-24 11:06:29 -08:00
parent ad7ca9bf21
commit eb19ecbad1
12 changed files with 112 additions and 92 deletions

5
lib/config-utils.js generated
View file

@ -19,7 +19,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.downloadPacks = exports.getConfig = exports.getPathToParsedConfigFile = exports.initConfig = exports.parsePacks = exports.validatePackSpecification = exports.prettyPrintPack = exports.parsePacksSpecification = exports.parsePacksFromConfig = exports.calculateAugmentation = exports.getDefaultConfig = exports.getRawLanguages = exports.getLanguages = exports.getUnknownLanguagesError = exports.getNoLanguagesError = exports.getConfigFileDirectoryGivenMessage = exports.getConfigFileFormatInvalidMessage = exports.getConfigFileRepoFormatInvalidMessage = exports.getConfigFileDoesNotExistErrorMessage = exports.getConfigFileOutsideWorkspaceErrorMessage = exports.getLocalPathDoesNotExist = exports.getLocalPathOutsideOfRepository = exports.getPacksStrInvalid = exports.getPacksInvalid = exports.getPacksInvalidSplit = exports.getPathsInvalid = exports.getPathsIgnoreInvalid = exports.getQueryUsesInvalid = exports.getQueriesMissingUses = exports.getQueriesInvalid = exports.getDisableDefaultQueriesInvalid = exports.getNameInvalid = exports.validateAndSanitisePath = exports.defaultAugmentationProperties = void 0;
exports.downloadPacks = exports.getConfig = exports.getPathToParsedConfigFile = exports.initConfig = exports.parsePacks = exports.validatePackSpecification = exports.prettyPrintPack = exports.parsePacksSpecification = exports.parsePacksFromConfig = exports.calculateAugmentation = exports.getDefaultConfig = exports.getRawLanguages = exports.getLanguages = exports.getLanguagesInRepo = exports.getUnknownLanguagesError = exports.getNoLanguagesError = exports.getConfigFileDirectoryGivenMessage = exports.getConfigFileFormatInvalidMessage = exports.getConfigFileRepoFormatInvalidMessage = exports.getConfigFileDoesNotExistErrorMessage = exports.getConfigFileOutsideWorkspaceErrorMessage = exports.getLocalPathDoesNotExist = exports.getLocalPathOutsideOfRepository = exports.getPacksStrInvalid = exports.getPacksInvalid = exports.getPacksInvalidSplit = exports.getPathsInvalid = exports.getPathsIgnoreInvalid = exports.getQueryUsesInvalid = exports.getQueriesMissingUses = exports.getQueriesInvalid = exports.getDisableDefaultQueriesInvalid = exports.getNameInvalid = exports.validateAndSanitisePath = exports.defaultAugmentationProperties = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
// We need to import `performance` on Node 12
@ -408,6 +408,7 @@ async function getLanguagesInRepo(repository, logger) {
}
return [...languages];
}
exports.getLanguagesInRepo = getLanguagesInRepo;
/**
* Get the languages to analyse.
*
@ -448,6 +449,8 @@ async function getLanguages(codeQL, languagesInput, repository, logger) {
parsedLanguages.push(parsedLanguage);
}
}
// Any unknown languages here would have come directly from the input
// since we filter unknown languages coming from the GitHub API.
if (unknownLanguages.length > 0) {
throw new Error(getUnknownLanguagesError(unknownLanguages));
}

File diff suppressed because one or more lines are too long

View file

@ -934,14 +934,7 @@ function parseInputAndConfigMacro(t, packsFromConfig, packsFromInput, languages,
languages, "/a/b", mockLogger), expected);
}
parseInputAndConfigMacro.title = (providedTitle) => `Parse Packs input and config: ${providedTitle}`;
const mockLogger = {
info: (message) => {
console.log("info:", message);
},
debug: (message) => {
console.log("debug:", message);
},
};
const mockLogger = (0, logging_1.getRunnerLogger)(true);
function parseInputAndConfigErrorMacro(t, packsFromConfig, packsFromInput, languages, packsFromInputOverride, expected) {
t.throws(() => {
configUtils.parsePacks(packsFromConfig, packsFromInput, packsFromInputOverride, languages, "/a/b", mockLogger);
@ -1285,23 +1278,6 @@ const mockRepositoryNwo = (0, repository_1.parseRepositoryNwo)("owner/repo");
expectedLanguages: ["javascript", "csharp", "cpp"],
expectedApiCall: true,
},
].forEach((args) => {
(0, ava_1.default)(`getLanguages: ${args.name}`, async (t) => {
const mockRequest = (0, testing_utils_1.mockLanguagesInRepo)(args.languagesInRepository);
const languages = args.codeqlResolvedLanguages.reduce((acc, lang) => ({
...acc,
[lang]: true,
}), {});
const codeQL = (0, codeql_1.setCodeQL)({
resolveLanguages: () => Promise.resolve(languages),
});
const actualLanguages = await configUtils.getLanguages(codeQL, args.languagesInput, mockRepositoryNwo, mockLogger);
t.deepEqual(actualLanguages.sort(), args.expectedLanguages.sort());
t.deepEqual(mockRequest.called, args.expectedApiCall);
});
});
// eslint-disable-next-line github/array-foreach
[
{
name: "no languages",
codeqlResolvedLanguages: ["javascript", "java", "python"],
@ -1319,7 +1295,7 @@ const mockRepositoryNwo = (0, repository_1.parseRepositoryNwo)("owner/repo");
expectedError: configUtils.getUnknownLanguagesError(["a", "b"]),
},
].forEach((args) => {
(0, ava_1.default)(`getLanguages (error when empty): ${args.name}`, async (t) => {
(0, ava_1.default)(`getLanguages: ${args.name}`, async (t) => {
const mockRequest = (0, testing_utils_1.mockLanguagesInRepo)(args.languagesInRepository);
const languages = args.codeqlResolvedLanguages.reduce((acc, lang) => ({
...acc,
@ -1328,7 +1304,15 @@ const mockRepositoryNwo = (0, repository_1.parseRepositoryNwo)("owner/repo");
const codeQL = (0, codeql_1.setCodeQL)({
resolveLanguages: () => Promise.resolve(languages),
});
await t.throwsAsync(async () => await configUtils.getLanguages(codeQL, args.languagesInput, mockRepositoryNwo, mockLogger), { message: args.expectedError });
if (args.expectedLanguages) {
// happy path
const actualLanguages = await configUtils.getLanguages(codeQL, args.languagesInput, mockRepositoryNwo, mockLogger);
t.deepEqual(actualLanguages.sort(), args.expectedLanguages.sort());
}
else {
// there is an error
await t.throwsAsync(async () => await configUtils.getLanguages(codeQL, args.languagesInput, mockRepositoryNwo, mockLogger), { message: args.expectedError });
}
t.deepEqual(mockRequest.called, args.expectedApiCall);
});
});

File diff suppressed because one or more lines are too long

17
lib/util.js generated
View file

@ -728,17 +728,26 @@ async function shouldBypassToolcache(featuresEnablement, codeqlUrl, languagesInp
if (await featuresEnablement.getValue(feature_flags_1.Feature.BypassToolcacheEnabled)) {
return true;
}
let bypass = false;
// Check if the toolcache is disabled for kotlin and swift.
if (await featuresEnablement.getValue(feature_flags_1.Feature.BypassToolcacheKotlinSwiftEnabled)) {
// Now check to see if kotlin or swift is one of the languages being analyzed.
const { rawLanguages } = await (0, config_utils_1.getRawLanguages)(languagesInput, repository, logger);
const bypass = rawLanguages.some((lang) => languages_1.KOTLIN_SWIFT_BYPASS.includes(lang));
const { rawLanguages, autodetected } = await (0, config_utils_1.getRawLanguages)(languagesInput, repository, logger);
bypass = rawLanguages.some((lang) => languages_1.KOTLIN_SWIFT_BYPASS.includes(lang));
if (bypass) {
logger.info(`Bypassing toolcache for kotlin or swift. Languages: ${rawLanguages}`);
}
return bypass;
else if (!autodetected && rawLanguages.includes(languages_1.Language.java)) {
// special case: java was explicitly specified, but there might be
// some kotlin in the repository, so we need to make a request for that.
const langsInRepo = await (0, config_utils_1.getLanguagesInRepo)(repository, logger);
if (langsInRepo.includes("kotlin")) {
logger.info(`Bypassing toolcache for kotlin.`);
bypass = true;
}
}
}
return false;
return bypass;
}
exports.shouldBypassToolcache = shouldBypassToolcache;
//# sourceMappingURL=util.js.map

File diff suppressed because one or more lines are too long

20
lib/util.test.js generated
View file

@ -449,10 +449,28 @@ const mockRepositoryNwo = (0, repository_1.parseRepositoryNwo)("owner/repo");
expected: true,
expectedApiCall: true,
},
{
name: "bypass java from input if there is kotlin in repository",
features: [feature_flags_1.Feature.BypassToolcacheKotlinSwiftEnabled],
hasCustomCodeQL: false,
languagesInput: "java",
languagesInRepository: ["kotlin", "other"],
expected: true,
expectedApiCall: true,
},
{
name: "don't bypass java from input if there is no kotlin in repository",
features: [feature_flags_1.Feature.BypassToolcacheKotlinSwiftEnabled],
hasCustomCodeQL: false,
languagesInput: "java",
languagesInRepository: ["java", "other"],
expected: false,
expectedApiCall: true,
},
].forEach((args) => {
(0, ava_1.default)(`shouldBypassToolcache: ${args.name}`, async (t) => {
const mockRequest = (0, testing_utils_1.mockLanguagesInRepo)(args.languagesInRepository);
const mockLogger = (0, testing_utils_1.getRecordingLogger)([]);
const mockLogger = (0, logging_1.getRunnerLogger)(true);
const featureEnablement = (0, testing_utils_1.createFeatures)(args.features);
const codeqlUrl = args.hasCustomCodeQL ? "custom-codeql-url" : undefined;
const actual = await util.shouldBypassToolcache(featureEnablement, codeqlUrl, args.languagesInput, mockRepositoryNwo, mockLogger);

File diff suppressed because one or more lines are too long

View file

@ -1764,14 +1764,7 @@ function parseInputAndConfigMacro(
parseInputAndConfigMacro.title = (providedTitle: string) =>
`Parse Packs input and config: ${providedTitle}`;
const mockLogger = {
info: (message: string) => {
console.log("info:", message);
},
debug: (message: string) => {
console.log("debug:", message);
},
} as Logger;
const mockLogger = getRunnerLogger(true);
function parseInputAndConfigErrorMacro(
t: ExecutionContext<unknown>,
@ -2503,33 +2496,6 @@ const mockRepositoryNwo = parseRepositoryNwo("owner/repo");
expectedLanguages: ["javascript", "csharp", "cpp"],
expectedApiCall: true,
},
].forEach((args) => {
test(`getLanguages: ${args.name}`, async (t) => {
const mockRequest = mockLanguagesInRepo(args.languagesInRepository);
const languages = args.codeqlResolvedLanguages.reduce(
(acc, lang) => ({
...acc,
[lang]: true,
}),
{}
);
const codeQL = setCodeQL({
resolveLanguages: () => Promise.resolve(languages),
});
const actualLanguages = await configUtils.getLanguages(
codeQL,
args.languagesInput,
mockRepositoryNwo,
mockLogger
);
t.deepEqual(actualLanguages.sort(), args.expectedLanguages.sort());
t.deepEqual(mockRequest.called, args.expectedApiCall);
});
});
// eslint-disable-next-line github/array-foreach
[
{
name: "no languages",
codeqlResolvedLanguages: ["javascript", "java", "python"],
@ -2547,7 +2513,7 @@ const mockRepositoryNwo = parseRepositoryNwo("owner/repo");
expectedError: configUtils.getUnknownLanguagesError(["a", "b"]),
},
].forEach((args) => {
test(`getLanguages (error when empty): ${args.name}`, async (t) => {
test(`getLanguages: ${args.name}`, async (t) => {
const mockRequest = mockLanguagesInRepo(args.languagesInRepository);
const languages = args.codeqlResolvedLanguages.reduce(
(acc, lang) => ({
@ -2560,16 +2526,29 @@ const mockRepositoryNwo = parseRepositoryNwo("owner/repo");
resolveLanguages: () => Promise.resolve(languages),
});
await t.throwsAsync(
async () =>
await configUtils.getLanguages(
codeQL,
args.languagesInput,
mockRepositoryNwo,
mockLogger
),
{ message: args.expectedError }
);
if (args.expectedLanguages) {
// happy path
const actualLanguages = await configUtils.getLanguages(
codeQL,
args.languagesInput,
mockRepositoryNwo,
mockLogger
);
t.deepEqual(actualLanguages.sort(), args.expectedLanguages.sort());
} else {
// there is an error
await t.throwsAsync(
async () =>
await configUtils.getLanguages(
codeQL,
args.languagesInput,
mockRepositoryNwo,
mockLogger
),
{ message: args.expectedError }
);
}
t.deepEqual(mockRequest.called, args.expectedApiCall);
});
});

View file

@ -860,7 +860,7 @@ export function getUnknownLanguagesError(languages: string[]): string {
* Gets the set of languages in the current repository that are
* scannable by CodeQL.
*/
async function getLanguagesInRepo(
export async function getLanguagesInRepo(
repository: RepositoryNwo,
logger: Logger
): Promise<LanguageOrAlias[]> {
@ -937,6 +937,9 @@ export async function getLanguages(
parsedLanguages.push(parsedLanguage);
}
}
// Any unknown languages here would have come directly from the input
// since we filter unknown languages coming from the GitHub API.
if (unknownLanguages.length > 0) {
throw new Error(getUnknownLanguagesError(unknownLanguages));
}

View file

@ -14,7 +14,6 @@ import { getRunnerLogger } from "./logging";
import { parseRepositoryNwo } from "./repository";
import {
createFeatures,
getRecordingLogger,
mockLanguagesInRepo,
setupTests,
} from "./testing-utils";
@ -541,10 +540,28 @@ const mockRepositoryNwo = parseRepositoryNwo("owner/repo");
expected: true,
expectedApiCall: true,
},
{
name: "bypass java from input if there is kotlin in repository",
features: [Feature.BypassToolcacheKotlinSwiftEnabled],
hasCustomCodeQL: false,
languagesInput: "java",
languagesInRepository: ["kotlin", "other"],
expected: true,
expectedApiCall: true,
},
{
name: "don't bypass java from input if there is no kotlin in repository",
features: [Feature.BypassToolcacheKotlinSwiftEnabled],
hasCustomCodeQL: false,
languagesInput: "java",
languagesInRepository: ["java", "other"],
expected: false,
expectedApiCall: true,
},
].forEach((args) => {
test(`shouldBypassToolcache: ${args.name}`, async (t) => {
const mockRequest = mockLanguagesInRepo(args.languagesInRepository);
const mockLogger = getRecordingLogger([]);
const mockLogger = getRunnerLogger(true);
const featureEnablement = createFeatures(args.features);
const codeqlUrl = args.hasCustomCodeQL ? "custom-codeql-url" : undefined;
const actual = await util.shouldBypassToolcache(

View file

@ -14,6 +14,7 @@ import * as apiCompatibility from "./api-compatibility.json";
import { CodeQL, CODEQL_VERSION_NEW_TRACING } from "./codeql";
import {
Config,
getLanguagesInRepo,
getRawLanguages,
parsePacksSpecification,
prettyPrintPack,
@ -860,25 +861,31 @@ export async function shouldBypassToolcache(
return true;
}
let bypass = false;
// Check if the toolcache is disabled for kotlin and swift.
if (
await featuresEnablement.getValue(Feature.BypassToolcacheKotlinSwiftEnabled)
) {
// Now check to see if kotlin or swift is one of the languages being analyzed.
const { rawLanguages } = await getRawLanguages(
const { rawLanguages, autodetected } = await getRawLanguages(
languagesInput,
repository,
logger
);
const bypass = rawLanguages.some((lang) =>
KOTLIN_SWIFT_BYPASS.includes(lang)
);
bypass = rawLanguages.some((lang) => KOTLIN_SWIFT_BYPASS.includes(lang));
if (bypass) {
logger.info(
`Bypassing toolcache for kotlin or swift. Languages: ${rawLanguages}`
);
} else if (!autodetected && rawLanguages.includes(Language.java)) {
// special case: java was explicitly specified, but there might be
// some kotlin in the repository, so we need to make a request for that.
const langsInRepo = await getLanguagesInRepo(repository, logger);
if (langsInRepo.includes("kotlin")) {
logger.info(`Bypassing toolcache for kotlin.`);
bypass = true;
}
}
return bypass;
}
return false;
return bypass;
}