diff --git a/.azure-pipelines/ci.yml b/.azure-pipelines/ci.yml new file mode 100644 index 0000000..c3441df --- /dev/null +++ b/.azure-pipelines/ci.yml @@ -0,0 +1,20 @@ +steps: +- task: UsePythonVersion@0 + inputs: + versionSpec: '$(PYTHON_VERSION)' + displayName: 'Use Python $(PYTHON_VERSION)' + +- script: | + python -m pip install --upgrade pip + pip install tox + displayName: "Set up tox" + +- script: | + tox -e azure + displayName: "Run tests" + +- task: PublishTestResults@2 + condition: succeededOrFailed() + inputs: + testResultsFiles: '**/test-*.xml' + testRunTitle: 'Publish test results for Python $(PYTHON_VERSION)' diff --git a/.azure-pipelines/flake8-validation.py b/.azure-pipelines/flake8-validation.py new file mode 100644 index 0000000..89594a9 --- /dev/null +++ b/.azure-pipelines/flake8-validation.py @@ -0,0 +1,42 @@ +import os +import subprocess + +# Flake8 validation +failures = 0 +try: + flake8 = subprocess.run( + [ + "flake8", + "--exit-zero", + "--max-line-length=88", + "--select=E401,E711,E712,E713,E714,E721,E722,E901,F401,F402,F403,F405,F631,F632,F633,F811,F812,F821,F822,F841,F901,W191,W291,W292,W293,W602,W603,W604,W605,W606", + ], + capture_output=True, + check=True, + encoding="latin-1", + timeout=300, + ) +except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as e: + print( + "##vso[task.logissue type=error;]flake8 validation failed with", + str(e.__class__.__name__), + ) + print(e.stdout) + print(e.stderr) + print("##vso[task.complete result=Failed;]flake8 validation failed") + exit() +for line in flake8.stdout.split("\n"): + if ":" not in line: + continue + filename, lineno, column, error = line.split(":", maxsplit=3) + errcode, error = error.strip().split(" ", maxsplit=1) + filename = os.path.normpath(filename) + failures += 1 + print( + f"##vso[task.logissue type=error;sourcepath={filename};" + f"linenumber={lineno};columnnumber={column};code={errcode};]" + error + ) + +if failures: + print(f"##vso[task.logissue type=warning]Found {failures} flake8 violation(s)") + print(f"##vso[task.complete result=Failed;]Found {failures} flake8 violation(s)") diff --git a/.azure-pipelines/syntax-validation.py b/.azure-pipelines/syntax-validation.py new file mode 100644 index 0000000..6483683 --- /dev/null +++ b/.azure-pipelines/syntax-validation.py @@ -0,0 +1,30 @@ +import ast +import os +import sys + +print("Python", sys.version, "\n") + +failures = 0 + +for base, _, files in os.walk("."): + for f in files: + if not f.endswith(".py"): + continue + filename = os.path.normpath(os.path.join(base, f)) + try: + with open(filename, "r") as fh: + ast.parse(fh.read()) + except SyntaxError as se: + failures += 1 + print( + f"##vso[task.logissue type=error;sourcepath={filename};" + f"linenumber={se.lineno};columnnumber={se.offset};]" + f"SyntaxError: {se.msg}" + ) + print(" " + se.text + " " * se.offset + "^") + print(f"SyntaxError: {se.msg} in {filename} line {se.lineno}") + print() + +if failures: + print(f"##vso[task.logissue type=warning]Found {failures} syntax error(s)") + print(f"##vso[task.complete result=Failed;]Found {failures} syntax error(s)") diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml new file mode 100644 index 0000000..288782f --- /dev/null +++ b/.azure-pipelines.yml @@ -0,0 +1,73 @@ +jobs: + - job: checks + displayName: static code analysis + pool: + vmImage: ubuntu-latest + steps: + # Use Python >=3.7 for syntax validation + - task: UsePythonVersion@0 + displayName: Set up python + inputs: + versionSpec: 3.7 + + # Run syntax validation on a shallow clone + - bash: | + python .azure-pipelines/syntax-validation.py + displayName: Syntax validation + + # Run flake8 validation on a shallow clone + - bash: | + pip install flake8 + python .azure-pipelines/flake8-validation.py + displayName: Flake8 validation + + - job: linux + pool: + vmImage: ubuntu-latest + strategy: + matrix: + python35: + PYTHON_VERSION: 3.5 + python36: + PYTHON_VERSION: 3.6 + python37: + PYTHON_VERSION: 3.7 + python38: + PYTHON_VERSION: 3.8 + + steps: + - template: .azure-pipelines/ci.yml + + - job: macOS + pool: + vmImage: macOS-latest + strategy: + matrix: + python35: + PYTHON_VERSION: 3.5 + python36: + PYTHON_VERSION: 3.6 + python37: + PYTHON_VERSION: 3.7 + python38: + PYTHON_VERSION: 3.8 + + steps: + - template: .azure-pipelines/ci.yml + + - job: windows + pool: + vmImage: windows-latest + strategy: + matrix: + python35: + PYTHON_VERSION: 3.5 + python36: + PYTHON_VERSION: 3.6 + python37: + PYTHON_VERSION: 3.7 + python38: + PYTHON_VERSION: 3.8 + + steps: + - template: .azure-pipelines/ci.yml diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..fe55d2e --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +junit_family=xunit2 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/tox.ini b/tox.ini index d37d50b..c5ef51d 100644 --- a/tox.ini +++ b/tox.ini @@ -8,6 +8,15 @@ 3.7: py37 3.6: py36 3.5: py35 + +[testenv:azure] +basepython = python +deps = + pytest-azurepipelines + pytest-cov + -r{toxinidir}/requirements_dev.txt +commands = + pytest -ra --basetemp={envtmpdir} --cov=procrunner --cov-report=html --cov-report=xml --cov-branch [testenv:flake8] basepython = python