mirror of
https://github.com/pyTooling/Actions.git
synced 2026-02-14 12:06:56 +08:00
v0.0.0
This commit is contained in:
34
.github/workflows/ArtifactCleanUp.yml
vendored
Normal file
34
.github/workflows/ArtifactCleanUp.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
name: ArtifactCleanUp
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
package:
|
||||
description: 'Artifacts to be removed on not tagged runs.'
|
||||
required: true
|
||||
type: string
|
||||
remaining:
|
||||
description: 'Artifacts to be removed unconditionally.'
|
||||
required: false
|
||||
default: ''
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
|
||||
ArtifactCleanUp:
|
||||
name: 🗑️ Artifact Cleanup
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
||||
- name: 🗑️ Delete package Artifacts
|
||||
if: ${{ ! startsWith(github.ref, 'refs/tags') }}
|
||||
uses: geekyeggo/delete-artifact@v1
|
||||
with:
|
||||
name: ${{ inputs.package }}
|
||||
|
||||
- name: 🗑️ Delete remaining Artifacts
|
||||
if: ${{ inputs.remaining != '' }}
|
||||
uses: geekyeggo/delete-artifact@v1
|
||||
with:
|
||||
name: ${{ inputs.remaining }}
|
||||
31
.github/workflows/BuildTheDocs.yml
vendored
Normal file
31
.github/workflows/BuildTheDocs.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: Documentation
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
artifact:
|
||||
description: 'Name of the documentation artifact.'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
|
||||
BuildTheDocs:
|
||||
name: 📓 Run BuildTheDocs
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: ⏬ Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: 🛳️ Build documentation
|
||||
uses: buildthedocs/btd@v0
|
||||
with:
|
||||
skip-deploy: true
|
||||
|
||||
- name: 📤 Upload 'documentation' artifacts
|
||||
uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: ${{ inputs.artifact }}
|
||||
path: doc/_build/html
|
||||
retention-days: 1
|
||||
80
.github/workflows/CoverageCollection.yml
vendored
Normal file
80
.github/workflows/CoverageCollection.yml
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
name: Coverage Collection
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
python_version:
|
||||
description: 'Python version.'
|
||||
required: false
|
||||
default: '3.10'
|
||||
type: string
|
||||
requirements:
|
||||
description: 'Python dependencies to be installed through pip.'
|
||||
required: false
|
||||
default: '-r tests/requirements.txt'
|
||||
type: string
|
||||
artifact:
|
||||
description: 'Name of the coverage artifact.'
|
||||
required: true
|
||||
type: string
|
||||
secrets:
|
||||
codacy_token:
|
||||
description: 'Token to push result to codacy.'
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
|
||||
Coverage:
|
||||
name: 📈 Collect Coverage Data using Python ${{ inputs.python_version }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: ⏬ Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: 🐍 Setup Python ${{ inputs.python_version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ inputs.python_version }}
|
||||
|
||||
- name: 🗂 Install dependencies
|
||||
run: |
|
||||
python -m pip install -U pip
|
||||
python -m pip install ${{ inputs.requirements }}
|
||||
|
||||
- name: Collect coverage
|
||||
continue-on-error: true
|
||||
run: |
|
||||
python -m pytest -rA --cov=.. --cov-config=tests/.coveragerc tests/unit --color=yes
|
||||
|
||||
- name: Convert to cobertura format
|
||||
run: coverage xml
|
||||
|
||||
- name: Convert to HTML format
|
||||
run: |
|
||||
coverage html
|
||||
rm htmlcov/.gitignore
|
||||
|
||||
- name: 📤 Upload 'Coverage Report' artifact
|
||||
continue-on-error: true
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ inputs.artifact }}
|
||||
path: htmlcov
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
- name: 📊 Publish coverage at CodeCov
|
||||
continue-on-error: true
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
file: ./coverage.xml
|
||||
flags: unittests
|
||||
env_vars: PYTHON
|
||||
|
||||
- name: 📉 Publish coverage at Codacy
|
||||
continue-on-error: true
|
||||
uses: codacy/codacy-coverage-reporter-action@master
|
||||
with:
|
||||
project-token: ${{ secrets.codacy_token }}
|
||||
coverage-reports: ./coverage.xml
|
||||
53
.github/workflows/Package.yml
vendored
Normal file
53
.github/workflows/Package.yml
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
name: Package
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
python_version:
|
||||
description: 'Python version.'
|
||||
required: false
|
||||
default: '3.10'
|
||||
type: string
|
||||
requirements:
|
||||
description: 'Python dependencies to be installed through pip.'
|
||||
required: false
|
||||
default: 'wheel'
|
||||
type: string
|
||||
artifact:
|
||||
description: 'Name of the package artifact.'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
|
||||
Package:
|
||||
name: 📦 Package in Source and Wheel Format
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: ⏬ Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: 🐍 Setup Python ${{ inputs.python_version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ inputs.python_version }}
|
||||
|
||||
- name: 🔧 Install dependencies for packaging and release
|
||||
run: |
|
||||
python -m pip install -U pip
|
||||
python -m pip install ${{ inputs.requirements }}
|
||||
|
||||
- name: 🔨 Build Python package (source distribution)
|
||||
run: python setup.py sdist
|
||||
|
||||
- name: 🔨 Build Python package (binary distribution - wheel)
|
||||
run: python setup.py bdist_wheel
|
||||
|
||||
- name: 📤 Upload wheel artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ inputs.artifact }}
|
||||
path: dist/
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
69
.github/workflows/Params.yml
vendored
Normal file
69
.github/workflows/Params.yml
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
name: Params
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
python_version:
|
||||
description: 'Python version.'
|
||||
required: false
|
||||
default: '3.10'
|
||||
type: string
|
||||
python_version_list:
|
||||
description: 'Space separated list of Python versions to run tests with.'
|
||||
required: false
|
||||
default: '3.6 3.7 3.8 3.9 3.10'
|
||||
type: string
|
||||
name:
|
||||
description: 'Name of the tool.'
|
||||
required: true
|
||||
type: string
|
||||
outputs:
|
||||
params:
|
||||
description: "Parameters to be used in other jobs."
|
||||
value: ${{ jobs.Params.outputs.params }}
|
||||
python_jobs:
|
||||
description: "List of Python versions to be used in the matrix of other jobs."
|
||||
value: ${{ jobs.Params.outputs.python_jobs }}
|
||||
|
||||
jobs:
|
||||
|
||||
Params:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
params: ${{ steps.params.outputs.params }}
|
||||
python_jobs: ${{ steps.params.outputs.python_jobs }}
|
||||
steps:
|
||||
|
||||
- name: Generate 'params' and 'python_jobs'
|
||||
id: params
|
||||
shell: python
|
||||
run: |
|
||||
name = '${{ inputs.name }}'
|
||||
params = {
|
||||
'python_version': '${{ inputs.python_version }}',
|
||||
'artifacts': {
|
||||
'unittesting': f'{name}-TestReport',
|
||||
'coverage': f'{name}-coverage',
|
||||
'typing': f'{name}-typing',
|
||||
'package': f'{name}-package',
|
||||
'doc': f'{name}-doc',
|
||||
}
|
||||
}
|
||||
print(f'::set-output name=params::{params!s}')
|
||||
print("Params:")
|
||||
print(params)
|
||||
|
||||
data = {
|
||||
'3.6': { 'icon': '🔴', 'until': '23.12.2021' },
|
||||
'3.7': { 'icon': '🟠', 'until': '27.06.2023' },
|
||||
'3.8': { 'icon': '🟡', 'until': 'Oct. 2024' },
|
||||
'3.9': { 'icon': '🟢', 'until': 'Oct. 2025' },
|
||||
'3.10': { 'icon': '🟢', 'until': 'Oct. 2026' },
|
||||
}
|
||||
jobs = [
|
||||
{'python': version, 'icon': data[version]['icon']}
|
||||
for version in '${{ inputs.python_version_list }}'.split(' ')
|
||||
]
|
||||
print(f'::set-output name=python_jobs::{jobs!s}')
|
||||
print("Python jobs:")
|
||||
print(jobs)
|
||||
63
.github/workflows/PublishOnPyPI.yml
vendored
Normal file
63
.github/workflows/PublishOnPyPI.yml
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
name: Publish on PyPI
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
python_version:
|
||||
description: 'Python version.'
|
||||
required: false
|
||||
default: '3.10'
|
||||
type: string
|
||||
requirements:
|
||||
description: 'Python dependencies to be installed through pip.'
|
||||
required: false
|
||||
default: 'wheel twine'
|
||||
type: string
|
||||
artifact:
|
||||
description: 'Name of the package artifact.'
|
||||
required: true
|
||||
type: string
|
||||
secrets:
|
||||
PYPI_TOKEN:
|
||||
description: "Token for pushing releases to PyPI"
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
|
||||
PublishOnPyPI:
|
||||
name: 🚀 Publish to PyPI
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: 📥 Download artifacts '${{ inputs.artifact }}' from 'Package' job
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: ${{ inputs.artifact }}
|
||||
path: dist/
|
||||
|
||||
- name: 🐍 Setup Python ${{ inputs.python_version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ inputs.python_version }}
|
||||
|
||||
- name: ⚙ Install dependencies for packaging and release
|
||||
run: |
|
||||
python -m pip install -U pip
|
||||
python -m pip install ${{ inputs.requirements }}
|
||||
|
||||
- name: ⤴ Release Python source package to PyPI
|
||||
env:
|
||||
TWINE_USERNAME: __token__
|
||||
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
|
||||
run: twine upload dist/*.tar.gz
|
||||
|
||||
- name: ⤴ Release Python wheel package to PyPI
|
||||
env:
|
||||
TWINE_USERNAME: __token__
|
||||
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
|
||||
run: twine upload dist/*.whl
|
||||
|
||||
- name: 🗑️ Delete packaging Artifacts
|
||||
uses: geekyeggo/delete-artifact@v1
|
||||
with:
|
||||
name: ${{ inputs.artifact }}
|
||||
62
.github/workflows/PublishToGitHubPages.yml
vendored
Normal file
62
.github/workflows/PublishToGitHubPages.yml
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
name: Publish to GitHub Pages
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
doc:
|
||||
description: 'Name of the documentation artifact.'
|
||||
required: true
|
||||
type: string
|
||||
coverage:
|
||||
description: 'Name of the coverage artifact.'
|
||||
required: false
|
||||
default: ''
|
||||
type: string
|
||||
typing:
|
||||
description: 'Name of the typing artifact.'
|
||||
required: false
|
||||
default: ''
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
|
||||
PublishToGitHubPages:
|
||||
name: 📚 Publish to GH-Pages
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: ⏬ Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: 📥 Download artifacts '${{ inputs.doc }}' from 'BuildTheDocs' job
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: ${{ inputs.doc }}
|
||||
path: public
|
||||
|
||||
- name: 📥 Download artifacts '${{ inputs.coverage }}' from 'Coverage' job
|
||||
if: ${{ inputs.coverage != '' }}
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: ${{ inputs.coverage }}
|
||||
path: public/coverage
|
||||
|
||||
- name: 📥 Download artifacts '${{ inputs.typing }}' from 'StaticTypeCheck' job
|
||||
if: ${{ inputs.typing != '' }}
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: ${{ inputs.typing }}
|
||||
path: public/typing
|
||||
|
||||
- name: '📓 Publish site to GitHub Pages'
|
||||
if: github.event_name != 'pull_request'
|
||||
run: |
|
||||
cd public
|
||||
touch .nojekyll
|
||||
git init
|
||||
cp ../.git/config ./.git/config
|
||||
git add .
|
||||
git config --local user.email "BuildTheDocs@GitHubActions"
|
||||
git config --local user.name "GitHub Actions"
|
||||
git commit -a -m "update ${{ github.sha }}"
|
||||
git push -u origin +HEAD:gh-pages
|
||||
44
.github/workflows/Release.yml
vendored
Normal file
44
.github/workflows/Release.yml
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
|
||||
Release:
|
||||
name: 📝 Create 'Release Page' on GitHub
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: 🔁 Extract Git tag from GITHUB_REF
|
||||
id: getVariables
|
||||
run: |
|
||||
GIT_TAG=${GITHUB_REF#refs/*/}
|
||||
RELEASE_VERSION=${GIT_TAG#v}
|
||||
RELEASE_DATETIME="$(date --utc '+%d.%m.%Y - %H:%M:%S')"
|
||||
# write to step outputs
|
||||
echo ::set-output name=gitTag::${GIT_TAG}
|
||||
echo ::set-output name=version::${RELEASE_VERSION}
|
||||
echo ::set-output name=datetime::${RELEASE_DATETIME}
|
||||
|
||||
- name: 📑 Create Release Page
|
||||
id: createReleasePage
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
with:
|
||||
tag_name: ${{ steps.getVariables.outputs.gitTag }}
|
||||
# release_name: ${{ steps.getVariables.outputs.gitTag }}
|
||||
body: |
|
||||
**Automated Release created on: ${{ steps.getVariables.outputs.datetime }}**
|
||||
|
||||
# New Features
|
||||
* tbd
|
||||
|
||||
# Changes
|
||||
* tbd
|
||||
|
||||
# Bug Fixes
|
||||
* tbd
|
||||
draft: false
|
||||
prerelease: false
|
||||
62
.github/workflows/StaticTypeCheck.yml
vendored
Normal file
62
.github/workflows/StaticTypeCheck.yml
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
name: Static Type Check
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
python_version:
|
||||
description: 'Python version.'
|
||||
required: false
|
||||
default: '3.10'
|
||||
type: string
|
||||
requirements:
|
||||
description: 'Python dependencies to be installed through pip.'
|
||||
required: false
|
||||
default: '-r tests/requirements.txt'
|
||||
type: string
|
||||
report:
|
||||
description: 'Directory to upload as an artifact.'
|
||||
required: false
|
||||
default: 'htmlmypy'
|
||||
type: string
|
||||
commands:
|
||||
description: 'Commands to run the static type checks.'
|
||||
required: true
|
||||
type: string
|
||||
artifact:
|
||||
description: 'Name of the typing artifact.'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
|
||||
StaticTypeCheck:
|
||||
name: 👀 Check Static Typing using Python ${{ inputs.python_version }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: ⏬ Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: 🐍 Setup Python ${{ inputs.python_version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ inputs.python_version }}
|
||||
|
||||
- name: 🗂 Install dependencies
|
||||
run: |
|
||||
python -m pip install -U pip
|
||||
python -m pip install ${{ inputs.requirements }}
|
||||
|
||||
- name: Check Static Typing
|
||||
continue-on-error: true
|
||||
run: ${{ inputs.commands }}
|
||||
|
||||
- name: 📤 Upload 'Static Typing Report' artifact
|
||||
if: ${{ inputs.artifact != '' }}
|
||||
continue-on-error: true
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ inputs.artifact }}
|
||||
path: ${{ inputs.report }}
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
58
.github/workflows/UnitTesting.yml
vendored
Normal file
58
.github/workflows/UnitTesting.yml
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
name: Unit Testing
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
jobs:
|
||||
description: 'Space separated list of Python versions to run tests with.'
|
||||
required: true
|
||||
type: string
|
||||
requirements:
|
||||
description: 'Python dependencies to be installed through pip.'
|
||||
required: false
|
||||
default: '-r tests/requirements.txt'
|
||||
type: string
|
||||
artifact:
|
||||
description: "Generate unit test report with junitxml and upload results as an artifact."
|
||||
required: false
|
||||
default: ''
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
|
||||
UnitTesting:
|
||||
name: ${{ matrix.icon }} Unit Tests using Python ${{ matrix.python }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include: ${{ fromJson(inputs.jobs) }}
|
||||
|
||||
steps:
|
||||
- name: ⏬ Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: 🐍 Setup Python ${{ matrix.python }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: 🔧 Install dependencies
|
||||
run: |
|
||||
python -m pip install -U pip
|
||||
python -m pip install ${{ inputs.requirements }}
|
||||
|
||||
- name: ☑ Run unit tests
|
||||
run: |
|
||||
[ 'x${{ inputs.artifact }}' != 'x' ] && PYTEST_ARGS='--junitxml=TestReport.xml' || unset PYTEST_ARGS
|
||||
python -m pytest -rA tests/unit $PYTEST_ARGS --color=yes
|
||||
|
||||
- name: 📤 Upload 'TestReport.xml' artifact
|
||||
if: inputs.TestReport == 'true'
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ inputs.artifact }}-${{ matrix.python }}
|
||||
path: TestReport.xml
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
56
.github/workflows/VerifyDocs.yml
vendored
Normal file
56
.github/workflows/VerifyDocs.yml
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
name: Verify examples
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
python_version:
|
||||
description: 'Python version.'
|
||||
required: false
|
||||
default: '3.10'
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
|
||||
VerifyDocs:
|
||||
name: 👍 Verify example snippets using Python ${{ inputs.python_version }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: ⏬ Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: 🐍 Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ inputs.python_version }}
|
||||
|
||||
- name: 🐍 Install dependencies
|
||||
run: |
|
||||
pip3 install .
|
||||
|
||||
- name: ✂ Extract code snippet from README
|
||||
shell: python
|
||||
run: |
|
||||
from pathlib import Path
|
||||
import re
|
||||
|
||||
ROOT = Path('.')
|
||||
|
||||
with (ROOT / 'README.md').open('r') as rptr:
|
||||
content = rptr.read()
|
||||
|
||||
m = re.search(r"```py(thon)?(?P<code>.*?)```", content, re.MULTILINE|re.DOTALL)
|
||||
|
||||
if m is None:
|
||||
raise Exception("Regular expression did not find the example in the README!")
|
||||
|
||||
with (ROOT / 'tests/docs/example.py').open('w') as wptr:
|
||||
wptr.write(m["code"])
|
||||
|
||||
- name: Print example.py
|
||||
run: cat tests/docs/example.py
|
||||
|
||||
- name: ☑ Run example snippet
|
||||
working-directory: tests/docs
|
||||
run: |
|
||||
python3 example.py
|
||||
BIN
ExamplePipeline.png
Normal file
BIN
ExamplePipeline.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 74 KiB |
130
ExamplePipeline.yml
Normal file
130
ExamplePipeline.yml
Normal file
@@ -0,0 +1,130 @@
|
||||
name: Unit Testing, Coverage Collection, Package, Release, Documentation and Publish
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
||||
# This job is a workaround for global variables
|
||||
# See https://github.com/actions/runner/issues/480
|
||||
Params:
|
||||
uses: pyTooling/Actions/.github/workflows/Params.yml@main
|
||||
with:
|
||||
name: ToolName
|
||||
# Optional
|
||||
python_version: '3.10'
|
||||
python_version_list: '3.8 3.9 3.10'
|
||||
|
||||
UnitTesting:
|
||||
uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@main
|
||||
needs:
|
||||
- Params
|
||||
with:
|
||||
jobs: ${{ needs.Params.outputs.python_jobs }}
|
||||
# Optional
|
||||
requirements: '-r tests/requirements.txt'
|
||||
artifact: ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}
|
||||
|
||||
Coverage:
|
||||
uses: pyTooling/Actions/.github/workflows/CoverageCollection.yml@main
|
||||
needs:
|
||||
- Params
|
||||
with:
|
||||
artifact: ${{ fromJson(needs.Params.outputs.params).artifacts.coverage }}
|
||||
# Optional
|
||||
python_version: ${{ fromJson(needs.Params.outputs.params).python_version }}
|
||||
requirements: '-r tests/requirements.txt'
|
||||
secrets:
|
||||
codacy_token: ${{ secrets.CODACY_PROJECT_TOKEN }}
|
||||
|
||||
StaticTypeCheck:
|
||||
uses: pyTooling/Actions/.github/workflows/StaticTypeCheck.yml@main
|
||||
needs:
|
||||
- Params
|
||||
with:
|
||||
commands: mypy --html-report htmlmypy -p ToolName
|
||||
artifact: ${{ fromJson(needs.Params.outputs.params).artifacts.typing }}
|
||||
# Optional
|
||||
python_version: ${{ fromJson(needs.Params.outputs.params).python_version }}
|
||||
requirements: '-r tests/requirements.txt'
|
||||
report: 'htmlmypy'
|
||||
|
||||
Release:
|
||||
uses: pyTooling/Actions/.github/workflows/Release.yml@main
|
||||
if: startsWith(github.ref, 'refs/tags')
|
||||
needs:
|
||||
- UnitTesting
|
||||
- Coverage
|
||||
- StaticTypeCheck
|
||||
|
||||
Package:
|
||||
uses: pyTooling/Actions/.github/workflows/Package.yml@main
|
||||
if: startsWith(github.ref, 'refs/tags')
|
||||
needs:
|
||||
- Params
|
||||
- Coverage
|
||||
with:
|
||||
artifact: ${{ fromJson(needs.Params.outputs.params).artifacts.package }}
|
||||
# Optional
|
||||
python_version: ${{ fromJson(needs.Params.outputs.params).python_version }}
|
||||
requirements: 'wheel'
|
||||
|
||||
PublishOnPyPI:
|
||||
uses: pyTooling/Actions/.github/workflows/PublishOnPyPI.yml@main
|
||||
if: startsWith(github.ref, 'refs/tags')
|
||||
needs:
|
||||
- Params
|
||||
- Release
|
||||
- Package
|
||||
with:
|
||||
artifact: ${{ fromJson(needs.Params.outputs.params).artifacts.package }}
|
||||
# Optional
|
||||
python_version: ${{ fromJson(needs.Params.outputs.params).python_version }}
|
||||
requirements: 'wheel twine'
|
||||
secrets:
|
||||
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
|
||||
|
||||
VerifyDocs:
|
||||
uses: pyTooling/Actions/.github/workflows/VerifyDocs.yml@main
|
||||
needs:
|
||||
- Params
|
||||
with:
|
||||
# Optional
|
||||
python_version: ${{ fromJson(needs.Params.outputs.params).python_version }}
|
||||
|
||||
BuildTheDocs:
|
||||
uses: pyTooling/Actions/.github/workflows/BuildTheDocs.yml@main
|
||||
needs:
|
||||
- Params
|
||||
- VerifyDocs
|
||||
with:
|
||||
artifact: ${{ fromJson(needs.Params.outputs.params).artifacts.doc }}
|
||||
|
||||
PublishToGitHubPages:
|
||||
uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@main
|
||||
needs:
|
||||
- Params
|
||||
- BuildTheDocs
|
||||
- Coverage
|
||||
- StaticTypeCheck
|
||||
with:
|
||||
doc: ${{ fromJson(needs.Params.outputs.params).artifacts.doc }}
|
||||
# Optional
|
||||
coverage: ${{ fromJson(needs.Params.outputs.params).artifacts.coverage }}
|
||||
typing: ${{ fromJson(needs.Params.outputs.params).artifacts.typing }}
|
||||
|
||||
ArtifactCleanUp:
|
||||
uses: pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml@main
|
||||
needs:
|
||||
- Params
|
||||
- Coverage
|
||||
- StaticTypeCheck
|
||||
- BuildTheDocs
|
||||
- PublishToGitHubPages
|
||||
with:
|
||||
package: ${{ fromJson(needs.Params.outputs.params).artifacts.package }}
|
||||
remaining: |
|
||||
${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-*
|
||||
${{ fromJson(needs.Params.outputs.params).artifacts.coverage }}
|
||||
${{ fromJson(needs.Params.outputs.params).artifacts.typing }}
|
||||
${{ fromJson(needs.Params.outputs.params).artifacts.doc }}
|
||||
146
README.md
146
README.md
@@ -1,3 +1,149 @@
|
||||
# Actions
|
||||
|
||||
Reusable steps and workflows for GitHub Actions, focused on Python packages.
|
||||
|
||||
GitHub Actions workflows, actions and documentation are mostly focused on JavaScript/TypeScript as the scripting
|
||||
language for writing reusable CI code.
|
||||
However, Python being equally popular and capable, usage of JS/TS might be bypassed, with some caveats.
|
||||
This repository gathers reusable CI tooling for testing, packaging and distributing Python projects and documentation.
|
||||
|
||||
## Context
|
||||
|
||||
GitHub Actions supports four types of reusable code:
|
||||
|
||||
- JavaScript Action.
|
||||
- [docs.github.com: actions/creating-actions/creating-a-javascript-action](https://docs.github.com/en/actions/creating-actions/creating-a-javascript-action)
|
||||
- Container Action.
|
||||
- [docs.github.com: actions/creating-actions/creating-a-docker-container-action](https://docs.github.com/en/actions/creating-actions/creating-a-docker-container-action)
|
||||
- Composite Action.
|
||||
- [docs.github.com: actions/creating-actions/creating-a-composite-action](https://docs.github.com/en/actions/creating-actions/creating-a-composite-action)
|
||||
- [github.blog/changelog: 2020-08-07-github-actions-composite-run-steps](https://github.blog/changelog/2020-08-07-github-actions-composite-run-steps/)
|
||||
- [github.blog/changelog: 2021-08-25-github-actions-reduce-duplication-with-action-compositio](https://github.blog/changelog/2021-08-25-github-actions-reduce-duplication-with-action-composition/)
|
||||
- Reusable Workflows.
|
||||
- [docs.github.com: actions/learn-github-actions/reusing-workflows](https://docs.github.com/en/actions/learn-github-actions/reusing-workflows)
|
||||
- [github.blog/changelog: 2021-10-05-github-actions-dry-your-github-actions-configuration-by-reusing-workflows](https://github.blog/changelog/2021-10-05-github-actions-dry-your-github-actions-configuration-by-reusing-workflows/)
|
||||
|
||||
Leaving JavaScript and Container Actions aside, the main differences between Composite Actions and Reusable Workflows
|
||||
are the following:
|
||||
|
||||
- Composite Actions can be executed from a remote/external path or from the checked out branch, and from any location.
|
||||
However, Reusable Workflows can only be used through a remote/external path (`{owner}/{repo}/{path}/{filename}@{ref}`),
|
||||
where `{path}` must be `.github/workflows`, and `@{ref}` is required.
|
||||
See [actions/runner#1493](https://github.com/actions/runner/issues/1493).
|
||||
As a result:
|
||||
- Local Composite Actions cannot be used without a prior repo checkout, but Reusable Workflows can be used without
|
||||
checkout.
|
||||
- Testing development versions of local Reusable Workflows is cumbersome, because PRs do not pick the modifications by
|
||||
default.
|
||||
- Composite Actions can include multiple steps, but not multiple jobs.
|
||||
Conversely, Reusable Workflows can include multiple jobs, and multiple steps in each job.
|
||||
- Composite Actions can include multiple files, so it's possible to use files from the Action or from the user's repository.
|
||||
Conversely, Reusable Workflows are a single YAML file, with no additional files retrieved by default.
|
||||
|
||||
### Callable vs dispatchable workflows
|
||||
|
||||
Reusable Workflows are defined through the `workflow_call` event kind.
|
||||
Similarly, any "regular" Workflow can be triggered through a `workflow_dispatch` event.
|
||||
Both event kinds support `input` options, which are usable within the Workflow.
|
||||
Therefore, one might intuitively try to write a workflow which is both callable and dispatchable.
|
||||
In other words, which can be either reused from another workflow, or triggered through the API.
|
||||
Unfortunately, that is not the case.
|
||||
Although `input` options can be duplicated for both events, GitHub's backend exposes them through different objects.
|
||||
In dispatchable Workflows, the object is `${{ github.event.inputs }}`, while callable workflows receive `${{ inputs }}`.
|
||||
|
||||
As a result, in order to make a reusable workflow dispatchable, a wrapper workflow is required.
|
||||
See, for instance, [hdl/containers: .github/workflows/common.yml](https://github.com/hdl/containers/blob/main/.github/workflows/common.yml) and [hdl/containers: .github/workflows/dispatch.yml](https://github.com/hdl/containers/blob/main/.github/workflows/dispatch.yml).
|
||||
Alternatively, a normalisation job might be used, similar to the `Params` in this repo.
|
||||
|
||||
### Call hierarchy
|
||||
|
||||
Reusable Workflows cannot call other Reusable Workflows, however, they can use Composite Actions and Composite Actions
|
||||
can call other Actions.
|
||||
Therefore, in some use cases it is sensible to combine one layer of reusable workflows for orchestrating the jobs, along
|
||||
with multiple layers of composite actions.
|
||||
|
||||
### Script with post step
|
||||
|
||||
JavaScript Actions support defining `pre`, `pre-if`, `post` and `post-if` steps, which allow executing steps at the
|
||||
beginning or the end of a job, regardless of intermediate steps failing.
|
||||
Unfortunately, those are not available for any other Action type.
|
||||
|
||||
Action [with-post-step](with-post-step) is a generic JS Action to execute a main command and to set a command as a post
|
||||
step.
|
||||
It allows using the `post` feature with scripts written in bash, python or any other interpreted language available on
|
||||
the environment.
|
||||
See: [actions/runner#1478](https://github.com/actions/runner/issues/1478).
|
||||
|
||||
## Reusable workflows
|
||||
|
||||
This repository provides 10+ Reusable Workflows based on the CI pipelines of the repos in this organisation,
|
||||
[EDA²](https://github.com/edaa-org), [VHDL](https://github.com/vhdl), and others.
|
||||
By combining them, Python packages can be continuously tested and released along with Sphinx documentation sites, to GitHub Releases, GitHub Pages and PyPI.
|
||||
Optionally, coverage and static type check reports can be gathered.
|
||||
|
||||
[](ExamplePipeline.png)
|
||||
|
||||
As shown in the screenshot above, the expected order is:
|
||||
|
||||
- Global:
|
||||
- [Params](.github/workflows/Params.yml): a workaround for the limitations to handle global variables in
|
||||
GitHub Actions workflows (see [actions/runner#480](https://github.com/actions/runner/issues/480)).
|
||||
It generates outputs with artifact names and job matrices to be used in other jobs.
|
||||
- Code testing/analysis:
|
||||
- [UnitTesting](.github/workflows/UnitTesting.yml): run unit test with `pytest` using multiple versions of Python, and
|
||||
optionally upload results as XML reports.
|
||||
- [CoverageCollection](.github/workflows/CoverageCollection.yml): collect coverage data with `pytest` using a single
|
||||
version of Python, generate HTML and Cobertura (XML) reports, upload the HTML report as an artifact, and upload the
|
||||
results to Codecov and Codacy.
|
||||
- [StaticTypeCheck](.github/workflows/StaticTypeCheck.yml): collect static type check result with `mypy`, and
|
||||
optionally upload results as an HTML report.
|
||||
Example `commands`:
|
||||
|
||||
1. Regular package
|
||||
|
||||
```yml
|
||||
commands: mypy --html-report htmlmypy -p ToolName
|
||||
```
|
||||
|
||||
2. Parent namespace package
|
||||
|
||||
```yml
|
||||
commands: |
|
||||
touch Parent/__init__.py
|
||||
mypy --html-report htmlmypy -p ToolName
|
||||
```
|
||||
|
||||
3. Child namespace package
|
||||
|
||||
```yml
|
||||
commands: |
|
||||
cd Parent
|
||||
mypy --html-report ../htmlmypy -p ToolName
|
||||
```
|
||||
|
||||
- [VerifyDocs](.github/workflows/VerifyDocs.yml): extract code examples from the README and test.
|
||||
- Packaging and releasing:
|
||||
- [Release](.github/workflows/Release.yml): publish GitHub Release.
|
||||
- [Package](.github/workflows/Package.yml): generate source and wheel packages, and upload them as an artifact.
|
||||
- [PublishOnPyPI](.github/workflows/PublishOnPyPI.yml): publish source and wheel packages to PyPI.
|
||||
- Documentation:
|
||||
- [BuildTheDocs](.github/workflows/BuildTheDocs.yml): build Sphinx documentation with BuildTheDocs, and upload HTML as
|
||||
an artifact.
|
||||
- [PublishToGitHubPages](.github/workflows/PublishToGitHubPages.yml): publish HTML documentation to GitHub Pages.
|
||||
- Cleanup:
|
||||
- [ArtifactCleanUp](.github/workflows/ArtifactCleanUp.yml): delete artifacts.
|
||||
|
||||
### Example pipeline
|
||||
|
||||
[ExamplePipeline.yml](ExamplePipeline.yml) is an example Workflow which uses all of the Reusable Workflows.
|
||||
Python package/tool developers can copy it into their repos, in order to use al the reusable workflows straightaway.
|
||||
Minimal required modifications are the following:
|
||||
|
||||
- Set the `name` input of job `Params`.
|
||||
- Specify the `commands` input of job `StaticTypeCheck`.
|
||||
|
||||
Find further usage cases in the following list of projects:
|
||||
|
||||
- [edaa-org/pyEDAA.ProjectModel](https://github.com/edaa-org/pyEDAA.ProjectModel/tree/main/.github/workflows)
|
||||
- [edaa-org/pySVModel](https://github.com/edaa-org/pySVModel/tree/main/.github/workflows)
|
||||
- [VHDL/pyVHDLModel](https://github.com/VHDL/pyVHDLModel/tree/main/.github/workflows)
|
||||
|
||||
17
with-post-step/action.yml
Normal file
17
with-post-step/action.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
name: With post step
|
||||
description: 'Generic JS Action to execute a main command and set a command as a post step.'
|
||||
inputs:
|
||||
main:
|
||||
description: 'Main command/script.'
|
||||
required: true
|
||||
post:
|
||||
description: 'Post command/script.'
|
||||
required: true
|
||||
key:
|
||||
description: 'Name of the state variable used to detect the post step.'
|
||||
required: false
|
||||
default: POST
|
||||
runs:
|
||||
using: 'node12'
|
||||
main: 'main.js'
|
||||
post: 'main.js'
|
||||
44
with-post-step/main.js
Normal file
44
with-post-step/main.js
Normal file
@@ -0,0 +1,44 @@
|
||||
// Authors:
|
||||
// Unai Martinez-Corral
|
||||
//
|
||||
// Copyright 2021 Unai Martinez-Corral <unai.martinezcorral@ehu.eus>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Context:
|
||||
// * https://github.com/docker/login-action/issues/72
|
||||
// * https://github.com/actions/runner/issues/1478
|
||||
|
||||
const { exec } = require("child_process");
|
||||
|
||||
function run(cmd) {
|
||||
exec(cmd, (error, stdout, stderr) => {
|
||||
if ( stdout.length !== 0 ) { console.log(`${stdout}`); }
|
||||
if ( stderr.length !== 0 ) { console.error(`${stderr}`); }
|
||||
if (error) {
|
||||
process.exitCode = error.code;
|
||||
console.error(`${error}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const key = process.env.INPUT_KEY.toUpperCase();
|
||||
|
||||
if ( process.env[`STATE_${key}`] !== undefined ) { // Are we in the 'post' step?
|
||||
run(process.env.INPUT_POST);
|
||||
} else { // Otherwise, this is the main step
|
||||
console.log(`::save-state name=${key}::true`);
|
||||
run(process.env.INPUT_MAIN);
|
||||
}
|
||||
Reference in New Issue
Block a user