Skip to content

Commit

Permalink
feat: allow configure severity level at which to fail the GHA (#3)
Browse files Browse the repository at this point in the history
Co-authored-by: Vladimír Gorej <[email protected]>
  • Loading branch information
amandalian and char0n authored Nov 25, 2023
1 parent f30df61 commit f3d008a
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 13 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/fixtures/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,8 @@
"available",
"pending",
"sold"
]
],
"nullable": "true"
}
}
],
Expand Down
36 changes: 35 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,45 @@ on:
jobs:
test_github_action:
runs-on: ubuntu-latest
continue-on-error: true
outputs:
output1: ${{ steps.test1.outcome }}
output2: ${{ steps.test2.outcome }}

steps:
- uses: actions/checkout@v2
- uses: char0n/apidom-validate@main

- id: test1
name: "Test 1: Validate at Severity level 1 (Errors)"
uses: char0n/apidom-validate@main
with:
definition-file: ./.github/workflows/fixtures/openapi.json
- id: test2
name: "Test 2: Validate at Severity level 4 (Hints)"
if: success() || failure()
uses: char0n/apidom-validate@main
with:
definition-file: ./.github/workflows/fixtures/openapi.json
fails-on: 4

verify_outcomes:
needs: test_github_action
runs-on: ubuntu-latest

steps:
- name: Compare Expected vs Actual Outcomes
run: |
echo ""
echo " Test | Definition File | Fails On Level | Expected Outcome | Actual Outcome"
echo "------|-----------------|----------------|------------------|----------------"
echo " 1 | openapi.json | 1 | success | ${{ needs.test_github_action.outputs['output1'] }}"
echo " 2 | openapi.json | 4 | failure | ${{ needs.test_github_action.outputs['output2'] }}"
echo ""
if [ "${{ needs.test_github_action.outputs['output1'] }}" == "success" ] && [ "${{ needs.test_github_action.outputs['output2'] }}" == "failure" ] ; then
echo "SUCCESS: Actual outcomes match expected outcomes."
exit 0
else
echo "FAILED: Actual outcomes do not match expected outcomes."
exit 1
fi
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,19 @@ Validation rules are exactly the same as the one that https://editor-next.swagge

**Required** Path to definition file.

## `fails-on`

Severity level at which to fail action. Default `1`, if not specified.
- `1`: Fails if **error** messages exist in validation output
- `2`: Fails if **error** or **warning** messages exist in validation output
- `3`: Fails if **error**, **warning** or **information** messages exist in validation output
- `4`: Fails if **error**, **warning**, **information** or **hint** messages exist in validation output

## Example usage

```yaml
uses: char0n/apidom-validate@v1
with:
definition-file: 'path/to/my/openapi.yaml'
fails-on: 2
```
11 changes: 11 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,22 @@ inputs:
definition-file:
description: Path to definition file
required: true
fails-on:
type: choice
description: Severity level at which to fail action
required: false
options:
- 1
- 2
- 3
- 4
default: 1
runs:
using: 'docker'
image: 'Dockerfile'
args:
- ${{ inputs.definition-file }}
- ${{ inputs.fails-on }}
branding:
icon: 'file-text'
color: 'green'
34 changes: 23 additions & 11 deletions validate.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,26 @@ const core = require('@actions/core');
const { TextDocument } = require('vscode-languageserver-textdocument');


const [, , definitionFile] = process.argv;
const definitionFile = process.argv.at(2);
const failsOn = parseInt(process.argv.at(3), 10);
const definitionFilePath = path.join('/github/workspace', definitionFile);
const definitionFileContent = fs.readFileSync(definitionFilePath, { encoding:'utf8', flag:'r' });
const languageService = getLanguageService({});
const textDocument = TextDocument.create(definitionFile, 'apidom', 0, definitionFileContent);

const DiagnosticSeverity = {
Error: 1,
Warning: 2,
Information: 3,
Hint: 4,
}

const mapLine = (range) => `${range.start.line}:${range.start.character}`;
const mapSeverity = (severity) => severity === 1
const mapSeverity = (severity) => severity === DiagnosticSeverity.Error
? 'error'
: severity === 2
: severity === DiagnosticSeverity.Warning
? 'warning'
: severity === 3
: severity === DiagnosticSeverity.Information
? 'information'
: 'hint';
const mapDiagnostic = ({ range, severity, code, message }) => ({
Expand All @@ -34,15 +42,19 @@ const mapDiagnostics = (diagnostics) => {
logger.table(diagnostics.map(mapDiagnostic));
return (ts.read() || '').toString();
};
const getSeverityCount = (severity, { errors, warnings, information, hints }) => {
const components = [errors, warnings, information, hints];
return components.slice(0, severity).reduce((total, current) => total + current.length, 0);
};

(async () => {
core.info(`\u001b[1mApiDOM lint ${definitionFile}`);

const validationResult = await languageService.doValidation(textDocument);
const errors = validationResult.filter((diagnostic) => diagnostic.severity === 1);
const warnings = validationResult.filter((diagnostic) => diagnostic.severity === 2);
const information = validationResult.filter((diagnostic) => diagnostic.severity === 3);
const hints = validationResult.filter((diagnostic) => diagnostic.severity === 4);
const errors = validationResult.filter((diagnostic) => diagnostic.severity === DiagnosticSeverity.Error);
const warnings = validationResult.filter((diagnostic) => diagnostic.severity === DiagnosticSeverity.Warning);
const information = validationResult.filter((diagnostic) => diagnostic.severity === DiagnosticSeverity.Information);
const hints = validationResult.filter((diagnostic) => diagnostic.severity === DiagnosticSeverity.Hint);

languageService.terminate();

Expand All @@ -61,8 +73,8 @@ const mapDiagnostics = (diagnostics) => {
: '\u001b[1;1m';
core.info(`${color}${errors.length + warnings.length} problems (${errors.length} error, ${warnings.length} warnings, ${information.length} information, ${hints.length} hints)`);

// fail the action on errors
if (errors.length > 0) {
// fail the action depending on severity defined in `failsOn`
if (getSeverityCount(failsOn, { errors, warnings, information, hints }) > 0) {
core.setFailed('');
}
})();
})();

0 comments on commit f3d008a

Please sign in to comment.