Skip to content
Open
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
27 changes: 25 additions & 2 deletions lib/entry-points.js

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

15 changes: 15 additions & 0 deletions src/actions-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,21 @@ import {
*/
declare const __CODEQL_ACTION_VERSION__: string;

/**
* Abstracts over GitHub Actions functions so that we do not have to stub
* global functions in tests.
*/
export interface ActionsEnv {
getOptionalInput: (name: string) => string | undefined;
}

/**
* Gets the real `ActionsEnv` used by production code.
*/
export function getActionsEnv(): ActionsEnv {
return { getOptionalInput };
}

/**
* Wrapper around core.getInput for inputs that always have a value.
* Also see getOptionalInput.
Expand Down
58 changes: 58 additions & 0 deletions src/config/file.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import test from "ava";
import sinon from "sinon";

import { RepositoryPropertyName } from "../feature-flags/properties";
import {
getTestActionsEnv,
RecordingLogger,
setupTests,
} from "../testing-utils";

import { getConfigFileInput } from "./file";

setupTests(test);

test("getConfigFileInput returns undefined by default", async (t) => {
const logger = new RecordingLogger();
const actionsEnv = getTestActionsEnv();
const result = getConfigFileInput(logger, actionsEnv, {});
t.is(result, undefined);
});

const repositoryProperties = {
[RepositoryPropertyName.CONFIG_FILE]: "/path/from/property",
};

test("getConfigFileInput returns input value", async (t) => {
const logger = new RecordingLogger();
const actionsEnv = getTestActionsEnv();
const testInput = "/some/path";
sinon
.stub(actionsEnv, "getOptionalInput")
.withArgs("config-file")
.returns(testInput);

// Even though both an input and repository property are configured,
// we prefer the direct input to the Action.
const result = getConfigFileInput(logger, actionsEnv, repositoryProperties);
t.is(result, testInput);

// Check for the expected log message.
t.true(logger.hasMessage("Using configuration file input from workflow"));
});

test("getConfigFileInput returns repository property value", async (t) => {
const logger = new RecordingLogger();
const actionsEnv = getTestActionsEnv();

// Since there is no direct input, we should use the repository property.
const result = getConfigFileInput(logger, actionsEnv, repositoryProperties);
t.is(result, repositoryProperties[RepositoryPropertyName.CONFIG_FILE]);

// Check for the expected log message.
t.true(
logger.hasMessage(
"Using configuration file input from repository property",
),
);
});
34 changes: 34 additions & 0 deletions src/config/file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { ActionsEnv } from "../actions-util";
import {
RepositoryProperties,
RepositoryPropertyName,
} from "../feature-flags/properties";
import { Logger } from "../logging";

/**
* Gets the value that is configured for the configuration file, if any.
*/
export function getConfigFileInput(
logger: Logger,
actions: ActionsEnv,
repositoryProperties: Partial<RepositoryProperties>,
): string | undefined {
const input = actions.getOptionalInput("config-file");

if (input !== undefined) {
logger.info(`Using configuration file input from workflow: ${input}`);
return input;
}

const propertyValue =
repositoryProperties[RepositoryPropertyName.CONFIG_FILE];

if (propertyValue !== undefined) {
logger.info(
`Using configuration file input from repository property: ${propertyValue}`,
);
return propertyValue;
}
Comment on lines +23 to +31

return undefined;
}
6 changes: 5 additions & 1 deletion src/feature-flags/properties.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ test.serial("loadPropertiesFromApi loads known properties", async (t) => {
status: 200,
url: "",
data: [
{ property_name: "github-codeql-config-file", value: "owner/repo" },
Comment thread
mbg marked this conversation as resolved.
{ property_name: "github-codeql-extra-queries", value: "+queries" },
{ property_name: "unknown-property", value: "something" },
] satisfies properties.GitHubPropertiesResponse,
Expand All @@ -87,7 +88,10 @@ test.serial("loadPropertiesFromApi loads known properties", async (t) => {
logger,
mockRepositoryNwo,
);
t.deepEqual(response, { "github-codeql-extra-queries": "+queries" });
t.deepEqual(response, {
"github-codeql-config-file": "owner/repo",
Comment thread
mbg marked this conversation as resolved.
"github-codeql-extra-queries": "+queries",
});
});

test.serial("loadPropertiesFromApi parses true boolean property", async (t) => {
Expand Down
4 changes: 4 additions & 0 deletions src/feature-flags/properties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ export const GITHUB_CODEQL_PROPERTY_PREFIX = "github-codeql-";
* Enumerates repository property names that have some meaning to us.
*/
export enum RepositoryPropertyName {
CONFIG_FILE = "github-codeql-config-file",
DISABLE_OVERLAY = "github-codeql-disable-overlay",
EXTRA_QUERIES = "github-codeql-extra-queries",
FILE_COVERAGE_ON_PRS = "github-codeql-file-coverage-on-prs",
}

/** Parsed types of the known repository properties. */
export type AllRepositoryProperties = {
[RepositoryPropertyName.CONFIG_FILE]: string;
[RepositoryPropertyName.DISABLE_OVERLAY]: boolean;
[RepositoryPropertyName.EXTRA_QUERIES]: string;
[RepositoryPropertyName.FILE_COVERAGE_ON_PRS]: boolean;
Expand All @@ -27,6 +29,7 @@ export type RepositoryProperties = Partial<AllRepositoryProperties>;

/** Maps known repository properties to the type we expect to get from the API. */
export type RepositoryPropertyApiType = {
[RepositoryPropertyName.CONFIG_FILE]: string;
[RepositoryPropertyName.DISABLE_OVERLAY]: string;
[RepositoryPropertyName.EXTRA_QUERIES]: string;
[RepositoryPropertyName.FILE_COVERAGE_ON_PRS]: string;
Expand Down Expand Up @@ -74,6 +77,7 @@ const booleanProperty = {
const repositoryPropertyParsers: {
[K in RepositoryPropertyName]: PropertyInfo<K>;
} = {
[RepositoryPropertyName.CONFIG_FILE]: stringProperty,
[RepositoryPropertyName.DISABLE_OVERLAY]: booleanProperty,
[RepositoryPropertyName.EXTRA_QUERIES]: stringProperty,
[RepositoryPropertyName.FILE_COVERAGE_ON_PRS]: booleanProperty,
Expand Down
7 changes: 5 additions & 2 deletions src/init-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { v4 as uuidV4 } from "uuid";

import {
FileCmdNotFoundError,
getActionsEnv,
getActionVersion,
getFileType,
getOptionalInput,
Expand All @@ -24,6 +25,7 @@ import {
shouldRestoreCache,
} from "./caching-utils";
import { CodeQL } from "./codeql";
import { getConfigFileInput } from "./config/file";
import * as configUtils from "./config-utils";
import {
DependencyCacheRestoreStatusReport,
Expand Down Expand Up @@ -207,6 +209,7 @@ async function run(startedAt: Date) {
// possible, and only use safe functions outside.

const logger = getActionsLogger();
const actionsEnv = getActionsEnv();

let apiDetails: GitHubApiCombinedDetails;
let config: configUtils.Config | undefined;
Expand Down Expand Up @@ -251,6 +254,7 @@ async function run(startedAt: Date) {
repositoryNwo,
logger,
);
const repositoryProperties = repositoryPropertiesResult.orElse({});

// Create a unique identifier for this run.
const jobRunUuid = uuidV4();
Expand All @@ -259,7 +263,7 @@ async function run(startedAt: Date) {

core.exportVariable(EnvVar.INIT_ACTION_HAS_RUN, "true");

configFile = getOptionalInput("config-file");
configFile = getConfigFileInput(logger, actionsEnv, repositoryProperties);

// path.resolve() respects the intended semantics of source-root. If
// source-root is relative, it is relative to the GITHUB_WORKSPACE. If
Expand Down Expand Up @@ -350,7 +354,6 @@ async function run(startedAt: Date) {

analysisKinds = await getAnalysisKinds(logger, features);
const debugMode = getOptionalInput("debug") === "true" || core.isDebug();
const repositoryProperties = repositoryPropertiesResult.orElse({});
const fileCoverageResult = await getFileCoverageInformationEnabled(
debugMode,
codeql,
Expand Down
11 changes: 10 additions & 1 deletion src/testing-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import test, {
import nock from "nock";
import * as sinon from "sinon";

import { getActionVersion } from "./actions-util";
import { ActionsEnv, getActionVersion } from "./actions-util";
import { AnalysisKind } from "./analyses";
import * as apiClient from "./api-client";
import { GitHubApiDetails } from "./api-client";
Expand Down Expand Up @@ -172,6 +172,15 @@ export function makeMacro<Args extends unknown[]>(
return wrapper;
}

/**
* Gets an `ActionsEnv` instance for use in tests.
*/
export function getTestActionsEnv(): ActionsEnv {
return {
getOptionalInput: () => undefined,
};
Comment thread
mbg marked this conversation as resolved.
}

/**
* Default values for environment variables typically set in an Actions
* environment. Tests can override individual variables by passing them in the
Expand Down
Loading