Fix inconsistencies due to zod defaults / partials mixup
This commit is contained in:
+25
-57
@@ -3,15 +3,10 @@ import path from 'path'
|
||||
import YAML from 'yaml'
|
||||
import * as core from '@actions/core'
|
||||
import * as z from 'zod'
|
||||
import {
|
||||
ConfigurationOptions,
|
||||
ConfigurationOptionsSchema,
|
||||
SeveritySchema,
|
||||
SCOPES
|
||||
} from './schemas'
|
||||
import {ConfigurationOptions, ConfigurationOptionsSchema} from './schemas'
|
||||
import {isSPDXValid, octokitClient} from './utils'
|
||||
|
||||
type licenseKey = 'allow-licenses' | 'deny-licenses'
|
||||
type ConfigurationOptionsPartial = Partial<ConfigurationOptions>
|
||||
|
||||
function getOptionalBoolean(name: string): boolean | undefined {
|
||||
const value = core.getInput(name)
|
||||
@@ -32,7 +27,7 @@ function parseList(list: string | undefined): string[] | undefined {
|
||||
}
|
||||
|
||||
function validateLicenses(
|
||||
key: licenseKey,
|
||||
key: 'allow-licenses' | 'deny-licenses',
|
||||
licenses: string[] | undefined
|
||||
): void {
|
||||
if (licenses === undefined) {
|
||||
@@ -53,49 +48,36 @@ export async function readConfig(): Promise<ConfigurationOptions> {
|
||||
const configFile = getOptionalInput('config-file')
|
||||
if (configFile !== undefined) {
|
||||
const externalConfig = await readConfigFile(configFile)
|
||||
console.log('externalConfig====', externalConfig)
|
||||
// TO DO check order of precedence
|
||||
return mergeConfigs(inlineConfig, externalConfig)
|
||||
return ConfigurationOptionsSchema.parse({
|
||||
...externalConfig,
|
||||
...inlineConfig
|
||||
})
|
||||
}
|
||||
return inlineConfig
|
||||
|
||||
return ConfigurationOptionsSchema.parse(inlineConfig)
|
||||
}
|
||||
|
||||
export function readInlineConfig(): ConfigurationOptions {
|
||||
const fail_on_severity = SeveritySchema.parse(
|
||||
getOptionalInput('fail-on-severity')
|
||||
)
|
||||
const fail_on_scopes = z
|
||||
.array(z.enum(SCOPES))
|
||||
.default(['runtime'])
|
||||
.parse(parseList(getOptionalInput('fail-on-scopes')))
|
||||
export function readInlineConfig(): ConfigurationOptionsPartial {
|
||||
const fail_on_severity = getOptionalInput('fail-on-severity')
|
||||
|
||||
const fail_on_scopes = parseList(getOptionalInput('fail-on-scopes'))
|
||||
|
||||
const allow_licenses = parseList(getOptionalInput('allow-licenses'))
|
||||
const deny_licenses = parseList(getOptionalInput('deny-licenses'))
|
||||
|
||||
if (allow_licenses !== undefined && deny_licenses !== undefined) {
|
||||
throw new Error("Can't specify both allow_licenses and deny_licenses")
|
||||
}
|
||||
validateLicenses('allow-licenses', allow_licenses)
|
||||
validateLicenses('deny-licenses', deny_licenses)
|
||||
|
||||
const allow_ghsas = parseList(getOptionalInput('allow-ghsas'))
|
||||
|
||||
const license_check = z
|
||||
.boolean()
|
||||
.default(true)
|
||||
.parse(getOptionalBoolean('license-check'))
|
||||
const vulnerability_check = z
|
||||
.boolean()
|
||||
.default(true)
|
||||
.parse(getOptionalBoolean('vulnerability-check'))
|
||||
if (license_check === false && vulnerability_check === false) {
|
||||
throw new Error("Can't disable both license-check and vulnerability-check")
|
||||
}
|
||||
const license_check = getOptionalBoolean('license-check')
|
||||
const vulnerability_check = getOptionalBoolean('vulnerability-check')
|
||||
|
||||
const base_ref = getOptionalInput('base-ref')
|
||||
const head_ref = getOptionalInput('head-ref')
|
||||
|
||||
return {
|
||||
const data = {
|
||||
fail_on_severity,
|
||||
fail_on_scopes,
|
||||
allow_licenses,
|
||||
@@ -106,11 +88,15 @@ export function readInlineConfig(): ConfigurationOptions {
|
||||
base_ref,
|
||||
head_ref
|
||||
}
|
||||
|
||||
return Object.fromEntries(
|
||||
Object.entries(data).filter(([_, value]) => value !== undefined)
|
||||
)
|
||||
}
|
||||
|
||||
export async function readConfigFile(
|
||||
filePath: string
|
||||
): Promise<ConfigurationOptions> {
|
||||
): Promise<ConfigurationOptionsPartial> {
|
||||
const format = new RegExp(
|
||||
'(?<owner>[^/]+)/(?<repo>[^/]+)/(?<path>[^@]+)@(?<ref>.*)'
|
||||
)
|
||||
@@ -135,7 +121,9 @@ export async function readConfigFile(
|
||||
}
|
||||
}
|
||||
|
||||
export function parseConfigFile(configData: string): ConfigurationOptions {
|
||||
export function parseConfigFile(
|
||||
configData: string
|
||||
): ConfigurationOptionsPartial {
|
||||
try {
|
||||
const data = YAML.parse(configData)
|
||||
for (const key of Object.keys(data)) {
|
||||
@@ -148,8 +136,7 @@ export function parseConfigFile(configData: string): ConfigurationOptions {
|
||||
delete data[key]
|
||||
}
|
||||
}
|
||||
const values = ConfigurationOptionsSchema.parse(data)
|
||||
return values
|
||||
return data
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
@@ -180,22 +167,3 @@ async function getRemoteConfig(configOpts: {
|
||||
throw new Error('Error fetching remote config file')
|
||||
}
|
||||
}
|
||||
|
||||
function mergeConfigs(
|
||||
...configs: ConfigurationOptions[]
|
||||
): ConfigurationOptions {
|
||||
return configs.reduce(
|
||||
(mergedConfig: ConfigurationOptions, config: ConfigurationOptions) => {
|
||||
for (const [key, value] of Object.entries(config)) {
|
||||
if (Array.isArray(value)) {
|
||||
const currentValue: string[] = mergedConfig[key] || []
|
||||
mergedConfig[key] = [...currentValue, ...value]
|
||||
} else {
|
||||
mergedConfig[key] = value
|
||||
}
|
||||
}
|
||||
return mergedConfig
|
||||
},
|
||||
{}
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ async function run(): Promise<void> {
|
||||
headRef: refs.head
|
||||
})
|
||||
|
||||
const minSeverity = config.fail_on_severity as Severity
|
||||
const minSeverity = config.fail_on_severity
|
||||
const scopedChanges = filterChangesByScopes(config.fail_on_scopes, changes)
|
||||
const filteredChanges = filterAllowedAdvisories(
|
||||
config.allow_ghsas,
|
||||
|
||||
+28
-10
@@ -38,20 +38,38 @@ export const ConfigurationOptionsSchema = z
|
||||
.object({
|
||||
fail_on_severity: SeveritySchema,
|
||||
fail_on_scopes: z.array(z.enum(SCOPES)).default(['runtime']),
|
||||
allow_licenses: z.array(z.string()).default([]),
|
||||
deny_licenses: z.array(z.string()).default([]),
|
||||
allow_licenses: z.array(z.string()).optional(),
|
||||
deny_licenses: z.array(z.string()).optional(),
|
||||
allow_ghsas: z.array(z.string()).default([]),
|
||||
license_check: z.boolean().default(true),
|
||||
vulnerability_check: z.boolean().default(true),
|
||||
config_file: z.string().optional().default('false'),
|
||||
base_ref: z.string(),
|
||||
head_ref: z.string()
|
||||
config_file: z.string().optional(),
|
||||
base_ref: z.string().optional(),
|
||||
head_ref: z.string().optional()
|
||||
})
|
||||
.superRefine((config, context) => {
|
||||
if (config.allow_licenses && config.deny_licenses) {
|
||||
context.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'You cannot specify both allow-licenses and deny-licenses'
|
||||
})
|
||||
}
|
||||
if (config.allow_licenses && config.allow_licenses.length < 1) {
|
||||
context.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'You should provide at least one license in allow-licenses'
|
||||
})
|
||||
}
|
||||
if (
|
||||
config.license_check === false &&
|
||||
config.vulnerability_check === false
|
||||
) {
|
||||
context.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Can't disable both license-check and vulnerability-check"
|
||||
})
|
||||
}
|
||||
})
|
||||
.partial()
|
||||
.refine(
|
||||
obj => !(obj.allow_licenses && obj.deny_licenses),
|
||||
'Your workflow file has both an allow_licenses list and deny_licenses list, but you can only set one or the other.'
|
||||
)
|
||||
|
||||
export const ChangesSchema = z.array(ChangeSchema)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user