move config parsing earlier + add to codeql search path

This commit is contained in:
Robert Brignull 2020-06-26 15:33:59 +01:00
parent c3dcf26eaf
commit da3d6d25eb
36 changed files with 1115 additions and 812 deletions

View file

@ -5,6 +5,7 @@ import * as path from 'path';
import sinon from 'sinon';
import * as api from './api-client';
import * as CodeQL from './codeql';
import * as configUtils from './config-utils';
import {setupTests} from './testing-utils';
import * as util from './util';
@ -41,10 +42,21 @@ test("load empty config", async t => {
process.env['GITHUB_WORKSPACE'] = tmpDir;
setInput('config-file', undefined);
setInput('languages', 'javascript,python');
const config = await configUtils.loadConfig();
CodeQL.setCodeQL({
resolveQueries: async function() {
return {
byLanguage: {},
noDeclaredLanguage: {},
multipleDeclaredLanguages: {},
};
},
});
t.deepEqual(config, new configUtils.Config());
const config = await configUtils.initConfig();
t.deepEqual(config, await configUtils.getBlankConfig());
});
});
@ -53,17 +65,30 @@ test("loading config saves config", async t => {
process.env['RUNNER_TEMP'] = tmpDir;
process.env['GITHUB_WORKSPACE'] = tmpDir;
const configFile = configUtils.getConfigFile();
// Sanity check the saved config file does not already exist
t.false(fs.existsSync(configFile));
setInput('config-file', undefined);
setInput('languages', 'javascript,python');
const config = await configUtils.loadConfig();
CodeQL.setCodeQL({
resolveQueries: async function() {
return {
byLanguage: {},
noDeclaredLanguage: {},
multipleDeclaredLanguages: {},
};
},
});
// Sanity check the saved config file does not already exist
t.false(fs.existsSync(configUtils.getParsedConfigFile()));
const config1 = await configUtils.initConfig();
// The saved config file should now exist
t.true(fs.existsSync(configFile));
t.true(fs.existsSync(configUtils.getParsedConfigFile()));
// And the contents should parse correctly to the config that was returned
t.deepEqual(fs.readFileSync(configFile, 'utf8'), JSON.stringify(config));
// And the same config should be returned again
const config2 = await configUtils.getConfig();
t.deepEqual(config1, config2);
});
});
@ -75,8 +100,8 @@ test("load input outside of workspace", async t => {
setInput('config-file', '../input');
try {
await configUtils.loadConfig();
throw new Error('loadConfig did not throw error');
await configUtils.initConfig();
throw new Error('initConfig did not throw error');
} catch (err) {
t.deepEqual(err, new Error(configUtils.getConfigFileOutsideWorkspaceErrorMessage(path.join(tmpDir, '../input'))));
}
@ -92,8 +117,8 @@ test("load non-local input with invalid repo syntax", async t => {
setInput('config-file', 'octo-org/codeql-config@main');
try {
await configUtils.loadConfig();
throw new Error('loadConfig did not throw error');
await configUtils.initConfig();
throw new Error('initConfig did not throw error');
} catch (err) {
t.deepEqual(err, new Error(configUtils.getConfigFileRepoFormatInvalidMessage('octo-org/codeql-config@main')));
}
@ -107,10 +132,11 @@ test("load non-existent input", async t => {
t.false(fs.existsSync(path.join(tmpDir, 'input')));
setInput('config-file', 'input');
setInput('languages', 'javascript');
try {
await configUtils.loadConfig();
throw new Error('loadConfig did not throw error');
await configUtils.initConfig();
throw new Error('initConfig did not throw error');
} catch (err) {
t.deepEqual(err, new Error(configUtils.getConfigFileDoesNotExistErrorMessage(path.join(tmpDir, 'input'))));
}
@ -122,7 +148,68 @@ test("load non-empty input", async t => {
process.env['RUNNER_TEMP'] = tmpDir;
process.env['GITHUB_WORKSPACE'] = tmpDir;
CodeQL.setCodeQL({
resolveQueries: async function() {
return {
byLanguage: {
'javascript': {
'/foo/a.ql': {},
'/bar/b.ql': {},
},
},
noDeclaredLanguage: {},
multipleDeclaredLanguages: {},
};
},
});
// Just create a generic config object with non-default values for all fields
const inputFileContents = `
name: my config
disable-default-queries: true
queries:
- uses: ./foo
paths-ignore:
- a
- b
paths:
- c/d`;
fs.mkdirSync(path.join(tmpDir, 'foo'));
// And the config we expect it to parse to
const expectedConfig: configUtils.Config = {
languages: ['javascript'],
queries: {'javascript': ['/foo/a.ql', '/bar/b.ql']},
pathsIgnore: ['a', 'b'],
paths: ['c/d'],
};
fs.writeFileSync(path.join(tmpDir, 'input'), inputFileContents, 'utf8');
setInput('config-file', 'input');
const actualConfig = await configUtils.initConfig();
// Should exactly equal the object we constructed earlier
t.deepEqual(actualConfig, expectedConfig);
});
});
test("API client used when reading remote config", async t => {
return await util.withTmpDir(async tmpDir => {
process.env['RUNNER_TEMP'] = tmpDir;
process.env['GITHUB_WORKSPACE'] = tmpDir;
CodeQL.setCodeQL({
resolveQueries: async function() {
return {
byLanguage: {},
noDeclaredLanguage: {},
multipleDeclaredLanguages: {},
};
},
});
const inputFileContents = `
name: my config
disable-default-queries: true
@ -135,51 +222,16 @@ test("load non-empty input", async t => {
- b
paths:
- c/d`;
fs.mkdirSync(path.join(tmpDir, 'foo'));
// And the config we expect it to parse to
const expectedConfig = new configUtils.Config();
expectedConfig.name = 'my config';
expectedConfig.disableDefaultQueries = true;
expectedConfig.additionalQueries.push(fs.realpathSync(tmpDir));
expectedConfig.additionalQueries.push(fs.realpathSync(path.join(tmpDir, 'foo')));
expectedConfig.externalQueries = [new configUtils.ExternalQuery('foo/bar', 'dev')];
expectedConfig.pathsIgnore = ['a', 'b'];
expectedConfig.paths = ['c/d'];
fs.writeFileSync(path.join(tmpDir, 'input'), inputFileContents, 'utf8');
setInput('config-file', 'input');
const actualConfig = await configUtils.loadConfig();
// Should exactly equal the object we constructed earlier
t.deepEqual(actualConfig, expectedConfig);
});
});
test("API client used when reading remote config", async t => {
return await util.withTmpDir(async tmpDir => {
process.env['RUNNER_TEMP'] = tmpDir;
process.env['GITHUB_WORKSPACE'] = tmpDir;
const inputFileContents = `
name: my config
disable-default-queries: true
queries:
- uses: ./
paths-ignore:
- a
- b
paths:
- c/d`;
const dummyResponse = {
content: Buffer.from(inputFileContents).toString("base64"),
};
const spyGetContents = mockGetContents(dummyResponse);
// Create checkout directory for remote queries repository
fs.mkdirSync(path.join(tmpDir, 'foo/bar'), { recursive: true });
setInput('config-file', 'octo-org/codeql-config/config.yaml@main');
await configUtils.loadConfig();
await configUtils.initConfig();
t.assert(spyGetContents.called);
});
});
@ -195,8 +247,8 @@ test("Remote config handles the case where a directory is provided", async t =>
const repoReference = 'octo-org/codeql-config/config.yaml@main';
setInput('config-file', repoReference);
try {
await configUtils.loadConfig();
throw new Error('loadConfig did not throw error');
await configUtils.initConfig();
throw new Error('initConfig did not throw error');
} catch (err) {
t.deepEqual(err, new Error(configUtils.getConfigFileDirectoryGivenMessage(repoReference)));
}
@ -216,8 +268,8 @@ test("Invalid format of remote config handled correctly", async t => {
const repoReference = 'octo-org/codeql-config/config.yaml@main';
setInput('config-file', repoReference);
try {
await configUtils.loadConfig();
throw new Error('loadConfig did not throw error');
await configUtils.initConfig();
throw new Error('initConfig did not throw error');
} catch (err) {
t.deepEqual(err, new Error(configUtils.getConfigFileFormatInvalidMessage(repoReference)));
}
@ -239,8 +291,8 @@ function doInvalidInputTest(
setInput('config-file', 'input');
try {
await configUtils.loadConfig();
throw new Error('loadConfig did not throw error');
await configUtils.initConfig();
throw new Error('initConfig did not throw error');
} catch (err) {
t.deepEqual(err, new Error(expectedErrorMessageGenerator(inputFile)));
}