Allow the codeql-action to be run locally (#117)

* Allow the codeql-action to be run locally

This change allows the codeql-action to be run locally through
[act](https://github.com/nektos/act).

In order to run the action locally, you need to do two things:

1. Add the `CODEQL_LOCAL_RUN: true` environment variable. The only way
   I could figure out how to do this was to add it directly in the
   workflow file in an `env` block. It _should_ be possible to add it
   through a `.env` file and pass it to `act`, but I couldn't get it
   working.
2. Run this command `act -j codeql -s GITHUB_TOKEN=<MY_PAT>`

Setting the `CODEQL_LOCAL_RUN` env var will fill in missing env vars
that the action needs, but isn't set by `act`. It will also avoid
making api calls to github that would fail locally.

This is a refactoring discussed in
https://github.com/github/dsp-codeql/issues/36
This commit is contained in:
Andrew Eisenberg 2020-08-04 14:35:20 -07:00 committed by GitHub
parent 631929a68f
commit 42235cc048
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 195 additions and 20 deletions

View file

@ -2,7 +2,12 @@ import * as core from "@actions/core";
import * as github from "@actions/github";
import consoleLogLevel from "console-log-level";
export const getApiClient = function() {
import { isLocalRun } from "./util";
export const getApiClient = function(allowLocalRun = false) {
if (isLocalRun() && !allowLocalRun) {
throw new Error('Invalid API call in local run');
}
return new github.GitHub(
core.getInput('token'),
{

View file

@ -36,6 +36,7 @@ async function run() {
const startedAt = new Date();
let language;
try {
util.prepareLocalRunEnvironment();
if (util.should_abort('autobuild', true) ||
!await util.sendStatusReport(await util.createStatusReportBase('autobuild', 'starting', startedAt), true)) {
return;

View file

@ -423,7 +423,7 @@ async function getLanguagesInRepo(): Promise<string[]> {
let repo = repo_nwo[1];
core.debug(`GitHub repo ${owner} ${repo}`);
const response = await api.getApiClient().request("GET /repos/:owner/:repo/languages", ({
const response = await api.getApiClient(true).request("GET /repos/:owner/:repo/languages", ({
owner,
repo
}));
@ -636,7 +636,7 @@ async function getRemoteConfig(configFile: string): Promise<UserConfig> {
throw new Error(getConfigFileRepoFormatInvalidMessage(configFile));
}
const response = await api.getApiClient().repos.getContents({
const response = await api.getApiClient(true).repos.getContents({
owner: pieces.groups.owner,
repo: pieces.groups.repo,
path: pieces.groups.path,

View file

@ -125,6 +125,7 @@ async function run() {
let queriesStats: QueriesStatusReport | undefined = undefined;
let uploadStats: upload_lib.UploadStatusReport | undefined = undefined;
try {
util.prepareLocalRunEnvironment();
if (util.should_abort('finish', true) ||
!await util.sendStatusReport(await util.createStatusReportBase('finish', 'starting', startedAt), true)) {
return;

View file

@ -177,6 +177,7 @@ async function run() {
let codeql: CodeQL;
try {
util.prepareLocalRunEnvironment();
if (util.should_abort('init', false) ||
!await util.sendStatusReport(await util.createStatusReportBase('init', 'starting', startedAt), true)) {
return;

View file

@ -218,9 +218,12 @@ async function uploadFiles(sarifFiles: string[]): Promise<UploadStatusReport> {
const numResultInSarif = countResultsInSarif(sarifPayload);
core.debug("Number of results in upload: " + numResultInSarif);
// Make the upload
await uploadPayload(payload);
if (!util.isLocalRun()) {
// Make the upload
await uploadPayload(payload);
} else {
core.debug("Not uploading because this is a local run.");
}
core.endGroup();
return {

View file

@ -67,3 +67,52 @@ test('getRef() throws on the empty string', t => {
process.env["GITHUB_REF"] = "";
t.throws(util.getRef);
});
test('isLocalRun() runs correctly', t => {
const origLocalRun = process.env.CODEQL_LOCAL_RUN;
process.env.CODEQL_LOCAL_RUN = '';
t.assert(!util.isLocalRun());
process.env.CODEQL_LOCAL_RUN = 'false';
t.assert(!util.isLocalRun());
process.env.CODEQL_LOCAL_RUN = '0';
t.assert(!util.isLocalRun());
process.env.CODEQL_LOCAL_RUN = 'true';
t.assert(util.isLocalRun());
process.env.CODEQL_LOCAL_RUN = 'hucairz';
t.assert(util.isLocalRun());
process.env.CODEQL_LOCAL_RUN = origLocalRun;
});
test('prepareEnvironment() when a local run', t => {
const origLocalRun = process.env.CODEQL_LOCAL_RUN;
process.env.CODEQL_LOCAL_RUN = 'false';
process.env.GITHUB_JOB = 'YYY';
util.prepareLocalRunEnvironment();
// unchanged
t.deepEqual(process.env.GITHUB_JOB, 'YYY');
process.env.CODEQL_LOCAL_RUN = 'true';
util.prepareLocalRunEnvironment();
// unchanged
t.deepEqual(process.env.GITHUB_JOB, 'YYY');
process.env.GITHUB_JOB = '';
util.prepareLocalRunEnvironment();
// updated
t.deepEqual(process.env.GITHUB_JOB, 'UNKNOWN-JOB');
process.env.CODEQL_LOCAL_RUN = origLocalRun;
});

View file

@ -64,6 +64,26 @@ export function getRequiredEnvParam(paramName: string): string {
return value;
}
export function isLocalRun(): boolean {
return !!process.env.CODEQL_LOCAL_RUN
&& process.env.CODEQL_LOCAL_RUN !== 'false'
&& process.env.CODEQL_LOCAL_RUN !== '0';
}
/**
* Ensures all required environment variables are set in the context of a local run.
*/
export function prepareLocalRunEnvironment() {
if (!isLocalRun()) {
return;
}
core.debug('Action is running locally.');
if (!process.env.GITHUB_JOB) {
core.exportVariable('GITHUB_JOB', 'UNKNOWN-JOB');
}
}
/**
* Gets the SHA of the commit that is currently checked out.
*/
@ -95,6 +115,9 @@ export async function getCommitOid(): Promise<string> {
* Get the path of the currently executing workflow.
*/
async function getWorkflowPath(): Promise<string> {
if (isLocalRun()) {
return 'LOCAL';
}
const repo_nwo = getRequiredEnvParam('GITHUB_REPOSITORY').split("/");
const owner = repo_nwo[0];
const repo = repo_nwo[1];
@ -273,8 +296,12 @@ export async function sendStatusReport<S extends StatusReportBase>(
return true;
}
const statusReportJSON = JSON.stringify(statusReport);
if (isLocalRun()) {
core.debug("Not sending status report because this is a local run");
return true;
}
const statusReportJSON = JSON.stringify(statusReport);
core.debug('Sending status report: ' + statusReportJSON);
const nwo = getRequiredEnvParam("GITHUB_REPOSITORY");