Add support for basic query overriding in action file itself

See https://github.com/github/dsp-code-scanning/issues/1446
This commit is contained in:
Sam Partington 2020-07-27 11:03:57 +01:00
parent 74268130c6
commit 95cef22589
6 changed files with 141 additions and 2 deletions

18
lib/config-utils.js generated
View file

@ -460,6 +460,12 @@ async function initConfig() {
else {
config = await loadConfig(configFile);
}
// If queries were provided as using `with` in the action configuration,
// they should take precedence over the queries in the config file
const queryUses = core.getInput('queries');
if (queryUses) {
config = await updateConfigWithQueries(config, queryUses, configFile);
}
// Save the config so we can easily access it again in the future
await saveConfig(config);
return config;
@ -472,6 +478,18 @@ function isLocal(configPath) {
}
return (configPath.indexOf("@") === -1);
}
async function updateConfigWithQueries(config, queryUses, configPath) {
if (isLocal(configPath)) {
// Treat the config file as relative to the workspace
const workspacePath = util.getRequiredEnvParam('GITHUB_WORKSPACE');
configPath = path.resolve(workspacePath, configPath);
}
const languages = await getLanguages();
const queries = {};
await parseQueryUses(configPath, languages, queries, queryUses);
config.queries = queries;
return config;
}
function getLocalConfig(configFile, workspacePath) {
// Error if the config file is now outside of the workspace
if (!(configFile + path.sep).startsWith(workspacePath + path.sep)) {

File diff suppressed because one or more lines are too long

View file

@ -224,6 +224,51 @@ ava_1.default("default queries are used", async (t) => {
t.deepEqual(resolveQueriesArgs[0].extraSearchPath, undefined);
});
});
ava_1.default("Queries can be overridden in action file", async (t) => {
return await util.withTmpDir(async (tmpDir) => {
process.env['RUNNER_TEMP'] = tmpDir;
process.env['GITHUB_WORKSPACE'] = tmpDir;
const inputFileContents = `
name: my config
queries:
- uses: ./foo`;
fs.mkdirSync(path.join(tmpDir, 'foo'));
fs.mkdirSync(path.join(tmpDir, 'override'));
// This config item should take precedence.
setInput('queries', './override');
const resolveQueriesArgs = [];
CodeQL.setCodeQL({
resolveQueries: async function (queries, extraSearchPath) {
resolveQueriesArgs.push({ queries, extraSearchPath });
// Return what we're given, just in the right format for a resolved query
// This way we can test overriding by seeing which returned items are in
// the final configuration.
const dummyResolvedQueries = {};
queries.forEach(q => { dummyResolvedQueries[q] = {}; });
return {
byLanguage: {
'javascript': dummyResolvedQueries,
},
noDeclaredLanguage: {},
multipleDeclaredLanguages: {},
};
},
});
fs.writeFileSync(path.join(tmpDir, 'input'), inputFileContents, 'utf8');
setInput('config-file', 'input');
setInput('languages', 'javascript');
const config = await configUtils.initConfig();
// Check resolveQueries was called correctly
// It'll be called once for the default queries, once for './foo' (from the config file),
// and then finally for './override'
t.deepEqual(resolveQueriesArgs.length, 3);
t.deepEqual(resolveQueriesArgs[2].queries.length, 1);
t.regex(resolveQueriesArgs[2].queries[0], /.*\/override$/);
// Now check that the end result contains only the override query, not the others
t.deepEqual(config.queries['javascript'].length, 1);
t.regex(config.queries['javascript'][0], /.*\/override$/);
});
});
ava_1.default("API client used when reading remote config", async (t) => {
return await util.withTmpDir(async (tmpDir) => {
process.env['RUNNER_TEMP'] = tmpDir;

File diff suppressed because one or more lines are too long

View file

@ -254,6 +254,60 @@ test("default queries are used", async t => {
});
});
test("Queries can be overridden in action file", async t => {
return await util.withTmpDir(async tmpDir => {
process.env['RUNNER_TEMP'] = tmpDir;
process.env['GITHUB_WORKSPACE'] = tmpDir;
const inputFileContents = `
name: my config
queries:
- uses: ./foo`;
// This config item should take precedence.
setInput('queries', './override');
fs.mkdirSync(path.join(tmpDir, 'foo'));
fs.mkdirSync(path.join(tmpDir, 'override'));
const resolveQueriesArgs: {queries: string[], extraSearchPath: string | undefined}[] = [];
CodeQL.setCodeQL({
resolveQueries: async function(queries: string[], extraSearchPath: string | undefined) {
resolveQueriesArgs.push({queries, extraSearchPath});
// Return what we're given, just in the right format for a resolved query
// This way we can test overriding by seeing which returned items are in
// the final configuration.
const dummyResolvedQueries = {};
queries.forEach(q => { dummyResolvedQueries[q] = {}; });
return {
byLanguage: {
'javascript': dummyResolvedQueries,
},
noDeclaredLanguage: {},
multipleDeclaredLanguages: {},
};
},
});
fs.writeFileSync(path.join(tmpDir, 'input'), inputFileContents, 'utf8');
setInput('config-file', 'input');
setInput('languages', 'javascript');
const config = await configUtils.initConfig();
// Check resolveQueries was called correctly
// It'll be called once for the default queries, once for './foo' (from the config file),
// and then finally for './override'
t.deepEqual(resolveQueriesArgs.length, 3);
t.deepEqual(resolveQueriesArgs[2].queries.length, 1);
t.regex(resolveQueriesArgs[2].queries[0], /.*\/override$/);
// Now check that the end result contains only the override query, not the others
t.deepEqual(config.queries['javascript'].length, 1);
t.regex(config.queries['javascript'][0], /.*\/override$/);
});
});
test("API client used when reading remote config", async t => {
return await util.withTmpDir(async tmpDir => {
process.env['RUNNER_TEMP'] = tmpDir;

View file

@ -599,6 +599,13 @@ export async function initConfig(): Promise<Config> {
config = await loadConfig(configFile);
}
// If queries were provided as using `with` in the action configuration,
// they should take precedence over the queries in the config file
const queryUses = core.getInput('queries');
if (queryUses) {
config = await updateConfigWithQueries(config, queryUses, configFile);
}
// Save the config so we can easily access it again in the future
await saveConfig(config);
return config;
@ -613,6 +620,21 @@ function isLocal(configPath: string): boolean {
return (configPath.indexOf("@") === -1);
}
async function updateConfigWithQueries(config: Config, queryUses: string, configPath: string): Promise<Config> {
if (isLocal(configPath)) {
// Treat the config file as relative to the workspace
const workspacePath = util.getRequiredEnvParam('GITHUB_WORKSPACE');
configPath = path.resolve(workspacePath, configPath);
}
const languages = await getLanguages();
const queries = {};
await parseQueryUses(configPath, languages, queries, queryUses);
config.queries = queries;
return config;
}
function getLocalConfig(configFile: string, workspacePath: string): UserConfig {
// Error if the config file is now outside of the workspace
if (!(configFile + path.sep).startsWith(workspacePath + path.sep)) {