Skip to content

Commit

Permalink
Merge pull request #3 from helpfulengineering/gh-action
Browse files Browse the repository at this point in the history
Initial github action logic
  • Loading branch information
lepidopterane-atsmith authored Mar 23, 2021
2 parents d84c0bc + a6b1bb5 commit 6a9ede3
Show file tree
Hide file tree
Showing 9 changed files with 262 additions and 73 deletions.
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
.git
.devcontainer
testschemas
.vscode
.devcontainer
16 changes: 16 additions & 0 deletions .github/workflows/push-main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: Python deploy

on:
push:
branches: [ main ]

jobs:
deploy:
steps:
- uses: actions/checkout@v2
- name: Bump version and push tag
if: ${{ github.event_name == 'push' }}
uses: anothrNick/[email protected]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
WITH_V: true
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ RUN groupadd -g ${gid} ${group} && useradd -u ${uid} -g ${group} -s /bin/sh ${us
chown -R ${uid}:${gid} /okv
WORKDIR /okv

COPY --chown=${uid}:${gid} entrypoint.sh .
# Should convert image to a multi-stage build and optimize
# for size later but this is for the POC
COPY --chown=${uid}:${gid} . .
Expand All @@ -18,4 +19,4 @@ RUN python setup.py install

USER ${user}

ENTRYPOINT [ "okv" ]
ENTRYPOINT [ "/okv/entrypoint.sh" ]
49 changes: 49 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,51 @@
# ok-validate

Validation of Open Know How specification and related schemas (https://openknowhow.org)
## GitHub Action

This action uses an Open Knowledge Framework schema to validate your manifest such as for [`OKH`](https://openknowhow.org/)

### Inputs

#### `cpu-num`

*Optional* How many cores to use. Default `"4"`.

#### `ok`

*Optional* Which Open Knowledge Framework schema to use for validation. Default [`"okh"`](./okv/schemas/okh.yaml).

#### `parser`

*Optional* YAML library to load file. Default `"pyyaml"` (can be one of `"pyyaml"` or `"ruamel"`).

#### `path`

*Required* The path to your manifest or manifests relative to your workspace. Default `"./"`.

#### `schema`

*Optional* The full path to an Open Knowledge Framework schema or custom schema to use for validation. Default `""`.

#### `strict`

*Optional* Whether to run in strict mode. Default `"false"` (can be one of `"true"` or `"false"`).
### Example usage

```yaml
uses: helpfulengineering/[email protected]
```
```yaml
uses: helpfulengineering/[email protected]
with:
path: './some/path/to/file.yaml'
```
## Automatic releasing
<https://github.com/anothrNick/github-tag-action#bumping>
> Manual Bumping: Any commit message that includes #major, #minor, #patch, or #none will trigger the
> respective version bump. If two or more are present, the highest-ranking one will take precedence.
> If #none is contained in the commit message, it will skip bumping regardless DEFAUT_BUMP.
37 changes: 37 additions & 0 deletions action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# action.yml
name: 'ok-validate'
description: 'Use an Open Knowledge Framework schema to validate your manifest'
branding:
icon: check-circle
color: green
inputs:
cpu-num:
description: 'How many cores to use'
required: false
default: '4'
ok:
description: 'Which preset Open Knowledge Framework schema to use for validation. Should be one of ("okh")'
required: false
default: 'okh'
parser:
description: 'YAML library to load file'
required: false
default: 'pyyaml'
path:
description: 'The path to your mainfest or manifests relative to your workspace'
required: true
default: './'
schema:
description: 'The full path to an Open Knowledge Framework schema or custom schema to use for validation'
required: false
default: ''
strict:
description: 'Should the schema validator be run in strict mode?'
required: false
default: 'false'
outputs:
results:
description: 'The results of the validator'
runs:
using: 'docker'
image: 'Dockerfile'
18 changes: 18 additions & 0 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash
set -ex

if [ -z "${GITHUB_ACTIONS}" ]; then
exec okv ${@}
else
strict_mode='--no-strict'
if [ "${INPUT_STRICT}" = "true" ]; then strict_mode=''; fi
INPUT_CPU_NUM="$(env | sed -n 's/^INPUT_CPU-NUM=\(.*\)/\1/p')"
schema_flag="--ok=${INPUT_OK}"
if [ ! -z "${INPUT_SCHEMA}" ]; then
schema_flag="--schema=${INPUT_SCHEMA}"
fi
output=$(okv ${schema_flag} --cpu-num=${INPUT_CPU_NUM} --parser=${INPUT_PARSER} ${strict_mode} -path=${INPUT_PATH})
result=$?
echo "::set-output name=results::$(echo $output)"
if [ "${result}" != "0" ]; then exit 1; fi
fi
176 changes: 113 additions & 63 deletions okv/okv.py
Original file line number Diff line number Diff line change
@@ -1,86 +1,136 @@
import argparse
# Import Yamale and make a schema object:
import datetime
import os
import sys

import yamale
import yaml
from yamale.yamale_error import YamaleError
import datetime

from .validators import DefaultValidators, RootValidation
import yaml
import os



def yamale_args_wrapper():
parser = argparse.ArgumentParser(description='Validate yaml files.')
parser.add_argument('-path', metavar='PATH', default='./', nargs='?',
help='folder to validate. Default is current directory.')
parser.add_argument('-s', '--schema',
help='filename of schema. Default is schema.yaml.')
parser.add_argument('-n', '--cpu-num', default=4, type=int,
help='number of CPUs to use. Default is 4.')
parser.add_argument('-p', '--parser', default='pyyaml',
help='YAML library to load files. Choices are "ruamel" or "pyyaml" (default).')
parser.add_argument('--no-strict', action='store_true',
help='Disable strict mode, unexpected elements in the data will be accepted.')
parser.add_argument('--ok', default='okh',
help='This indicates which Open Know specification to use.')
parser.add_argument(
'-path',
metavar='PATH',
default='./',
nargs='?',
help='folder to validate. Default is current directory.',
)
parser.add_argument(
'-s',
'--schema',
help='filename of schema. Default is schema.yaml.',
)
parser.add_argument(
'-n', '--cpu-num',
default=4,
type=int,
help='number of CPUs to use. Default is 4.',
)
parser.add_argument(
'-p',
'--parser',
default='pyyaml',
help='YAML library to load files. Choices are "ruamel" or "pyyaml" (default).',
)
parser.add_argument(
'--no-strict',
action='store_true',
help='Disable strict mode, unexpected elements in the data will be accepted.',
)
parser.add_argument(
'--no-error',
action='store_true',
help='Ignore error when violation of schema is identified.',
)
parser.add_argument(
'--ok',
default='okh',
help='This indicates which Open Know specification to use.'
)
return parser.parse_args()


def included_ok_schema(oktype):
module_root = os.path.abspath(os.path.dirname(__file__))
return os.path.join(module_root, 'schemas', oktype + '.yaml')
return os.path.join(module_root, 'schemas', oktype.lower() + '.yaml')


def composite_error_message(result, root_validation_error=None):
results = []
for given_result in result:
results.append(str(given_result))
if root_validation_error:
for error_result in root_validation_error.results:
for error in error_result.errors:
error_string = '\t{0}'.format(str(error))
results.append(error_string)
return '\n'.join(results)


def _handle_error_exit(error_messages):
raise_python_exception = False
error_string = '\n----\n'.join(set(error_messages))
if raise_python_exception:
raise ValueError(error_string)
print(error_string)
sys.exit(1)


def main():
args = yamale_args_wrapper()
validators = DefaultValidators.copy()
schema_to_use = included_ok_schema(args.ok) if args.schema is None else args.schema
results = []
data_filename_arr=[]
string_error_messages = []
data_filename_arr = []
path_to_use = args.path
strict = not args.no_strict
raise_error = not args.no_error

try:
schema = yamale.make_schema(schema_to_use, validators=validators)
if(path_to_use.endswith('.yaml') or path_to_use.endswith('.yml')):
schema = yamale.make_schema(schema_to_use, validators=validators)
path_to_use = os.path.abspath(path_to_use)
if os.path.isfile(path_to_use):
if (path_to_use.endswith('.yaml') or path_to_use.endswith('.yml')):
data_filename_arr.append(path_to_use)
else:
for root, dirs, files in os.walk(path_to_use):
for f in files:
if (f.endswith('.yaml') or f.endswith('.yml')) and f != schema_to_use:
data_filename_arr.append(args.path+"/"+f)

file_count = 1

for d in data_filename_arr:
# print(d)
data = yamale.make_data(d) # currently single files
# Create a Data object

root_validation = RootValidation(schema=schema, data=data, validators=validators, args=args)
pre_results = root_validation.validate() # single goes through here
for p in pre_results:
if '\'None\' is Valid' not in p:
print(p)

# root_level_validation(schema, data, validators, args)
# Validate data against the schema. Throws a ValueError if data is invalid.
mistake_made = False
try:
result = yamale.validate(schema, data, False, False)
except (SyntaxError, NameError, TypeError, ValueError, YamaleError) as f:
print(f)
print(result)
mistake_made = True

if mistake_made == False:
results_list = list(dict.fromkeys(result))
for r in results_list:
print(r)

if file_count < len(data_filename_arr):
print("\n • • • \n")
file_count += 1

except (SyntaxError, NameError, TypeError, ValueError, YamaleError) as e:
err_type = str(type(e)).split("\'")[1]+": "
print('Validation error!\n%s' % err_type+str(e))
print("Consider revising .yaml format.")
else:
for root, _dirs, files in os.walk(path_to_use):
for file in files:
if (file.endswith('.yaml') or file.endswith('.yml')) and file != schema_to_use:
data_filename = os.path.join(root, file)
data_filename_arr.append(data_filename)

file_count = 1

for data_filename in data_filename_arr:
data = yamale.make_data(data_filename)
# Create a Data object
root_validation = RootValidation(schema=schema, data=data, validators=validators, args=args)
root_validation_error = None
try:
root_validation.validate()
except (YamaleError) as err:
root_validation_error = err

# root_level_validation(schema, data, validators, args)
# Validate data against the schema. Throws a ValueError if data is invalid.
result = None
should_raise_error = False if data_filename_arr else raise_error
result = yamale.validate(schema, data, strict, should_raise_error)
error_message = composite_error_message(result, root_validation_error)
if error_message:
string_error_messages.append(error_message)
results.extend(result)

if file_count < len(data_filename_arr):
file_count += 1

if string_error_messages:
_handle_error_exit(string_error_messages)

if __name__ == '__main__':
main()
Loading

0 comments on commit 6a9ede3

Please sign in to comment.