Skip to content

Commit 49e5245

Browse files
authored
Merge pull request #3963 from github/mbg/repo-prop/config-file
Add repository property for configuration files
2 parents 0337b99 + 52077a0 commit 49e5245

8 files changed

Lines changed: 167 additions & 6 deletions

File tree

lib/entry-points.js

Lines changed: 25 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/actions-util.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,21 @@ import {
2121
*/
2222
declare const __CODEQL_ACTION_VERSION__: string;
2323

24+
/**
25+
* Abstracts over GitHub Actions functions so that we do not have to stub
26+
* global functions in tests.
27+
*/
28+
export interface ActionsEnv {
29+
getOptionalInput: (name: string) => string | undefined;
30+
}
31+
32+
/**
33+
* Gets the real `ActionsEnv` used by production code.
34+
*/
35+
export function getActionsEnv(): ActionsEnv {
36+
return { getOptionalInput };
37+
}
38+
2439
/**
2540
* Wrapper around core.getInput for inputs that always have a value.
2641
* Also see getOptionalInput.

src/config/file.test.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import test from "ava";
2+
import sinon from "sinon";
3+
4+
import { RepositoryPropertyName } from "../feature-flags/properties";
5+
import {
6+
getTestActionsEnv,
7+
RecordingLogger,
8+
setupTests,
9+
} from "../testing-utils";
10+
11+
import { getConfigFileInput } from "./file";
12+
13+
setupTests(test);
14+
15+
test("getConfigFileInput returns undefined by default", async (t) => {
16+
const logger = new RecordingLogger();
17+
const actionsEnv = getTestActionsEnv();
18+
const result = getConfigFileInput(logger, actionsEnv, {});
19+
t.is(result, undefined);
20+
});
21+
22+
const repositoryProperties = {
23+
[RepositoryPropertyName.CONFIG_FILE]: "/path/from/property",
24+
};
25+
26+
test("getConfigFileInput returns input value", async (t) => {
27+
const logger = new RecordingLogger();
28+
const actionsEnv = getTestActionsEnv();
29+
const testInput = "/some/path";
30+
sinon
31+
.stub(actionsEnv, "getOptionalInput")
32+
.withArgs("config-file")
33+
.returns(testInput);
34+
35+
// Even though both an input and repository property are configured,
36+
// we prefer the direct input to the Action.
37+
const result = getConfigFileInput(logger, actionsEnv, repositoryProperties);
38+
t.is(result, testInput);
39+
40+
// Check for the expected log message.
41+
t.true(logger.hasMessage("Using configuration file input from workflow"));
42+
});
43+
44+
test("getConfigFileInput returns repository property value", async (t) => {
45+
const logger = new RecordingLogger();
46+
const actionsEnv = getTestActionsEnv();
47+
48+
// Since there is no direct input, we should use the repository property.
49+
const result = getConfigFileInput(logger, actionsEnv, repositoryProperties);
50+
t.is(result, repositoryProperties[RepositoryPropertyName.CONFIG_FILE]);
51+
52+
// Check for the expected log message.
53+
t.true(
54+
logger.hasMessage(
55+
"Using configuration file input from repository property",
56+
),
57+
);
58+
});
59+
60+
test("getConfigFileInput ignores empty repository property value", async (t) => {
61+
const logger = new RecordingLogger();
62+
const actionsEnv = getTestActionsEnv();
63+
64+
// Since the repository property value is an empty/whitespace string, we should ignore it.
65+
const result = getConfigFileInput(logger, actionsEnv, {
66+
[RepositoryPropertyName.CONFIG_FILE]: " ",
67+
});
68+
t.is(result, undefined);
69+
});

src/config/file.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { ActionsEnv } from "../actions-util";
2+
import {
3+
RepositoryProperties,
4+
RepositoryPropertyName,
5+
} from "../feature-flags/properties";
6+
import { Logger } from "../logging";
7+
8+
/**
9+
* Gets the value that is configured for the configuration file, if any.
10+
*/
11+
export function getConfigFileInput(
12+
logger: Logger,
13+
actions: ActionsEnv,
14+
repositoryProperties: Partial<RepositoryProperties>,
15+
): string | undefined {
16+
const input = actions.getOptionalInput("config-file");
17+
18+
if (input !== undefined) {
19+
logger.info(`Using configuration file input from workflow: ${input}`);
20+
return input;
21+
}
22+
23+
const propertyValue =
24+
repositoryProperties[RepositoryPropertyName.CONFIG_FILE];
25+
26+
if (propertyValue !== undefined && propertyValue.trim().length > 0) {
27+
logger.info(
28+
`Using configuration file input from repository property: ${propertyValue}`,
29+
);
30+
return propertyValue;
31+
}
32+
33+
return undefined;
34+
}

src/feature-flags/properties.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ test.serial("loadPropertiesFromApi loads known properties", async (t) => {
7777
status: 200,
7878
url: "",
7979
data: [
80+
{ property_name: "github-codeql-config-file", value: "owner/repo" },
8081
{ property_name: "github-codeql-extra-queries", value: "+queries" },
8182
{ property_name: "unknown-property", value: "something" },
8283
] satisfies properties.GitHubPropertiesResponse,
@@ -87,7 +88,10 @@ test.serial("loadPropertiesFromApi loads known properties", async (t) => {
8788
logger,
8889
mockRepositoryNwo,
8990
);
90-
t.deepEqual(response, { "github-codeql-extra-queries": "+queries" });
91+
t.deepEqual(response, {
92+
"github-codeql-config-file": "owner/repo",
93+
"github-codeql-extra-queries": "+queries",
94+
});
9195
});
9296

9397
test.serial("loadPropertiesFromApi parses true boolean property", async (t) => {

src/feature-flags/properties.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ export const GITHUB_CODEQL_PROPERTY_PREFIX = "github-codeql-";
1010
* Enumerates repository property names that have some meaning to us.
1111
*/
1212
export enum RepositoryPropertyName {
13+
CONFIG_FILE = "github-codeql-config-file",
1314
DISABLE_OVERLAY = "github-codeql-disable-overlay",
1415
EXTRA_QUERIES = "github-codeql-extra-queries",
1516
FILE_COVERAGE_ON_PRS = "github-codeql-file-coverage-on-prs",
1617
}
1718

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

2830
/** Maps known repository properties to the type we expect to get from the API. */
2931
export type RepositoryPropertyApiType = {
32+
[RepositoryPropertyName.CONFIG_FILE]: string;
3033
[RepositoryPropertyName.DISABLE_OVERLAY]: string;
3134
[RepositoryPropertyName.EXTRA_QUERIES]: string;
3235
[RepositoryPropertyName.FILE_COVERAGE_ON_PRS]: string;
@@ -74,6 +77,7 @@ const booleanProperty = {
7477
const repositoryPropertyParsers: {
7578
[K in RepositoryPropertyName]: PropertyInfo<K>;
7679
} = {
80+
[RepositoryPropertyName.CONFIG_FILE]: stringProperty,
7781
[RepositoryPropertyName.DISABLE_OVERLAY]: booleanProperty,
7882
[RepositoryPropertyName.EXTRA_QUERIES]: stringProperty,
7983
[RepositoryPropertyName.FILE_COVERAGE_ON_PRS]: booleanProperty,

src/init-action.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { v4 as uuidV4 } from "uuid";
99

1010
import {
1111
FileCmdNotFoundError,
12+
getActionsEnv,
1213
getActionVersion,
1314
getFileType,
1415
getOptionalInput,
@@ -24,6 +25,7 @@ import {
2425
shouldRestoreCache,
2526
} from "./caching-utils";
2627
import { CodeQL } from "./codeql";
28+
import { getConfigFileInput } from "./config/file";
2729
import * as configUtils from "./config-utils";
2830
import {
2931
DependencyCacheRestoreStatusReport,
@@ -207,6 +209,7 @@ async function run(startedAt: Date) {
207209
// possible, and only use safe functions outside.
208210

209211
const logger = getActionsLogger();
212+
const actionsEnv = getActionsEnv();
210213

211214
let apiDetails: GitHubApiCombinedDetails;
212215
let config: configUtils.Config | undefined;
@@ -251,6 +254,7 @@ async function run(startedAt: Date) {
251254
repositoryNwo,
252255
logger,
253256
);
257+
const repositoryProperties = repositoryPropertiesResult.orElse({});
254258

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

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

262-
configFile = getOptionalInput("config-file");
266+
configFile = getConfigFileInput(logger, actionsEnv, repositoryProperties);
263267

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

351355
analysisKinds = await getAnalysisKinds(logger, features);
352356
const debugMode = getOptionalInput("debug") === "true" || core.isDebug();
353-
const repositoryProperties = repositoryPropertiesResult.orElse({});
354357
const fileCoverageResult = await getFileCoverageInformationEnabled(
355358
debugMode,
356359
codeql,

src/testing-utils.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import test, {
1010
import nock from "nock";
1111
import * as sinon from "sinon";
1212

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

175+
/**
176+
* Gets an `ActionsEnv` instance for use in tests.
177+
*/
178+
export function getTestActionsEnv(): ActionsEnv {
179+
return {
180+
getOptionalInput: () => undefined,
181+
};
182+
}
183+
175184
/**
176185
* Default values for environment variables typically set in an Actions
177186
* environment. Tests can override individual variables by passing them in the

0 commit comments

Comments
 (0)