diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml new file mode 100644 index 0000000..ef43720 --- /dev/null +++ b/.github/workflows/tests.yaml @@ -0,0 +1,64 @@ +name: Action tests + +on: + push: + branches: + - master + - main + pull_request: + branches: + - master + - main + +jobs: + schema: + name: Run tests + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + test: + # Strict + - suite: nostrict + schema: ./tests/nostrict/schema.yaml + target: ./tests/nostrict/valid-matching-fields.yaml + no-strict: true + error-is-success: false + - suite: nostrict + schema: ./tests/nostrict/schema.yaml + target: ./tests/nostrict/valid-extra-argument.yaml + no-strict: true + error-is-success: false + - suite: nostrict + schema: ./tests/nostrict/schema.yaml + target: ./tests/nostrict/invalid-not-a-boolean.yaml + no-strict: true + error-is-success: true + - suite: nostrict + schema: ./tests/nostrict/schema.yaml + target: ./tests/nostrict/invalid-nums-out-of-range.yaml + no-strict: true + error-is-success: true + + # Nostrict + - suite: strict + schema: ./tests/strict/schema.yaml + target: ./tests/strict/valid-matching-fields.yaml + no-strict: false + error-is-success: false + - suite: strict + schema: ./tests/strict/schema.yaml + target: ./tests/strict/invalid-extra-argument.yaml + no-strict: false + error-is-success: true + + steps: + - uses: actions/checkout@v2 + - uses: ./ + name: "Test: ${{ matrix.test.suite }} ${{ matrix.test.target }}" + with: + schema: ${{ matrix.test.schema }} + target: ${{ matrix.test.target }} + no-strict: ${{ matrix.test.no-strict }} + error-is-success: ${{ matrix.test.error-is-success }} diff --git a/Dockerfile b/Dockerfile index 6f93cb4..3e93752 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,19 @@ -FROM python:3.8-alpine +FROM alpine:3 -RUN apk add --no-cache bash +RUN apk add --no-cache bash python3 py3-pip \ + && mkdir -p /usr/src/app \ + && addgroup -g 10000 app \ + && adduser -s /bin/bash -G app -u 10000 -h /usr/src/app -k /dev/null -D app \ + && python3 -m venv /usr/src/app/venv \ + && chown -R app:app /usr/src/app WORKDIR /usr/src/app +USER 10000:10000 +ENV VIRTUAL_ENV=/usr/src/app/venv \ + PATH=/usr/src/app/venv/bin:$PATH COPY requirements.txt ./ RUN pip install -r requirements.txt -COPY example/ ./example - -# Nonexistent -USER 2000:2000 - COPY entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] diff --git a/README.md b/README.md index 0bcbf87..78188c7 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A GitHub action that uses [Yamale][] for YAML schema validation. ## Usage - Filenames are relative to the repository root. -- Enable strict checking by setting `strict` to a non-empty string. +- Disable strict checking by setting `no-strict` to `true`, `1` or `yes`. - For help with the schema definitions and reference, see [Yamale][]. The following example sets up a check to validate a YAML file in your @@ -20,25 +20,27 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: nrkno/yaml-schema-validator-github-action@master + - uses: nrkno/yaml-schema-validator-github-action@v4 with: - schema: 'schemas/schema.yaml' - target: 'target.yaml' - # Uncomment to enable strict checks - # strict: '1' + schema: schemas/schema.yaml + target: target.yaml + # Uncomment to disable strict checks + # no-strict: true ``` -### Versioning +## Versioning -To bind the action to a specific release, prefix with `@`. -E.g. `nrkno/yaml-schema-validator-github-action@v0.1.0`. +This action is meant to be a wrapper around Yamale, so as of version 4.x +of Yamale, this action will follow Yamale's major version scheme. + +To bind the action to a specific release, suffix with `@`. +E.g. `nrkno/yaml-schema-validator-github-action@v4`. https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsuses ## Developing -Create and enable a Python virtualenv (not strictly required, but makes testing -more robust) +Create and enable a Python virtualenv ``` $ python -m venv venv @@ -51,21 +53,12 @@ Install dependencies $ pip install -r requirements.txt ``` -Do a test-run with the provided examples +Do a test-run with one of the provided examples ``` -$ ./entrypoint.sh example/schema.yaml example/file.yaml +$ INPUT_SCHEMA=example/schema.yaml \ + INPUT_TARGET=example/file-valid-strict.yaml \ + ./entrypoint.sh ``` -### Using Docker - -Build the container and reference files within the example/ folder. - -``` -$ docker build -t yaml-schema-validator . -$ docker run yaml-schema-validator example/schema.yaml example/file.yaml -$ docker run yaml-schema-validator example/schema.yaml example/file-invalid.yaml -``` - - [Yamale]: https://github.com/23andMe/Yamale diff --git a/action.yml b/action.yml index f899265..ecd6f84 100644 --- a/action.yml +++ b/action.yml @@ -11,9 +11,18 @@ inputs: target: description: 'File to validate' required: true - strict: - description: 'Enable strict mode. Set to a non-empty string to enable.' + no-strict: + description: 'Disable strict mode' + required: false + error-is-success: + description: 'Flip the validation logic making a failing test pass and a passing test fail. This is used internally for testing the action itself.' required: false runs: using: 'docker' image: 'Dockerfile' + env: + # Need to convert dashes to underscores in environment variable names for bash + # to be able to read them. + # Github Actions passes them like INPUT_NO-STRICT when they contain dashes. + INPUT_NO_STRICT: ${{ inputs.no-strict }} + INPUT_ERROR_IS_SUCCESS: ${{ inputs.error-is-success }} diff --git a/entrypoint.sh b/entrypoint.sh index aa5fcbc..fc6ac0a 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,25 +1,54 @@ #!/bin/bash set -eux -strict='' -schema=${INPUT_SCHEMA:-$1} -target=${INPUT_TARGET:-$2} +# Returns a string `true` the string is considered boolean true, +# otherwise `false`. An empty value is considered false. +function str_bool { + local str="${1:-false}" + local pat='^(true|1|yes)$' + if [[ "$str" =~ $pat ]] + then + echo 'true' + else + echo 'false' + fi +} -if [ -n "${INPUT_STRICT:-}" ] -then - strict='--strict' -fi +schema="$INPUT_SCHEMA" +target="$INPUT_TARGET" +no_strict=$(str_bool "${INPUT_NO_STRICT:-}") +error_is_success=$(str_bool "${INPUT_ERROR_IS_SUCCESS:-}") + +# Must end with a space here +extra_args=' ' -if [ ! -e ${schema} ] +if [ ! -e "${schema}" ] then >&2 echo "Schema does not exist: $schema" exit 1 fi -if [ ! -e ${target} ] +# TODO: Allow directories +if [ ! -e "${target}" ] then >&2 echo "Target does not exist: $target" exit 1 fi -yamale --schema=${schema} $target $strict +if [ "$no_strict" = "true" ] +then + extra_args='--no-strict ' +fi + +if [ "$error_is_success" = "true" ] +then + # Flipped validation logic + echo "--- Flipped validation logic enabled (error-is-success: true)! ---" + # shellcheck disable=SC2086 + yamale $extra_args --schema="${schema}" "$target" && exit 1 + exit 0 +fi + +# Normal execution +# shellcheck disable=SC2086 +yamale $extra_args --schema="${schema}" "$target" diff --git a/example/file-invalid.yaml b/example/file-invalid.yaml index 7b957e2..51edf8f 100644 --- a/example/file-invalid.yaml +++ b/example/file-invalid.yaml @@ -1,4 +1,4 @@ name: Bill -age: 26 -height: 6.2 -awesome: "pretty" +age: 42000 +height: "not a number" +awesome: "not a boolean" diff --git a/example/file.yaml b/example/file-valid-nostrict.yaml similarity index 100% rename from example/file.yaml rename to example/file-valid-nostrict.yaml diff --git a/example/file-valid-strict.yaml b/example/file-valid-strict.yaml new file mode 100644 index 0000000..4a574d2 --- /dev/null +++ b/example/file-valid-strict.yaml @@ -0,0 +1,4 @@ +name: Bill +age: 26 +height: 6.2 +awesome: True diff --git a/requirements.txt b/requirements.txt index 4c96b84..2e0908c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -yamale~=2.2.0 +yamale~=4.0.2 diff --git a/tests/nostrict/invalid-not-a-boolean.yaml b/tests/nostrict/invalid-not-a-boolean.yaml new file mode 100644 index 0000000..335ba64 --- /dev/null +++ b/tests/nostrict/invalid-not-a-boolean.yaml @@ -0,0 +1,4 @@ +name: Bill +age: 26 +height: 6.2 +awesome: "not a boolean" diff --git a/tests/nostrict/invalid-nums-out-of-range.yaml b/tests/nostrict/invalid-nums-out-of-range.yaml new file mode 100644 index 0000000..0af2689 --- /dev/null +++ b/tests/nostrict/invalid-nums-out-of-range.yaml @@ -0,0 +1,4 @@ +name: Bill +age: 420 +height: 6.2 +awesome: true diff --git a/tests/nostrict/schema.yaml b/tests/nostrict/schema.yaml new file mode 100644 index 0000000..9113c13 --- /dev/null +++ b/tests/nostrict/schema.yaml @@ -0,0 +1,4 @@ +name: str() +age: int(max=200) +height: num() +awesome: bool() diff --git a/tests/nostrict/valid-extra-argument.yaml b/tests/nostrict/valid-extra-argument.yaml new file mode 100644 index 0000000..bc87df6 --- /dev/null +++ b/tests/nostrict/valid-extra-argument.yaml @@ -0,0 +1,5 @@ +name: Bill +age: 26 +height: 6.2 +awesome: True +extra: true diff --git a/tests/nostrict/valid-matching-fields.yaml b/tests/nostrict/valid-matching-fields.yaml new file mode 100644 index 0000000..4a574d2 --- /dev/null +++ b/tests/nostrict/valid-matching-fields.yaml @@ -0,0 +1,4 @@ +name: Bill +age: 26 +height: 6.2 +awesome: True diff --git a/tests/strict/invalid-extra-argument.yaml b/tests/strict/invalid-extra-argument.yaml new file mode 100644 index 0000000..bc87df6 --- /dev/null +++ b/tests/strict/invalid-extra-argument.yaml @@ -0,0 +1,5 @@ +name: Bill +age: 26 +height: 6.2 +awesome: True +extra: true diff --git a/tests/strict/schema.yaml b/tests/strict/schema.yaml new file mode 100644 index 0000000..9113c13 --- /dev/null +++ b/tests/strict/schema.yaml @@ -0,0 +1,4 @@ +name: str() +age: int(max=200) +height: num() +awesome: bool() diff --git a/tests/strict/valid-matching-fields.yaml b/tests/strict/valid-matching-fields.yaml new file mode 100644 index 0000000..4a574d2 --- /dev/null +++ b/tests/strict/valid-matching-fields.yaml @@ -0,0 +1,4 @@ +name: Bill +age: 26 +height: 6.2 +awesome: True