Use config-file for both remote and local config-files

This commit is contained in:
cnagadya
2022-11-07 12:12:03 +00:00
parent dcdeb7de77
commit b55cddb69d
6 changed files with 102 additions and 99 deletions
+11 -12
View File
@@ -71,22 +71,21 @@ or by inlining these options in your workflow file.
### config-file
A string representing the path to a configuraton file local to the repo.
**Possible values**: A string representing an absolute path to a file.
**Example**: `config-file: ./.github/dependency-review-config.yml`.
### remote-config-file
A string representing the path to a configuraton file in an external repo. It follows the
A string representing the path to a configuraton file. The
configuration file can be local to the repo, or can be a file in an
external repo. If you are referencing a configuration file located in an
external repository, you can use the
`OWNER/REPOSITORY/FILENAME@BRANCH` syntax.
If the file is located in a private repository, use the [remote-config-repo-token](#remote-config-repo-token) parameter of the action to specify a token that has read access to the repository.
If the configuration file is located in an external private
repository, use the [external-repository-token](#external-repository-token) parameter of the action to specify a token that has read access to the repository.
**Possible values**: A string representing a path to a file located in another repository:
**Possible values**: A string representing an absolute path to a file,
or a file located in another repository:
**Example**: `config-file: github/octorepo/dependency-review-config.yml@main`
**Example**: `config-file: ./.github/dependency-review-config.yml # local file`.
**Example**: `config-file: github/octorepo/dependency-review-config.yml@main # external repo`
### fail-on-severity
+3 -3
View File
@@ -1,5 +1,5 @@
import {expect, test, beforeEach} from '@jest/globals'
import {readConfig, readConfigFile} from '../src/config'
import {readConfig, parseConfigFile} from '../src/config'
import {getRefs} from '../src/git-refs'
import * as Utils from '../src/utils'
@@ -97,13 +97,13 @@ test('it raises an error when no refs are provided and the event is not a pull r
})
test.skip('it reads an external config file', async () => {
let options = readConfigFile('./__tests__/fixtures/config-allow-sample.yml')
let options = parseConfigFile('./__tests__/fixtures/config-allow-sample.yml')
expect(options.fail_on_severity).toEqual('critical')
expect(options.allow_licenses).toEqual(['BSD', 'GPL 2'])
})
test.skip('raises an error when the the config file was not found', () => {
expect(() => readConfigFile('fixtures/i-dont-exist')).toThrow()
expect(() => parseConfigFile('fixtures/i-dont-exist')).toThrow()
})
test('it parses options from both sources', async () => {
-3
View File
@@ -35,9 +35,6 @@ inputs:
remote-config-repo-token:
description: A token for fetching external configuration file if it lives in another repository. It is required if the repository is private
required: false
remote-config-file:
description: 'Path to the configuration file in another repository.'
required: false
runs:
using: 'node16'
Generated Vendored
+43 -38
View File
@@ -27421,7 +27421,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.getRepoConfig = exports.readConfigFile = exports.readInlineConfig = exports.readConfig = void 0;
exports.parseConfigFile = exports.readConfigFile = exports.readInlineConfig = exports.readConfig = void 0;
const fs = __importStar(__nccwpck_require__(7147));
const path_1 = __importDefault(__nccwpck_require__(1017));
const yaml_1 = __importDefault(__nccwpck_require__(4083));
@@ -27456,24 +27456,17 @@ function validateLicenses(key, licenses) {
}
function readConfig() {
return __awaiter(this, void 0, void 0, function* () {
const remoteConfigFile = getOptionalInput('remote-config-file');
const repoConfigFile = getOptionalInput('config-file');
const inlineConfig = readInlineConfig();
let remoteConfig = {};
let repoConfig = {};
if (remoteConfigFile !== undefined) {
const fileContents = readConfigFile(yield getRemoteConfig(remoteConfigFile));
remoteConfig = Object.assign(Object.assign({}, remoteConfig), fileContents);
const configFile = getOptionalInput('config-file');
if (configFile !== undefined) {
const externalConfig = yield readConfigFile(configFile);
// the reasoning behind reading the inline config when an external
// config file is provided is that we still want to allow users to
// pass inline options in the presence of an external config file.
// TO DO check order of precedence
return Object.assign(Object.assign({}, inlineConfig), externalConfig);
}
if (repoConfigFile !== undefined) {
const fileContents = readConfigFile(getRepoConfig(repoConfigFile));
repoConfig = Object.assign(Object.assign({}, repoConfig), fileContents);
}
// the reasoning behind reading the inline config when an external
// config file is provided is that we still want to allow users to
// pass inline options in the presence of an external config file.
// TO DO check order of precedence
return Object.assign(Object.assign(Object.assign({}, inlineConfig), remoteConfig), repoConfig);
return inlineConfig;
});
}
exports.readConfig = readConfig;
@@ -27517,7 +27510,33 @@ function readInlineConfig() {
};
}
exports.readInlineConfig = readInlineConfig;
function readConfigFile(configData) {
function readConfigFile(filePath) {
return __awaiter(this, void 0, void 0, function* () {
const format = new RegExp('(?<owner>[^/]+)/(?<repo>[^/]+)/(?<path>[^@]+)@(?<ref>.*)');
let data;
const pieces = format.exec(filePath);
try {
if ((pieces === null || pieces === void 0 ? void 0 : pieces.groups) && pieces.length === 5) {
data = yield getRemoteConfig({
owner: pieces.groups.owner,
repo: pieces.groups.repo,
path: pieces.groups.path,
ref: pieces.groups.ref
});
}
else {
data = fs.readFileSync(path_1.default.resolve(filePath), 'utf-8');
}
return parseConfigFile(data);
}
catch (error) {
core.debug(error);
throw new Error('Unable to fetch config file');
}
});
}
exports.readConfigFile = readConfigFile;
function parseConfigFile(configData) {
try {
const data = yaml_1.default.parse(configData);
for (const key of Object.keys(data)) {
@@ -27537,32 +27556,18 @@ function readConfigFile(configData) {
throw error;
}
}
exports.readConfigFile = readConfigFile;
function getRepoConfig(filePath) {
try {
return fs.readFileSync(path_1.default.resolve(filePath), 'utf-8');
}
catch (error) {
throw error;
}
}
exports.getRepoConfig = getRepoConfig;
function getRemoteConfig(configFile) {
exports.parseConfigFile = parseConfigFile;
function getRemoteConfig(configOpts) {
return __awaiter(this, void 0, void 0, function* () {
const format = new RegExp('(?<owner>[^/]+)/(?<repo>[^/]+)/(?<path>[^@]+)@(?<ref>.*)');
const pieces = format.exec(configFile);
if (pieces === null || pieces.groups === undefined || pieces.length < 5) {
throw new Error('Invalid remote-config-file value. Expected format: OWNER/REPOSITORY/FILENAME@BRANCH ');
}
try {
const { data } = yield (0, utils_1.octokitClient)('remote-config-repo-token', false).rest.repos.getContent({
mediaType: {
format: 'raw'
},
owner: pieces.groups.owner,
repo: pieces.groups.repo,
path: pieces.groups.path,
ref: pieces.groups.ref
owner: configOpts.owner,
repo: configOpts.repo,
path: configOpts.path,
ref: configOpts.ref
});
// When using mediaType.format = 'raw', the response.data is a string but this is not reflected
// in the return type of getContent. So we're casting the return value to a string.
Generated Vendored
+1 -1
View File
File diff suppressed because one or more lines are too long
+44 -42
View File
@@ -48,27 +48,18 @@ function validateLicenses(
}
export async function readConfig(): Promise<ConfigurationOptions> {
const remoteConfigFile = getOptionalInput('remote-config-file')
const repoConfigFile = getOptionalInput('config-file')
const inlineConfig = readInlineConfig()
let remoteConfig: ConfigurationOptions = {}
let repoConfig: ConfigurationOptions = {}
if (remoteConfigFile !== undefined) {
const fileContents = readConfigFile(await getRemoteConfig(remoteConfigFile))
remoteConfig = {...remoteConfig, ...fileContents}
const configFile = getOptionalInput('config-file')
if (configFile !== undefined) {
const externalConfig = await readConfigFile(configFile)
// the reasoning behind reading the inline config when an external
// config file is provided is that we still want to allow users to
// pass inline options in the presence of an external config file.
// TO DO check order of precedence
return {...inlineConfig, ...externalConfig}
}
if (repoConfigFile !== undefined) {
const fileContents = readConfigFile(getRepoConfig(repoConfigFile))
repoConfig = {...repoConfig, ...fileContents}
}
// the reasoning behind reading the inline config when an external
// config file is provided is that we still want to allow users to
// pass inline options in the presence of an external config file.
// TO DO check order of precedence
return {...inlineConfig, ...remoteConfig, ...repoConfig}
return inlineConfig
}
export function readInlineConfig(): ConfigurationOptions {
@@ -119,7 +110,34 @@ export function readInlineConfig(): ConfigurationOptions {
}
}
export function readConfigFile(configData: string): ConfigurationOptions {
export async function readConfigFile(
filePath: string
): Promise<ConfigurationOptions> {
const format = new RegExp(
'(?<owner>[^/]+)/(?<repo>[^/]+)/(?<path>[^@]+)@(?<ref>.*)'
)
let data: string
const pieces = format.exec(filePath)
try {
if (pieces?.groups && pieces.length === 5) {
data = await getRemoteConfig({
owner: pieces.groups.owner,
repo: pieces.groups.repo,
path: pieces.groups.path,
ref: pieces.groups.ref
})
} else {
data = fs.readFileSync(path.resolve(filePath), 'utf-8')
}
return parseConfigFile(data)
} catch (error) {
core.debug(error as string)
throw new Error('Unable to fetch config file')
}
}
export function parseConfigFile(configData: string): ConfigurationOptions {
try {
const data = YAML.parse(configData)
for (const key of Object.keys(data)) {
@@ -139,25 +157,9 @@ export function readConfigFile(configData: string): ConfigurationOptions {
}
}
export function getRepoConfig(filePath: string): string {
try {
return fs.readFileSync(path.resolve(filePath), 'utf-8')
} catch (error) {
throw error
}
}
async function getRemoteConfig(configFile: string): Promise<string> {
const format = new RegExp(
'(?<owner>[^/]+)/(?<repo>[^/]+)/(?<path>[^@]+)@(?<ref>.*)'
)
const pieces = format.exec(configFile)
if (pieces === null || pieces.groups === undefined || pieces.length < 5) {
throw new Error(
'Invalid remote-config-file value. Expected format: OWNER/REPOSITORY/FILENAME@BRANCH '
)
}
async function getRemoteConfig(configOpts: {
[key: string]: string
}): Promise<string> {
try {
const {data} = await octokitClient(
'remote-config-repo-token',
@@ -166,10 +168,10 @@ async function getRemoteConfig(configFile: string): Promise<string> {
mediaType: {
format: 'raw'
},
owner: pieces.groups.owner,
repo: pieces.groups.repo,
path: pieces.groups.path,
ref: pieces.groups.ref
owner: configOpts.owner,
repo: configOpts.repo,
path: configOpts.path,
ref: configOpts.ref
})
// When using mediaType.format = 'raw', the response.data is a string but this is not reflected