From f9a10c21df6f4070d5a3f6a095e59e6c270df282 Mon Sep 17 00:00:00 2001 From: "k.panithi" Date: Mon, 30 Oct 2023 10:40:53 +0700 Subject: [PATCH 1/4] feature: allow multiple documents --- __tests__/fixtures/yaml/multiple/invalid.yaml | 16 ++++++ __tests__/fixtures/yaml/multiple/yaml1.yaml | 16 ++++++ __tests__/functions/yaml-validator.test.js | 29 +++++++++++ action.yml | 4 ++ src/functions/yaml-validator.js | 50 +++++++++++++------ 5 files changed, 100 insertions(+), 15 deletions(-) create mode 100644 __tests__/fixtures/yaml/multiple/invalid.yaml create mode 100644 __tests__/fixtures/yaml/multiple/yaml1.yaml diff --git a/__tests__/fixtures/yaml/multiple/invalid.yaml b/__tests__/fixtures/yaml/multiple/invalid.yaml new file mode 100644 index 0000000..70ee448 --- /dev/null +++ b/__tests__/fixtures/yaml/multiple/invalid.yaml @@ -0,0 +1,16 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment + labels: + app: nginx +# spec: +--- +apiVersion: v1 +kind: Service +metadata: + name: nginx: service + labels: + app: nginx +# spec: \ No newline at end of file diff --git a/__tests__/fixtures/yaml/multiple/yaml1.yaml b/__tests__/fixtures/yaml/multiple/yaml1.yaml new file mode 100644 index 0000000..72394a3 --- /dev/null +++ b/__tests__/fixtures/yaml/multiple/yaml1.yaml @@ -0,0 +1,16 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment + labels: + app: nginx +# spec: +--- +apiVersion: v1 +kind: Service +metadata: + name: nginx-service + labels: + app: nginx +# spec: diff --git a/__tests__/functions/yaml-validator.test.js b/__tests__/functions/yaml-validator.test.js index 5e72d6f..cf98cd6 100644 --- a/__tests__/functions/yaml-validator.test.js +++ b/__tests__/functions/yaml-validator.test.js @@ -23,6 +23,7 @@ beforeEach(() => { process.env.INPUT_YAML_AS_JSON = false process.env.INPUT_USE_DOT_MATCH = 'true' process.env.INPUT_FILES = '' + process.env.INPUT_ALLOW_MULTIPLE_DOCUMENTS = 'false' }) test('successfully validates a yaml file with a schema', async () => { @@ -237,3 +238,31 @@ test('skips all files when yaml_as_json is true, even invalid ones', async () => 'skipping yaml since it should be treated as json: __tests__/fixtures/yaml/invalid/skip-bad.yaml' ) }) + +test('successfully validates a yaml file with multiple documents but fails on the other', async () => { + process.env.INPUT_ALLOW_MULTIPLE_DOCUMENTS = 'true' + process.env.INPUT_BASE_DIR = '__tests__/fixtures/yaml/multiple' + expect(await yamlValidator(excludeMock)).toStrictEqual({ + failed: 1, + passed: 1, + skipped: 0, + success: false, + violations: [{ + file: '__tests__/fixtures/yaml/multiple/invalid.yaml', + errors: [ + { + path: null, + message: 'Invalid YAML' + } + ] + }] + }) + expect(infoMock).toHaveBeenCalledWith( + `multiple documents found in file: __tests__/fixtures/yaml/multiple/yaml1.yaml` + ) + expect(errorMock).toHaveBeenCalledWith( + expect.stringMatching( + '❌ failed to parse YAML file: __tests__/fixtures/yaml/multiple/invalid.yaml' + ) + ) +}) \ No newline at end of file diff --git a/action.yml b/action.yml index 60cdcbc..a2e97cf 100644 --- a/action.yml +++ b/action.yml @@ -81,6 +81,10 @@ inputs: description: The full path to the .gitignore file to use if use_gitignore is set to "true" (e.g. ./src/.gitignore) - Default is ".gitignore" which uses the .gitignore file in the root of the repository required: false default: ".gitignore" + allow_multiple_documents: + description: Whether or not to allow multiple documents in a single YAML file - "true" or "false" - Default is "false" + required: false + default: "false" outputs: success: description: Whether or not the validation was successful for all files - "true" or "false" diff --git a/src/functions/yaml-validator.js b/src/functions/yaml-validator.js index 3441c31..37dc646 100644 --- a/src/functions/yaml-validator.js +++ b/src/functions/yaml-validator.js @@ -2,7 +2,7 @@ import * as core from '@actions/core' import validateSchema from 'yaml-schema-validator' import {readFileSync} from 'fs' import {fdir} from 'fdir' -import {parse} from 'yaml' +import {parse,parseAllDocuments} from 'yaml' // Helper function to validate all yaml files in the baseDir export async function yamlValidator(exclude) { @@ -13,6 +13,7 @@ export async function yamlValidator(exclude) { const yamlExcludeRegex = core.getInput('yaml_exclude_regex') const yamlAsJson = core.getBooleanInput('yaml_as_json') const useDotMatch = core.getBooleanInput('use_dot_match') + const allowMultipleDocuments = core.getBooleanInput('allow_multiple_documents') let files = core.getMultilineInput('files').filter(Boolean) // remove trailing slash from baseDir @@ -83,24 +84,42 @@ export async function yamlValidator(exclude) { continue } + let multipleDocuments = false try { // try to parse the yaml file parse(readFileSync(fullPath, 'utf8')) + } catch { - // if the yaml file is invalid, log an error and set success to false - core.error(`❌ failed to parse YAML file: ${fullPath}`) - result.success = false - result.failed++ - result.violations.push({ - file: fullPath, - errors: [ - { - path: null, - message: 'Invalid YAML' + if (allowMultipleDocuments) { + try { + let documents = parseAllDocuments(readFileSync(fullPath, 'utf8')) + for (let doc of documents) { + parse(doc.toString()) } - ] - }) - continue + multipleDocuments = true + } catch { + // doc.toString() will throw an error if the document is invalid + } + } + + if (!multipleDocuments) { + // if the yaml file is invalid, log an error and set success to false + core.error(`❌ failed to parse YAML file: ${fullPath}`) + result.success = false + result.failed++ + result.violations.push({ + file: fullPath, + errors: [ + { + path: null, + message: 'Invalid YAML' + } + ] + }) + continue + } else { + core.info(`multiple documents found in file: ${fullPath}`) + } } // if no yamlSchema is provided, skip validation against the schema @@ -108,7 +127,8 @@ export async function yamlValidator(exclude) { !yamlSchema || yamlSchema === '' || yamlSchema === null || - yamlSchema === undefined + yamlSchema === undefined || + multipleDocuments ) { result.passed++ core.info(`${fullPath} is valid`) From fe2f82be77c8c3494dff22a844c634fa61cf17a8 Mon Sep 17 00:00:00 2001 From: bbompk <65244610+bbompk@users.noreply.github.com> Date: Mon, 30 Oct 2023 10:55:48 +0700 Subject: [PATCH 2/4] trigger workflow --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 52009be..bcfb45e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# JSON and YAML - Validator ✅ +# JSON and YAML - Validator ✅ [![CodeQL](https://github.com/grantbirki/json-yaml-validate/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/grantbirki/json-yaml-validate/actions/workflows/codeql-analysis.yml) [![test](https://github.com/grantbirki/json-yaml-validate/actions/workflows/test.yml/badge.svg)](https://github.com/grantbirki/json-yaml-validate/actions/workflows/test.yml) [![acceptance-test](https://github.com/GrantBirki/json-yaml-validate/actions/workflows/acceptance-test.yml/badge.svg)](https://github.com/GrantBirki/json-yaml-validate/actions/workflows/acceptance-test.yml) [![package-check](https://github.com/grantbirki/json-yaml-validate/actions/workflows/package-check.yml/badge.svg)](https://github.com/grantbirki/json-yaml-validate/actions/workflows/package-check.yml) [![lint](https://github.com/grantbirki/json-yaml-validate/actions/workflows/lint.yml/badge.svg)](https://github.com/grantbirki/json-yaml-validate/actions/workflows/lint.yml) [![coverage](./badges/coverage.svg)](./badges/coverage.svg) From a62e2a945cd957afd7951a581ca21c3e30c51975 Mon Sep 17 00:00:00 2001 From: "k.panithi" Date: Wed, 8 Nov 2023 16:34:18 +0700 Subject: [PATCH 3/4] log parsing error --- __tests__/functions/yaml-validator.test.js | 6 ++- src/functions/yaml-validator.js | 52 +++++++++++----------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/__tests__/functions/yaml-validator.test.js b/__tests__/functions/yaml-validator.test.js index cf98cd6..3e6b8b3 100644 --- a/__tests__/functions/yaml-validator.test.js +++ b/__tests__/functions/yaml-validator.test.js @@ -112,7 +112,8 @@ test('fails to validate a yaml file without using a schema', async () => { errors: [ { path: null, - message: 'Invalid YAML' + message: 'Invalid YAML', + error: "YAMLParseError Nested mappings are not allowed in compact mappings at line 4, column 17" } ] } @@ -252,7 +253,8 @@ test('successfully validates a yaml file with multiple documents but fails on th errors: [ { path: null, - message: 'Invalid YAML' + message: 'Invalid YAML', + error: 'YAMLParseError Nested mappings are not allowed in compact mappings at line 13, column 9' } ] }] diff --git a/src/functions/yaml-validator.js b/src/functions/yaml-validator.js index 37dc646..f82c9c5 100644 --- a/src/functions/yaml-validator.js +++ b/src/functions/yaml-validator.js @@ -85,41 +85,41 @@ export async function yamlValidator(exclude) { } let multipleDocuments = false + try { // try to parse the yaml file - parse(readFileSync(fullPath, 'utf8')) - - } catch { if (allowMultipleDocuments) { - try { let documents = parseAllDocuments(readFileSync(fullPath, 'utf8')) for (let doc of documents) { - parse(doc.toString()) + if (doc.errors.length > 0) { + // format and show the first error + throw doc.errors[0] + } + parse(doc.toString()) // doc.toString() will throw an error if the document is invalid } + core.info(`multiple documents found in file: ${fullPath}`) multipleDocuments = true - } catch { - // doc.toString() will throw an error if the document is invalid - } } - - if (!multipleDocuments) { - // if the yaml file is invalid, log an error and set success to false - core.error(`❌ failed to parse YAML file: ${fullPath}`) - result.success = false - result.failed++ - result.violations.push({ - file: fullPath, - errors: [ - { - path: null, - message: 'Invalid YAML' - } - ] - }) - continue - } else { - core.info(`multiple documents found in file: ${fullPath}`) + else { + parse(readFileSync(fullPath, 'utf8')) } + } catch(err) { + // if the yaml file is invalid, log an error and set success to false + core.error(`❌ failed to parse YAML file: ${fullPath}`) + result.success = false + result.failed++ + result.violations.push({ + file: fullPath, + errors: [ + { + path: null, + message: 'Invalid YAML', + // format error message + error: err.toString().split(':').slice(0, 2).join('') + } + ] + }) + continue } // if no yamlSchema is provided, skip validation against the schema From 5b538b59456e5d7a1a9588976b0fd6e4037c5361 Mon Sep 17 00:00:00 2001 From: "k.panithi" Date: Wed, 8 Nov 2023 16:48:35 +0700 Subject: [PATCH 4/4] run prettier --- __tests__/functions/yaml-validator.test.js | 28 ++++++++++++--------- src/functions/yaml-validator.js | 29 +++++++++++----------- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/__tests__/functions/yaml-validator.test.js b/__tests__/functions/yaml-validator.test.js index 3e6b8b3..669e136 100644 --- a/__tests__/functions/yaml-validator.test.js +++ b/__tests__/functions/yaml-validator.test.js @@ -113,7 +113,8 @@ test('fails to validate a yaml file without using a schema', async () => { { path: null, message: 'Invalid YAML', - error: "YAMLParseError Nested mappings are not allowed in compact mappings at line 4, column 17" + error: + 'YAMLParseError Nested mappings are not allowed in compact mappings at line 4, column 17' } ] } @@ -248,16 +249,19 @@ test('successfully validates a yaml file with multiple documents but fails on th passed: 1, skipped: 0, success: false, - violations: [{ - file: '__tests__/fixtures/yaml/multiple/invalid.yaml', - errors: [ - { - path: null, - message: 'Invalid YAML', - error: 'YAMLParseError Nested mappings are not allowed in compact mappings at line 13, column 9' - } - ] - }] + violations: [ + { + file: '__tests__/fixtures/yaml/multiple/invalid.yaml', + errors: [ + { + path: null, + message: 'Invalid YAML', + error: + 'YAMLParseError Nested mappings are not allowed in compact mappings at line 13, column 9' + } + ] + } + ] }) expect(infoMock).toHaveBeenCalledWith( `multiple documents found in file: __tests__/fixtures/yaml/multiple/yaml1.yaml` @@ -267,4 +271,4 @@ test('successfully validates a yaml file with multiple documents but fails on th '❌ failed to parse YAML file: __tests__/fixtures/yaml/multiple/invalid.yaml' ) ) -}) \ No newline at end of file +}) diff --git a/src/functions/yaml-validator.js b/src/functions/yaml-validator.js index f82c9c5..56c6dfd 100644 --- a/src/functions/yaml-validator.js +++ b/src/functions/yaml-validator.js @@ -2,7 +2,7 @@ import * as core from '@actions/core' import validateSchema from 'yaml-schema-validator' import {readFileSync} from 'fs' import {fdir} from 'fdir' -import {parse,parseAllDocuments} from 'yaml' +import {parse, parseAllDocuments} from 'yaml' // Helper function to validate all yaml files in the baseDir export async function yamlValidator(exclude) { @@ -13,7 +13,9 @@ export async function yamlValidator(exclude) { const yamlExcludeRegex = core.getInput('yaml_exclude_regex') const yamlAsJson = core.getBooleanInput('yaml_as_json') const useDotMatch = core.getBooleanInput('use_dot_match') - const allowMultipleDocuments = core.getBooleanInput('allow_multiple_documents') + const allowMultipleDocuments = core.getBooleanInput( + 'allow_multiple_documents' + ) let files = core.getMultilineInput('files').filter(Boolean) // remove trailing slash from baseDir @@ -89,21 +91,20 @@ export async function yamlValidator(exclude) { try { // try to parse the yaml file if (allowMultipleDocuments) { - let documents = parseAllDocuments(readFileSync(fullPath, 'utf8')) - for (let doc of documents) { - if (doc.errors.length > 0) { - // format and show the first error - throw doc.errors[0] - } - parse(doc.toString()) // doc.toString() will throw an error if the document is invalid + let documents = parseAllDocuments(readFileSync(fullPath, 'utf8')) + for (let doc of documents) { + if (doc.errors.length > 0) { + // format and show the first error + throw doc.errors[0] } - core.info(`multiple documents found in file: ${fullPath}`) - multipleDocuments = true - } - else { + parse(doc.toString()) // doc.toString() will throw an error if the document is invalid + } + core.info(`multiple documents found in file: ${fullPath}`) + multipleDocuments = true + } else { parse(readFileSync(fullPath, 'utf8')) } - } catch(err) { + } catch (err) { // if the yaml file is invalid, log an error and set success to false core.error(`❌ failed to parse YAML file: ${fullPath}`) result.success = false