From b64acc9ddfbc0a9ad7164c51bca84d51d54cf22d Mon Sep 17 00:00:00 2001 From: Jason Dent Date: Sun, 3 Mar 2024 13:42:07 +0100 Subject: [PATCH] Add option use_cspell_files --- README.md | 8 +- action-src/fixtures/pull_request_payload.json | 2 +- action-src/src/ActionParams.test.ts | 13 +-- action-src/src/ActionParams.ts | 54 ++++----- .../src/__snapshots__/action.test.ts.snap | 108 +++++++++--------- action-src/src/action.test.ts | 2 +- action-src/src/action.ts | 79 +------------ action-src/src/checkSpelling.ts | 80 ++++++++++++- action-src/src/checkSpellingForContext.ts | 11 -- action-src/src/error.test.ts | 17 +-- action-src/src/error.ts | 20 +++- action-src/src/getActionParams.ts | 1 + action-src/src/main.ts | 8 +- action-src/src/spell.test.ts | 18 ++- action-src/src/spell.ts | 7 +- action.yaml | 10 +- 16 files changed, 218 insertions(+), 220 deletions(-) delete mode 100644 action-src/src/checkSpellingForContext.ts diff --git a/README.md b/README.md index da6850f26..23adf6795 100644 --- a/README.md +++ b/README.md @@ -32,11 +32,6 @@ jobs: # files: | # **/*.{ts,js} # !dist/**/*.{ts,js} - # # Hidden directories need an explicit .* to be included - # .*/**/*.yml - # - # To not check hidden files, use: - # files: "**" # # Default: ALL files files: '' @@ -70,6 +65,9 @@ jobs: # Log progress and other information during the action execution. # Default: false verbose: false + + # Use the `files` setting found in the CSpell configuration instead of `input.files`. + use_cspell_files: false ``` ## Yarn 2 - PlugNPlay diff --git a/action-src/fixtures/pull_request_payload.json b/action-src/fixtures/pull_request_payload.json index 47313b83c..1bb578412 100644 --- a/action-src/fixtures/pull_request_payload.json +++ b/action-src/fixtures/pull_request_payload.json @@ -285,7 +285,7 @@ "watchers": 1, "watchers_count": 1 }, - "sha": "245401caa9cb567e577bd4af251789e7e87c726d", + "sha": "779c8bde2ff3f09f0c09633ca17a9dbfb5b07528", "user": { "avatar_url": "https://avatars0.githubusercontent.com/u/50543896?v=4", "events_url": "https://api.github.com/users/streetsidesoftware/events{/privacy}", diff --git a/action-src/src/ActionParams.test.ts b/action-src/src/ActionParams.test.ts index 5ad4b5a91..796c20f1f 100644 --- a/action-src/src/ActionParams.test.ts +++ b/action-src/src/ActionParams.test.ts @@ -15,22 +15,15 @@ describe('ActionParams', () => { ${{ incremental_files_only: 'sure' }} | ${'Invalid incremental_files_only setting, must be one of (true, false)'} ${{ config: 'config_not_found' }} | ${'Configuration file "config_not_found" not found.'} ${{ root: 'root_not_found' }} | ${'Root path does not exist: "root_not_found"'} - ${{ inline: 'swizzle' }} | ${'Invalid inline level (swizzle), must be one of (error, warning, none)'} + ${{ inline: 'swizzle' }} | ${'Invalid inline setting, must be one of (error, warning, none)'} ${{ strict: 'sure' }} | ${'Invalid strict setting, must be one of (true, false)'} + ${{ use_cspell_files: 'sure' }} | ${'Invalid use_cspell_files setting, must be one of (true, false)'} + ${{ check_dot_files: 'sure' }} | ${'Invalid check_dot_files setting, must be one of (true, false, explicit)'} `('validateActionParams Errors $params', ({ params, expected }) => { const logger = vi.fn(); expect(() => validateActionParams(ap(params), logger)).toThrow(); expect(logger).toHaveBeenCalledWith(expected); }); - - test.each` - params - ${{ github_token: 'token' }} - `('validateActionParams $params', ({ params }) => { - const logger = vi.fn(); - expect(() => validateActionParams(ap(params), logger)).not.toThrow(); - expect(logger).not.toHaveBeenCalled(); - }); }); function ap(p: Partial): ActionParamsInput { diff --git a/action-src/src/ActionParams.ts b/action-src/src/ActionParams.ts index 37ce67c0d..f8fd54dda 100644 --- a/action-src/src/ActionParams.ts +++ b/action-src/src/ActionParams.ts @@ -11,16 +11,22 @@ export type TrueFalse = 'true' | 'false'; export interface ActionParamsInput extends Record {} export interface ActionParams { + /** + * Files or glob patterns to check. + */ files: string; incremental_files_only: TrueFalse; config: string; root: string; + /** + * @default 'warning' + */ inline: InlineWorkflowCommand; /** * Determines if the action should be failed if any spelling issues are found. * * Allowed values are: true, false - * @default 'warning' + * @default 'false' */ strict: TrueFalse; /** @@ -38,6 +44,12 @@ export interface ActionParams { * @default 'explicit' */ check_dot_files: TrueFalse | 'explicit'; + + /** + * Use the `files` setting in the CSpell configuration to determine the files to check. + * @default 'false' + */ + use_cspell_files: TrueFalse; } const defaultActionParams: ActionParams = { @@ -49,6 +61,7 @@ const defaultActionParams: ActionParams = { strict: 'true', verbose: 'false', check_dot_files: 'explicit', + use_cspell_files: 'false', }; type ValidationFunction = (params: ActionParamsInput) => string | undefined; @@ -74,37 +87,18 @@ function validateRoot(params: ActionParamsInput) { return !success ? `Root path does not exist: "${root}"` : undefined; } -function validateInlineLevel(params: ActionParamsInput) { - const inline = params.inline; - const success = isInlineWorkflowCommand(inline); - return !success ? `Invalid inline level (${inline}), must be one of (error, warning, none)` : undefined; +function validateTrueFalse(key: keyof ActionParamsInput): ValidationFunction { + return validateOptions(key, ['true', 'false']); } -const validateStrict = validateTrueFalse('strict', 'Invalid strict setting, must be one of (true, false)'); -const validateIncrementalFilesOnly = validateTrueFalse( - 'incremental_files_only', - 'Invalid incremental_files_only setting, must be one of (true, false)', -); -const validateVerbose = validateTrueFalse('verbose', 'Invalid verbose setting, must be one of (true, false)'); - -function validateTrueFalse(key: keyof ActionParamsInput, msg: string): ValidationFunction { +function validateOptions(key: keyof ActionParamsInput, options: string[]): ValidationFunction { return (params: ActionParamsInput) => { const value = params[key]; - const success = value === 'true' || value === 'false'; - return !success ? msg : undefined; + const success = options.includes(value); + return !success ? `Invalid ${key} setting, must be one of (${options.join(', ')})` : undefined; }; } -const inlineWorkflowCommandSet: Record = { - error: true, - warning: true, - none: true, -}; - -function isInlineWorkflowCommand(cmd: InlineWorkflowCommand | string): cmd is InlineWorkflowCommand { - return !!inlineWorkflowCommandSet[cmd]; -} - export function validateActionParams( params: ActionParamsInput | ActionParams, logError: (msg: string) => void, @@ -112,10 +106,12 @@ export function validateActionParams( const validations: ValidationFunction[] = [ validateConfig, validateRoot, - validateInlineLevel, - validateStrict, - validateIncrementalFilesOnly, - validateVerbose, + validateOptions('inline', ['error', 'warning', 'none']), + validateTrueFalse('strict'), + validateTrueFalse('incremental_files_only'), + validateTrueFalse('verbose'), + validateTrueFalse('use_cspell_files'), + validateOptions('check_dot_files', ['true', 'false', 'explicit']), ]; const success = validations .map((fn) => fn(params)) diff --git a/action-src/src/__snapshots__/action.test.ts.snap b/action-src/src/__snapshots__/action.test.ts.snap index b60a34090..9a23fc319 100644 --- a/action-src/src/__snapshots__/action.test.ts.snap +++ b/action-src/src/__snapshots__/action.test.ts.snap @@ -13,8 +13,8 @@ exports[`Validate Action > check all '**/*.md' 1`] = `[]`; exports[`Validate Action > check files "''" incremental: false 'pull_request_with_files.json', dot: "''" 1`] = ` [ - "../fixtures/sampleCode/samples_with_errors/withErrors.ts:5:19 Unknown word (Functon)", - "../fixtures/sampleCode/samples_with_errors/withErrors.ts:5:27 Unknown word (countt)", + "fixtures/sampleCode/samples_with_errors/withErrors.ts:5:19 Unknown word (Functon)", + "fixtures/sampleCode/samples_with_errors/withErrors.ts:5:27 Unknown word (countt)", ] `; @@ -27,11 +27,11 @@ exports[`Validate Action > check files "''" incremental: false 'pull_request_wit ", ], [ - "::warning file=../fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=19::Unknown word (Functon) + "::warning file=fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=19::Unknown word (Functon) ", ], [ - "::warning file=../fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=27::Unknown word (countt) + "::warning file=fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=27::Unknown word (countt) ", ], [ @@ -75,7 +75,7 @@ exports[`Validate Action > check files "''" incremental: false 'pull_request_wit ", ], [ - "::set-output name=files_with_issues::["../fixtures/sampleCode/samples_with_errors/withErrors.ts"] + "::set-output name=files_with_issues::["fixtures/sampleCode/samples_with_errors/withErrors.ts"] ", ], [ @@ -83,7 +83,7 @@ exports[`Validate Action > check files "''" incremental: false 'pull_request_wit ", ], [ - "::set-output name=result::{"success":false,"number_of_issues":2,"number_of_files_checked":4,"files_with_issues":["../fixtures/sampleCode/samples_with_errors/withErrors.ts"]} + "::set-output name=result::{"success":false,"number_of_issues":2,"number_of_files_checked":4,"files_with_issues":["fixtures/sampleCode/samples_with_errors/withErrors.ts"]} ", ], ] @@ -92,22 +92,22 @@ exports[`Validate Action > check files "''" incremental: false 'pull_request_wit exports[`Validate Action > check files "''" incremental: false 'pull_request_with_files.json', dot: "''" 4`] = ` [ "Pull Request", - "::warning file=../fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=19::Unknown word (Functon)", - "::warning file=../fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=27::Unknown word (countt)", + "::warning file=fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=19::Unknown word (Functon)", + "::warning file=fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=27::Unknown word (countt)", "Files checked: 4, Issues found: 2 in 1 files.", "::set-output name=success::false", "::set-output name=number_of_files_checked::4", "::set-output name=number_of_issues::2", "::set-output name=number_of_files_with_issues::1", - "::set-output name=files_with_issues::["../fixtures/sampleCode/samples_with_errors/withErrors.ts"]", - "::set-output name=result::{"success":false,"number_of_issues":2,"number_of_files_checked":4,"files_with_issues":["../fixtures/sampleCode/samples_with_errors/withErrors.ts"]}", + "::set-output name=files_with_issues::["fixtures/sampleCode/samples_with_errors/withErrors.ts"]", + "::set-output name=result::{"success":false,"number_of_issues":2,"number_of_files_checked":4,"files_with_issues":["fixtures/sampleCode/samples_with_errors/withErrors.ts"]}", ] `; exports[`Validate Action > check files "''" incremental: false 'pull_request_with_files.json', dot: "'explicit'" 1`] = ` [ - "../fixtures/sampleCode/samples_with_errors/withErrors.ts:5:19 Unknown word (Functon)", - "../fixtures/sampleCode/samples_with_errors/withErrors.ts:5:27 Unknown word (countt)", + "fixtures/sampleCode/samples_with_errors/withErrors.ts:5:19 Unknown word (Functon)", + "fixtures/sampleCode/samples_with_errors/withErrors.ts:5:27 Unknown word (countt)", ] `; @@ -120,11 +120,11 @@ exports[`Validate Action > check files "''" incremental: false 'pull_request_wit ", ], [ - "::warning file=../fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=19::Unknown word (Functon) + "::warning file=fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=19::Unknown word (Functon) ", ], [ - "::warning file=../fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=27::Unknown word (countt) + "::warning file=fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=27::Unknown word (countt) ", ], [ @@ -168,7 +168,7 @@ exports[`Validate Action > check files "''" incremental: false 'pull_request_wit ", ], [ - "::set-output name=files_with_issues::["../fixtures/sampleCode/samples_with_errors/withErrors.ts"] + "::set-output name=files_with_issues::["fixtures/sampleCode/samples_with_errors/withErrors.ts"] ", ], [ @@ -176,7 +176,7 @@ exports[`Validate Action > check files "''" incremental: false 'pull_request_wit ", ], [ - "::set-output name=result::{"success":false,"number_of_issues":2,"number_of_files_checked":4,"files_with_issues":["../fixtures/sampleCode/samples_with_errors/withErrors.ts"]} + "::set-output name=result::{"success":false,"number_of_issues":2,"number_of_files_checked":4,"files_with_issues":["fixtures/sampleCode/samples_with_errors/withErrors.ts"]} ", ], ] @@ -185,22 +185,22 @@ exports[`Validate Action > check files "''" incremental: false 'pull_request_wit exports[`Validate Action > check files "''" incremental: false 'pull_request_with_files.json', dot: "'explicit'" 4`] = ` [ "Pull Request", - "::warning file=../fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=19::Unknown word (Functon)", - "::warning file=../fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=27::Unknown word (countt)", + "::warning file=fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=19::Unknown word (Functon)", + "::warning file=fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=27::Unknown word (countt)", "Files checked: 4, Issues found: 2 in 1 files.", "::set-output name=success::false", "::set-output name=number_of_files_checked::4", "::set-output name=number_of_issues::2", "::set-output name=number_of_files_with_issues::1", - "::set-output name=files_with_issues::["../fixtures/sampleCode/samples_with_errors/withErrors.ts"]", - "::set-output name=result::{"success":false,"number_of_issues":2,"number_of_files_checked":4,"files_with_issues":["../fixtures/sampleCode/samples_with_errors/withErrors.ts"]}", + "::set-output name=files_with_issues::["fixtures/sampleCode/samples_with_errors/withErrors.ts"]", + "::set-output name=result::{"success":false,"number_of_issues":2,"number_of_files_checked":4,"files_with_issues":["fixtures/sampleCode/samples_with_errors/withErrors.ts"]}", ] `; exports[`Validate Action > check files "''" incremental: false 'pull_request_with_files.json', dot: "'true'" 1`] = ` [ - "../fixtures/sampleCode/samples_with_errors/withErrors.ts:5:19 Unknown word (Functon)", - "../fixtures/sampleCode/samples_with_errors/withErrors.ts:5:27 Unknown word (countt)", + "fixtures/sampleCode/samples_with_errors/withErrors.ts:5:19 Unknown word (Functon)", + "fixtures/sampleCode/samples_with_errors/withErrors.ts:5:27 Unknown word (countt)", ] `; @@ -213,15 +213,15 @@ exports[`Validate Action > check files "''" incremental: false 'pull_request_wit ", ], [ - "::warning file=../fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=19::Unknown word (Functon) + "::warning file=fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=19::Unknown word (Functon) ", ], [ - "::warning file=../fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=27::Unknown word (countt) + "::warning file=fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=27::Unknown word (countt) ", ], [ - "Files checked: 6, Issues found: 2 in 1 files. + "Files checked: 4, Issues found: 2 in 1 files. ", ], [ @@ -237,7 +237,7 @@ exports[`Validate Action > check files "''" incremental: false 'pull_request_wit ", ], [ - "::set-output name=number_of_files_checked::6 + "::set-output name=number_of_files_checked::4 ", ], [ @@ -261,7 +261,7 @@ exports[`Validate Action > check files "''" incremental: false 'pull_request_wit ", ], [ - "::set-output name=files_with_issues::["../fixtures/sampleCode/samples_with_errors/withErrors.ts"] + "::set-output name=files_with_issues::["fixtures/sampleCode/samples_with_errors/withErrors.ts"] ", ], [ @@ -269,7 +269,7 @@ exports[`Validate Action > check files "''" incremental: false 'pull_request_wit ", ], [ - "::set-output name=result::{"success":false,"number_of_issues":2,"number_of_files_checked":6,"files_with_issues":["../fixtures/sampleCode/samples_with_errors/withErrors.ts"]} + "::set-output name=result::{"success":false,"number_of_issues":2,"number_of_files_checked":4,"files_with_issues":["fixtures/sampleCode/samples_with_errors/withErrors.ts"]} ", ], ] @@ -278,22 +278,22 @@ exports[`Validate Action > check files "''" incremental: false 'pull_request_wit exports[`Validate Action > check files "''" incremental: false 'pull_request_with_files.json', dot: "'true'" 4`] = ` [ "Pull Request", - "::warning file=../fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=19::Unknown word (Functon)", - "::warning file=../fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=27::Unknown word (countt)", - "Files checked: 6, Issues found: 2 in 1 files.", + "::warning file=fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=19::Unknown word (Functon)", + "::warning file=fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=27::Unknown word (countt)", + "Files checked: 4, Issues found: 2 in 1 files.", "::set-output name=success::false", - "::set-output name=number_of_files_checked::6", + "::set-output name=number_of_files_checked::4", "::set-output name=number_of_issues::2", "::set-output name=number_of_files_with_issues::1", - "::set-output name=files_with_issues::["../fixtures/sampleCode/samples_with_errors/withErrors.ts"]", - "::set-output name=result::{"success":false,"number_of_issues":2,"number_of_files_checked":6,"files_with_issues":["../fixtures/sampleCode/samples_with_errors/withErrors.ts"]}", + "::set-output name=files_with_issues::["fixtures/sampleCode/samples_with_errors/withErrors.ts"]", + "::set-output name=result::{"success":false,"number_of_issues":2,"number_of_files_checked":4,"files_with_issues":["fixtures/sampleCode/samples_with_errors/withErrors.ts"]}", ] `; exports[`Validate Action > check files "''" incremental: true 'bad_params/bad_unsupported_event.json', dot: "'explicit'" 1`] = ` [ - "../fixtures/sampleCode/samples_with_errors/withErrors.ts:5:19 Unknown word (Functon)", - "../fixtures/sampleCode/samples_with_errors/withErrors.ts:5:27 Unknown word (countt)", + "fixtures/sampleCode/samples_with_errors/withErrors.ts:5:19 Unknown word (Functon)", + "fixtures/sampleCode/samples_with_errors/withErrors.ts:5:27 Unknown word (countt)", ] `; @@ -310,11 +310,11 @@ exports[`Validate Action > check files "''" incremental: true 'bad_params/bad_un ", ], [ - "::warning file=../fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=19::Unknown word (Functon) + "::warning file=fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=19::Unknown word (Functon) ", ], [ - "::warning file=../fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=27::Unknown word (countt) + "::warning file=fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=27::Unknown word (countt) ", ], [ @@ -358,7 +358,7 @@ exports[`Validate Action > check files "''" incremental: true 'bad_params/bad_un ", ], [ - "::set-output name=files_with_issues::["../fixtures/sampleCode/samples_with_errors/withErrors.ts"] + "::set-output name=files_with_issues::["fixtures/sampleCode/samples_with_errors/withErrors.ts"] ", ], [ @@ -366,7 +366,7 @@ exports[`Validate Action > check files "''" incremental: true 'bad_params/bad_un ", ], [ - "::set-output name=result::{"success":false,"number_of_issues":2,"number_of_files_checked":4,"files_with_issues":["../fixtures/sampleCode/samples_with_errors/withErrors.ts"]} + "::set-output name=result::{"success":false,"number_of_issues":2,"number_of_files_checked":4,"files_with_issues":["fixtures/sampleCode/samples_with_errors/withErrors.ts"]} ", ], [ @@ -380,23 +380,23 @@ exports[`Validate Action > check files "''" incremental: true 'bad_params/bad_un [ "::warning::Unable to determine which files have changed, checking files: **", "'fork'", - "::warning file=../fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=19::Unknown word (Functon)", - "::warning file=../fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=27::Unknown word (countt)", + "::warning file=fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=19::Unknown word (Functon)", + "::warning file=fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=27::Unknown word (countt)", "Files checked: 4, Issues found: 2 in 1 files.", "::set-output name=success::false", "::set-output name=number_of_files_checked::4", "::set-output name=number_of_issues::2", "::set-output name=number_of_files_with_issues::1", - "::set-output name=files_with_issues::["../fixtures/sampleCode/samples_with_errors/withErrors.ts"]", - "::set-output name=result::{"success":false,"number_of_issues":2,"number_of_files_checked":4,"files_with_issues":["../fixtures/sampleCode/samples_with_errors/withErrors.ts"]}", + "::set-output name=files_with_issues::["fixtures/sampleCode/samples_with_errors/withErrors.ts"]", + "::set-output name=result::{"success":false,"number_of_issues":2,"number_of_files_checked":4,"files_with_issues":["fixtures/sampleCode/samples_with_errors/withErrors.ts"]}", "::error::2 spelling issues found in 1 of the 4 files checked.", ] `; exports[`Validate Action > check files "'**'" incremental: true 'bad_params/bad_unsupported_event.json', dot: "'explicit'" 1`] = ` [ - "../fixtures/sampleCode/samples_with_errors/withErrors.ts:5:19 Unknown word (Functon)", - "../fixtures/sampleCode/samples_with_errors/withErrors.ts:5:27 Unknown word (countt)", + "fixtures/sampleCode/samples_with_errors/withErrors.ts:5:19 Unknown word (Functon)", + "fixtures/sampleCode/samples_with_errors/withErrors.ts:5:27 Unknown word (countt)", ] `; @@ -413,11 +413,11 @@ exports[`Validate Action > check files "'**'" incremental: true 'bad_params/bad_ ", ], [ - "::warning file=../fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=19::Unknown word (Functon) + "::warning file=fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=19::Unknown word (Functon) ", ], [ - "::warning file=../fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=27::Unknown word (countt) + "::warning file=fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=27::Unknown word (countt) ", ], [ @@ -461,7 +461,7 @@ exports[`Validate Action > check files "'**'" incremental: true 'bad_params/bad_ ", ], [ - "::set-output name=files_with_issues::["../fixtures/sampleCode/samples_with_errors/withErrors.ts"] + "::set-output name=files_with_issues::["fixtures/sampleCode/samples_with_errors/withErrors.ts"] ", ], [ @@ -469,7 +469,7 @@ exports[`Validate Action > check files "'**'" incremental: true 'bad_params/bad_ ", ], [ - "::set-output name=result::{"success":false,"number_of_issues":2,"number_of_files_checked":4,"files_with_issues":["../fixtures/sampleCode/samples_with_errors/withErrors.ts"]} + "::set-output name=result::{"success":false,"number_of_issues":2,"number_of_files_checked":4,"files_with_issues":["fixtures/sampleCode/samples_with_errors/withErrors.ts"]} ", ], [ @@ -483,15 +483,15 @@ exports[`Validate Action > check files "'**'" incremental: true 'bad_params/bad_ [ "::warning::Unable to determine which files have changed, checking files: **", "'fork'", - "::warning file=../fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=19::Unknown word (Functon)", - "::warning file=../fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=27::Unknown word (countt)", + "::warning file=fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=19::Unknown word (Functon)", + "::warning file=fixtures/sampleCode/samples_with_errors/withErrors.ts,line=5,col=27::Unknown word (countt)", "Files checked: 4, Issues found: 2 in 1 files.", "::set-output name=success::false", "::set-output name=number_of_files_checked::4", "::set-output name=number_of_issues::2", "::set-output name=number_of_files_with_issues::1", - "::set-output name=files_with_issues::["../fixtures/sampleCode/samples_with_errors/withErrors.ts"]", - "::set-output name=result::{"success":false,"number_of_issues":2,"number_of_files_checked":4,"files_with_issues":["../fixtures/sampleCode/samples_with_errors/withErrors.ts"]}", + "::set-output name=files_with_issues::["fixtures/sampleCode/samples_with_errors/withErrors.ts"]", + "::set-output name=result::{"success":false,"number_of_issues":2,"number_of_files_checked":4,"files_with_issues":["fixtures/sampleCode/samples_with_errors/withErrors.ts"]}", "::error::2 spelling issues found in 1 of the 4 files checked.", ] `; diff --git a/action-src/src/action.test.ts b/action-src/src/action.test.ts index 1a8bfe283..1ed9fdc77 100644 --- a/action-src/src/action.test.ts +++ b/action-src/src/action.test.ts @@ -54,7 +54,7 @@ describe('Validate Action', () => { await expect(action(context)).resolves.toBe(expected); }); - test.only.each` + test.each` testName | file | expected ${'event pr 1594'} | ${'pr_1594_env.json'} | ${true} `('$testName', async ({ file, expected }) => { diff --git a/action-src/src/action.ts b/action-src/src/action.ts index 1a2e5237b..0c37cf6cf 100644 --- a/action-src/src/action.ts +++ b/action-src/src/action.ts @@ -1,26 +1,16 @@ -import path from 'node:path'; -import { debug, info, error, warning, setFailed, setOutput } from '@actions/core'; +import { debug, error, info, setFailed, setOutput, warning } from '@actions/core'; import type { Context as GitHubContext } from '@actions/github/lib/context.js'; import type { RunResult } from 'cspell'; -import * as glob from 'cspell-glob'; +import path from 'node:path'; import { validateActionParams } from './ActionParams.js'; -import { getActionParams } from './getActionParams.js'; -import { gitListFilesForPullRequest, gitListFilesForPush, gitRoot } from './git.js'; -import type { PushEvent, PullRequestEvent } from '@octokit/webhooks-types'; import { checkDotMap } from './checkDotMap.js'; -import { checkSpellingForContext } from './checkSpellingForContext.js'; +import { checkSpellingForContext, type Context } from './checkSpelling.js'; +import { getActionParams } from './getActionParams.js'; const core = { debug, error, info, warning }; const defaultGlob = '**'; -export interface Context { - githubContext: GitHubContext; - files: string; - useEventFiles: boolean; - dot: boolean; -} - type EventNames = 'push' | 'pull_request'; const supportedIncrementalEvents = new Set(['push', 'pull_request']); @@ -39,64 +29,6 @@ function isSupportedEvent(eventName: EventNames | string): eventName is EventNam return supportedIncrementalEvents.has(eventName); } -export async function gatherGitCommitFilesFromContext(context: Context): Promise { - if (context.useEventFiles) { - const eventFiles = await gatherFiles(context); - if (!eventFiles) return undefined; - const files = filterFiles(context.files, eventFiles, context.dot); - const root = await gitRoot(); - return [...files].map((f) => path.resolve(root, f)); - } -} - -export async function gatherFileGlobsFromContext(context: Context): Promise> { - const files = new Set( - context.files - .split('\n') - .map((a) => a.trim()) - .filter((a) => !!a), - ); - return files; -} - -/** - * Gather the set of files to be spell checked. - * @param context Context - */ -async function gatherFiles(context: Context): Promise | undefined> { - const eventName = context.githubContext.eventName; - - // console.warn('gatherFiles %o', { context: context.githubContext, eventName }); - - try { - switch (eventName) { - case 'push': - return new Set(await gitListFilesForPush(context.githubContext.payload as PushEvent)); - case 'pull_request': - return new Set(await gitListFilesForPullRequest(context.githubContext.payload as PullRequestEvent)); - } - } catch (e) { - core.warning('Unable to determine which files have changed, checking files: ' + defaultGlob); - } - - return undefined; -} - -function filterFiles(globPattern: string, files: Set, dot: boolean): Set { - if (!globPattern) return files; - - const matchingFiles = new Set(); - - const g = new glob.GlobMatcher(globPattern, { mode: 'include', dot }); - for (const p of files) { - if (g.match(p)) { - matchingFiles.add(p); - } - } - - return matchingFiles; -} - /** * Run the action based upon the githubContext. * @param githubContext @@ -116,8 +48,9 @@ export async function action(githubContext: GitHubContext): Promise { const dot = !!checkDotMap[params.check_dot_files]; const context: Context = { githubContext, - files: params.files, + globs: params.files, useEventFiles: params.incremental_files_only === 'true', + useCSpellFiles: params.use_cspell_files === 'true', dot, }; diff --git a/action-src/src/checkSpelling.ts b/action-src/src/checkSpelling.ts index a8ce4c31b..d9c0d6a4d 100644 --- a/action-src/src/checkSpelling.ts +++ b/action-src/src/checkSpelling.ts @@ -1,15 +1,83 @@ -import { debug, info, error, warning } from '@actions/core'; +import { debug, error, info, warning } from '@actions/core'; +import type { Context as GitHubContext } from '@actions/github/lib/context.js'; +import type { PullRequestEvent, PushEvent } from '@octokit/webhooks-types'; import type { RunResult } from 'cspell'; +import path from 'node:path'; import { ActionParams } from './ActionParams.js'; -import { CSpellReporterForGithubAction } from './reporter.js'; -import { lint, LintOptions } from './spell.js'; import { checkDotMap } from './checkDotMap.js'; +import { toError } from './error.js'; +import { gitListFiles, gitListFilesForPullRequest, gitListFilesForPush, gitRoot } from './git.js'; +import { CSpellReporterForGithubAction } from './reporter.js'; +import { LintOptions, lint } from './spell.js'; const core = { debug, error, info, warning }; -export async function checkSpelling( +export async function checkSpellingForContext(params: ActionParams, context: Context): Promise { + const files = await gatherGitCommitFilesFromContext(context); + const globs = await gatherFileGlobsFromContext(context); + const result = await checkSpelling(params, globs, files); + return result; +} + +export interface Context { + githubContext: GitHubContext; + globs: string; + useEventFiles: boolean; + useCSpellFiles: boolean; + dot: boolean; +} + +async function gatherGitCommitFilesFromContext(context: Context): Promise { + if (context.useEventFiles) { + const eventFiles = await gatherFiles(context); + if (!eventFiles) return undefined; + const root = await gitRoot(); + return [...eventFiles].map((f) => path.resolve(root, f)); + } +} + +async function gatherFileGlobsFromContext(context: Context): Promise { + if (context.useCSpellFiles) { + return undefined; + } + const files = new Set( + context.globs + .split('\n') + .map((a) => a.trim()) + .filter((a) => !!a), + ); + return [...files]; +} + +/** + * Gather the set of files to be spell checked. + * @param context Context + */ +async function gatherFiles(context: Context): Promise | undefined> { + const eventName = context.githubContext.eventName; + + // console.warn('gatherFiles %o', { context: context.githubContext, eventName }); + + try { + switch (eventName) { + case 'push': + return new Set(await gitListFilesForPush(context.githubContext.payload as PushEvent)); + case 'pull_request': + return new Set(await gitListFilesForPullRequest(context.githubContext.payload as PullRequestEvent)); + default: + core.warning(`Unsupported event: ${eventName}. Using files from latest commit.`); + return new Set(await gitListFiles('HEAD')); + } + } catch (e) { + core.error(toError(e)); + } + + return undefined; +} + +async function checkSpelling( params: ActionParams, - globs: string[], + globs: string[] | undefined, files: string[] | undefined, ): Promise { const options: LintOptions = { @@ -24,7 +92,7 @@ export async function checkSpelling( }; const collector = new CSpellReporterForGithubAction(params.inline, reporterOptions, core); - await lint(globs, options, collector.reporter); + await lint(globs || [], options, collector.reporter); return collector.result; } diff --git a/action-src/src/checkSpellingForContext.ts b/action-src/src/checkSpellingForContext.ts deleted file mode 100644 index 0d51fcda2..000000000 --- a/action-src/src/checkSpellingForContext.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { RunResult } from 'cspell'; -import { ActionParams } from './ActionParams.js'; -import { checkSpelling } from './checkSpelling.js'; -import { Context, gatherGitCommitFilesFromContext, gatherFileGlobsFromContext } from './action.js'; - -export async function checkSpellingForContext(params: ActionParams, context: Context): Promise { - const fileList = await gatherGitCommitFilesFromContext(context); - const files = await gatherFileGlobsFromContext(context); - const result = await checkSpelling(params, fileList ? [] : [...files], fileList); - return result; -} diff --git a/action-src/src/error.test.ts b/action-src/src/error.test.ts index 69d30c8dc..6521cf902 100644 --- a/action-src/src/error.test.ts +++ b/action-src/src/error.test.ts @@ -1,15 +1,16 @@ import { describe, expect, test } from 'vitest'; -import { AppError, isError } from './error.js'; +import { AppError, isError, toError } from './error.js'; describe('error', () => { test.each` - value | expected - ${Error('hello')} | ${true} - ${new AppError()} | ${true} - ${'hello'} | ${false} - ${{}} | ${false} - ${null} | ${false} - `('isError', ({ value, expected }) => { + value | expected + ${Error('hello')} | ${true} + ${new AppError('app error')} | ${true} + ${toError('hello')} | ${true} + ${'hello'} | ${false} + ${{}} | ${false} + ${null} | ${false} + `('isError $value', ({ value, expected }) => { expect(isError(value)).toBe(expected); }); }); diff --git a/action-src/src/error.ts b/action-src/src/error.ts index 72b6d481b..475323f6a 100644 --- a/action-src/src/error.ts +++ b/action-src/src/error.ts @@ -7,14 +7,22 @@ export class AppError extends Error { export function isError(e: unknown): e is Error { if (!e) return false; if (typeof e !== 'object') return false; - const err = e; - return ( - err.message !== undefined && - err.name !== undefined && - (err.stack === undefined || typeof err.stack === 'string') - ); + return e instanceof Error; } export function isAppError(e: unknown): e is AppError { return e instanceof AppError; } + +/** + * Convert an unknown value to an error + * @param e - the unknown error + * @returns Error + */ +export function toError(e: unknown): Error { + if (e instanceof Error) return e; + if (typeof e === 'string') return new Error(e); + const err = new Error('Unknown error'); + err.cause = e; + return err; +} diff --git a/action-src/src/getActionParams.ts b/action-src/src/getActionParams.ts index 2962331b3..29753fdbc 100644 --- a/action-src/src/getActionParams.ts +++ b/action-src/src/getActionParams.ts @@ -12,6 +12,7 @@ export function getActionParams(): ActionParamsInput { strict: tf(getInput('strict')), verbose: tf(getInput('verbose')), check_dot_files: tf(getInput('check_dot_files')), + use_cspell_files: tf(getInput('use_cspell_files')), }); } diff --git a/action-src/src/main.ts b/action-src/src/main.ts index 62552d9ca..f523a937d 100644 --- a/action-src/src/main.ts +++ b/action-src/src/main.ts @@ -1,8 +1,7 @@ import { info, setFailed } from '@actions/core'; import { Context } from '@actions/github/lib/context.js'; -import { isAppError, isError } from './error.js'; +import { toError } from './error.js'; import { action } from './action.js'; -import { format } from 'util'; export async function run(): Promise { try { @@ -14,7 +13,8 @@ export async function run(): Promise { return undefined; } catch (error) { console.error(error); - setFailed(isAppError(error) ? error.message : isError(error) ? error : format(error)); - return isError(error) ? error : Error(format(error)); + const err = toError(error); + setFailed(err.message); + return err; } } diff --git a/action-src/src/spell.test.ts b/action-src/src/spell.test.ts index a4d594fd0..dd88e1199 100644 --- a/action-src/src/spell.test.ts +++ b/action-src/src/spell.test.ts @@ -1,7 +1,8 @@ -import * as spell from './spell.js'; -import { root, sourceDir, resolveFiles, resolveFile } from './test/helper.js'; -import { CSpellReporterForGithubAction, Logger } from './reporter.js'; +import path from 'node:path'; import { describe, expect, test, vi } from 'vitest'; +import { CSpellReporterForGithubAction, Logger } from './reporter.js'; +import * as spell from './spell.js'; +import { resolveFile, resolveFiles, root, sourceDir } from './test/helper.js'; const sc = expect.stringContaining; @@ -91,13 +92,20 @@ describe('Validate Spell Checking', () => { const sampleConfig = resolveFile('fixtures/cspell.json', sourceDir); const sampleConfigTs = resolveFile('fixtures/sampleCode/ts/cspell.config.yaml', sourceDir); - test.only.each` + const sampleCodeTsOptions = { + root: path.join(sourceDir, 'fixtures/sampleCode/ts'), + }; + + test.each` globs | files | options | expected ${[]} | ${['fixtures/sampleCode/ts/sample.ts']} | ${{}} | ${{ files: 1 }} + ${['**/*.ts']} | ${['fixtures/sampleCode/ts/sample.ts']} | ${{}} | ${{ files: 1 }} ${[]} | ${['fixtures/sampleCode/ts/missing.ts']} | ${{}} | ${{ files: 0 }} + ${[]} | ${[]} | ${{}} | ${{ files: 0 }} + ${[]} | ${undefined} | ${sampleCodeTsOptions} | ${{ files: 1 }} ${[]} | ${['fixtures/sampleCode/ts/cspell.config.yaml']} | ${{ config: sampleConfig }} | ${{ files: 1 }} ${[]} | ${['fixtures/sampleCode/ts/cspell.config.yaml']} | ${{ config: sampleConfigTs }} | ${{ files: 0 }} - ${['**/*.ts']} | ${['fixtures/sampleCode/ts/cspell.config.yaml']} | ${{ config: sampleConfig }} | ${{ files: 1 }} + ${['**/*.ts']} | ${['fixtures/sampleCode/ts/cspell.config.yaml']} | ${{ config: sampleConfig }} | ${{ files: 0 }} ${['**/ts/missing.ts']} | ${undefined} | ${{}} | ${{ files: 0 }} `('Linting $globs $files $options', async ({ globs, files, options, expected }) => { const opts: spell.LintOptions = { diff --git a/action-src/src/spell.ts b/action-src/src/spell.ts index 41474051d..090dcbc7a 100644 --- a/action-src/src/spell.ts +++ b/action-src/src/spell.ts @@ -1,6 +1,5 @@ import { type CSpellApplicationOptions, lint as cspellAppLint } from 'cspell'; import type { CSpellReporter } from 'cspell'; -import assert from 'node:assert'; export interface LintOptions { root: string; @@ -23,10 +22,6 @@ export interface LintOptions { */ export async function lint(globs: string[], lintOptions: LintOptions, reporter: CSpellReporter): Promise { const { root, config, checkDotFiles, files } = lintOptions; - assert( - (globs.length && !files) || (files && !globs.length), - 'Either globs or files must be specified, but not both.', - ); // It is expected that `files` in the configuration will be used to filter the files. const mustFindFiles = !files; const options: CSpellApplicationOptions = { @@ -41,6 +36,6 @@ export async function lint(globs: string[], lintOptions: LintOptions, reporter: } else if (checkDotFiles === false) { options.dot = false; } - console.warn('lint: %o', { globs, lintOptions, options }); + // console.warn('lint: %o', { globs, lintOptions, options }); await cspellAppLint(globs, options, reporter); } diff --git a/action.yaml b/action.yaml index 5fa943760..d5c4d7f55 100644 --- a/action.yaml +++ b/action.yaml @@ -15,7 +15,8 @@ inputs: required: false config: description: > - Path to `cspell.json` + Path to CSpell configuration file, i.e. `cspell.json` or `cspell.config.yaml`. + If not provided, the spell checker will search for the nearest configuration file. required: false root: description: > @@ -47,6 +48,13 @@ inputs: - "explicit" - glob patterns can match explicit `.dot` patterns. default: "explicit" required: false + use_cspell_files: + description: | + Use the `files` setting from the CSpell configuration file. + - "true" - Overrides the `input.files` setting. + - "false" - Use the `input.files` setting. + default: "false" + required: false outputs: success: