mirror of
https://github.com/pyTooling/Actions.git
synced 2026-02-14 12:06:56 +08:00
Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0ad308283 | ||
|
|
562b28ee34 | ||
|
|
8cfda1f21a | ||
|
|
457870d760 | ||
|
|
df3d45363b | ||
|
|
6ba0204549 | ||
|
|
7a7976677e | ||
|
|
4579381b78 | ||
|
|
14ac6c6386 | ||
|
|
18357ec213 | ||
|
|
4220a50041 | ||
|
|
d8264eab8a | ||
|
|
b9d3839abb | ||
|
|
997d548e60 | ||
|
|
83cd572694 | ||
|
|
68a446b9b6 | ||
|
|
43f0b79e88 | ||
|
|
b3d8a9c5ec | ||
|
|
edb6ca364e | ||
|
|
e00f5cf53d | ||
|
|
0da8c5a5c5 | ||
|
|
c9bee6fe65 | ||
|
|
94bb01d586 | ||
|
|
0fdef33cb4 | ||
|
|
e1f7599d79 | ||
|
|
dad5e71bfe | ||
|
|
60d77c2292 | ||
|
|
3f489f0bed | ||
|
|
26afa43fa4 | ||
|
|
c8003f1a0e | ||
|
|
6413469cdf | ||
|
|
8dbacda32c | ||
|
|
78b225195f | ||
|
|
1fbeef36d6 | ||
|
|
9846c9e60c | ||
|
|
b8564eb389 | ||
|
|
62cd2d1d0f | ||
|
|
9bd8004dfb | ||
|
|
925b44a8a8 | ||
|
|
9d8c1ecc05 | ||
|
|
66c7b4b619 | ||
|
|
b399aa8f93 | ||
|
|
dcd0a4b617 | ||
|
|
d7c765ba79 | ||
|
|
fa10ed076c | ||
|
|
9dfafd588e | ||
|
|
09f7504de4 | ||
|
|
6ad23eabf5 | ||
|
|
bb855d572d | ||
|
|
250cceb80d | ||
|
|
615aafc0b4 | ||
|
|
1f3d12ef95 | ||
|
|
608670c6b9 | ||
|
|
9cee3342e3 | ||
|
|
aec2613cd1 | ||
|
|
7c406d96e7 | ||
|
|
459faf880a | ||
|
|
e832625624 | ||
|
|
ee02a39a5e | ||
|
|
52491e6bcc | ||
|
|
4177a535f1 | ||
|
|
548437b824 | ||
|
|
596d0d774f | ||
|
|
877928ba4a | ||
|
|
a06d90f456 | ||
|
|
dcaff1e5a1 | ||
|
|
4d666520a0 | ||
|
|
cea369d703 | ||
|
|
08a19429d4 |
2
.github/workflows/ArtifactCleanUp.yml
vendored
2
.github/workflows/ArtifactCleanUp.yml
vendored
@@ -4,7 +4,7 @@
|
||||
# Unai Martinez-Corral #
|
||||
# #
|
||||
# ==================================================================================================================== #
|
||||
# Copyright 2020-2021 The pyTooling Authors #
|
||||
# Copyright 2020-2022 The pyTooling Authors #
|
||||
# #
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); #
|
||||
# you may not use this file except in compliance with the License. #
|
||||
|
||||
2
.github/workflows/BuildTheDocs.yml
vendored
2
.github/workflows/BuildTheDocs.yml
vendored
@@ -4,7 +4,7 @@
|
||||
# Unai Martinez-Corral #
|
||||
# #
|
||||
# ==================================================================================================================== #
|
||||
# Copyright 2020-2021 The pyTooling Authors #
|
||||
# Copyright 2020-2022 The pyTooling Authors #
|
||||
# #
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); #
|
||||
# you may not use this file except in compliance with the License. #
|
||||
|
||||
65
.github/workflows/CoverageCollection.yml
vendored
65
.github/workflows/CoverageCollection.yml
vendored
@@ -4,7 +4,7 @@
|
||||
# Unai Martinez-Corral #
|
||||
# #
|
||||
# ==================================================================================================================== #
|
||||
# Copyright 2020-2021 The pyTooling Authors #
|
||||
# Copyright 2020-2022 The pyTooling Authors #
|
||||
# #
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); #
|
||||
# you may not use this file except in compliance with the License. #
|
||||
@@ -35,6 +35,16 @@ on:
|
||||
required: false
|
||||
default: '-r tests/requirements.txt'
|
||||
type: string
|
||||
unittest_directory:
|
||||
description: 'Path to the directory containing unit tests.'
|
||||
required: false
|
||||
default: 'tests/unit'
|
||||
type: string
|
||||
coverage_config:
|
||||
description: 'Path to the .coveragerc file. Use pyproject.toml by default.'
|
||||
required: false
|
||||
default: 'pyproject.toml'
|
||||
type: string
|
||||
artifact:
|
||||
description: 'Name of the coverage artifact.'
|
||||
required: true
|
||||
@@ -62,27 +72,68 @@ jobs:
|
||||
- name: 🗂 Install dependencies
|
||||
run: |
|
||||
python -m pip install -U pip
|
||||
python -m pip install tomli
|
||||
python -m pip install ${{ inputs.requirements }}
|
||||
|
||||
- name: 🔁 Extract configurations from pyproject.toml
|
||||
id: getVariables
|
||||
shell: python
|
||||
run: |
|
||||
from pathlib import Path
|
||||
from tomli import load as tomli_load
|
||||
|
||||
htmlDirectory = 'htmlcov'
|
||||
xmlFile = './coverage.xml'
|
||||
coverageRC = "${{ inputs.coverage_config }}".strip()
|
||||
|
||||
# Read output paths from 'pyproject.toml' file
|
||||
if coverageRC == "pyproject.toml":
|
||||
pyProjectFile = Path("pyproject.toml")
|
||||
if pyProjectFile.exists():
|
||||
with pyProjectFile.open("rb") as file:
|
||||
pyProjectSettings = tomli_load(file)
|
||||
|
||||
htmlDirectory = pyProjectSettings["tool"]["coverage"]["html"]["directory"]
|
||||
xmlFile = pyProjectSettings["tool"]["coverage"]["xml"]["output"]
|
||||
else:
|
||||
print(f"File '{pyProjectFile}' not found and no ' .coveragerc' file specified.")
|
||||
|
||||
# Read output paths from '.coveragerc' file
|
||||
elif len(coverageRC) > 0:
|
||||
coverageRCFile = Path(coverageRC)
|
||||
if coverageRCFile.exists():
|
||||
with coverageRCFile.open("rb") as file:
|
||||
coverageRCSettings = tomli_load(file)
|
||||
|
||||
htmlDirectory = coverageRCSettings["html"]["directory"]
|
||||
xmlFile = coverageRCSettings["xml"]["output"]
|
||||
else:
|
||||
print(f"File '{coverageRCFile}' not found.")
|
||||
|
||||
print(f"::set-output name=coverage_report_html_directory::{htmlDirectory}")
|
||||
print(f"::set-output name=coverage_report_xml::{xmlFile}")
|
||||
print(f"DEBUG:\n html={htmlDirectory}\n xml={xmlFile}")
|
||||
|
||||
- name: Collect coverage
|
||||
continue-on-error: true
|
||||
run: |
|
||||
python -m pytest -rA --cov=.. --cov-config=tests/.coveragerc tests/unit --color=yes
|
||||
[ 'x${{ inputs.coverage_config }}' != 'x' ] && PYCOV_ARGS='--cov-config=${{ inputs.coverage_config }}' || unset PYCOV_ARGS
|
||||
python -m pytest -rA --cov=. $PYCOV_ARGS ${{ inputs.unittest_directory }} --color=yes
|
||||
|
||||
- name: Convert to cobertura format
|
||||
run: coverage xml
|
||||
|
||||
- name: Convert to HTML format
|
||||
run: |
|
||||
coverage html
|
||||
rm htmlcov/.gitignore
|
||||
coverage html -d ${{ steps.getVariables.outputs.coverage_report_html_directory }}
|
||||
rm ${{ steps.getVariables.outputs.coverage_report_html_directory }}/.gitignore
|
||||
|
||||
- name: 📤 Upload 'Coverage Report' artifact
|
||||
continue-on-error: true
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ inputs.artifact }}
|
||||
path: htmlcov
|
||||
path: ${{ steps.getVariables.outputs.coverage_report_html_directory }}
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
@@ -90,7 +141,7 @@ jobs:
|
||||
continue-on-error: true
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
file: ./coverage.xml
|
||||
file: ${{ steps.getVariables.outputs.coverage_report_xml }}
|
||||
flags: unittests
|
||||
env_vars: PYTHON
|
||||
|
||||
@@ -99,4 +150,4 @@ jobs:
|
||||
uses: codacy/codacy-coverage-reporter-action@master
|
||||
with:
|
||||
project-token: ${{ secrets.codacy_token }}
|
||||
coverage-reports: ./coverage.xml
|
||||
coverage-reports: ${{ steps.getVariables.outputs.coverage_report_xml }}
|
||||
|
||||
22
.github/workflows/Package.yml
vendored
22
.github/workflows/Package.yml
vendored
@@ -4,7 +4,7 @@
|
||||
# Unai Martinez-Corral #
|
||||
# #
|
||||
# ==================================================================================================================== #
|
||||
# Copyright 2020-2021 The pyTooling Authors #
|
||||
# Copyright 2020-2022 The pyTooling Authors #
|
||||
# #
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); #
|
||||
# you may not use this file except in compliance with the License. #
|
||||
@@ -72,18 +72,32 @@ jobs:
|
||||
if: inputs.requirements == ''
|
||||
run: python -m build --wheel
|
||||
|
||||
# build (not isolated)
|
||||
|
||||
- name: 🔧 [build] Install dependencies for packaging and release
|
||||
if: inputs.requirements == 'no-isolation'
|
||||
run: python -m pip install build
|
||||
|
||||
- name: 🔨 [build] Build Python package (source distribution)
|
||||
if: inputs.requirements == 'no-isolation'
|
||||
run: python -m build --no-isolation --sdist
|
||||
|
||||
- name: 🔨 [build] Build Python package (binary distribution - wheel)
|
||||
if: inputs.requirements == 'no-isolation'
|
||||
run: python -m build --no-isolation --wheel
|
||||
|
||||
# setuptools
|
||||
|
||||
- name: 🔧 [setuptools] Install dependencies for packaging and release
|
||||
if: inputs.requirements != ''
|
||||
if: inputs.requirements != '' && inputs.requirements != 'no-isolation'
|
||||
run: python -m pip install ${{ inputs.requirements }}
|
||||
|
||||
- name: 🔨 [setuptools] Build Python package (source distribution)
|
||||
if: inputs.requirements != ''
|
||||
if: inputs.requirements != '' && inputs.requirements != 'no-isolation'
|
||||
run: python setup.py sdist
|
||||
|
||||
- name: 🔨 [setuptools] Build Python package (binary distribution - wheel)
|
||||
if: inputs.requirements != ''
|
||||
if: inputs.requirements != '' && inputs.requirements != 'no-isolation'
|
||||
run: python setup.py bdist_wheel
|
||||
|
||||
|
||||
|
||||
46
.github/workflows/Parameters.yml
vendored
46
.github/workflows/Parameters.yml
vendored
@@ -4,7 +4,7 @@
|
||||
# Unai Martinez-Corral #
|
||||
# #
|
||||
# ==================================================================================================================== #
|
||||
# Copyright 2020-2021 The pyTooling Authors #
|
||||
# Copyright 2020-2022 The pyTooling Authors #
|
||||
# #
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); #
|
||||
# you may not use this file except in compliance with the License. #
|
||||
@@ -33,7 +33,12 @@ on:
|
||||
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'
|
||||
default: '3.7 3.8 3.9 3.10'
|
||||
type: string
|
||||
system_list:
|
||||
description: 'Space separated list of systems to run tests on.'
|
||||
required: false
|
||||
default: 'ubuntu windows msys2 macos'
|
||||
type: string
|
||||
name:
|
||||
description: 'Name of the tool.'
|
||||
@@ -75,16 +80,39 @@ jobs:
|
||||
print("Parameters:")
|
||||
print(params)
|
||||
|
||||
systems = '${{ inputs.system_list }}'.split(' ')
|
||||
versions = '${{ inputs.python_version_list }}'.split(' ')
|
||||
if '3.6' in versions:
|
||||
print("::warning title=Deprecated::Support for Python 3.6 ended in 2021.12.23.")
|
||||
if '3.11' in versions:
|
||||
print(f"::notice title=Experimental::Python 3.11 (3.11.0-alpha3) is a pre-release.")
|
||||
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' },
|
||||
'python': {
|
||||
'3.6': { 'icon': '⚫', 'until': '2021.12.23' },
|
||||
'3.7': { 'icon': '🔴', 'until': '2023.06.27' },
|
||||
'3.8': { 'icon': '🟠', 'until': '2024.10' },
|
||||
'3.9': { 'icon': '🟡', 'until': '2025.10' },
|
||||
'3.10': { 'icon': '🟢', 'until': '2026.10' },
|
||||
'3.11': { 'icon': '🟣', 'until': '2027.10' },
|
||||
},
|
||||
'sys': {
|
||||
'ubuntu': { 'icon': '🐧', 'runs-on': 'ubuntu-latest', 'shell': 'bash' },
|
||||
'windows': { 'icon': '🧊', 'runs-on': 'windows-latest', 'shell': 'pwsh' },
|
||||
'msys2': { 'icon': '🟦', 'runs-on': 'windows-latest', 'shell': 'msys2 {0}' },
|
||||
'macos': { 'icon': '🍎', 'runs-on': 'macos-latest', 'shell': 'bash' }
|
||||
}
|
||||
}
|
||||
jobs = [
|
||||
{'python': version, 'icon': data[version]['icon']}
|
||||
for version in '${{ inputs.python_version_list }}'.split(' ')
|
||||
{
|
||||
'sysicon': data['sys'][system]['icon'],
|
||||
'system': system,
|
||||
'runs-on': data['sys'][system]['runs-on'],
|
||||
'shell': data['sys'][system]['shell'],
|
||||
'pyicon': data['python'][version]['icon'],
|
||||
'python': '3.11.0-alpha.3' if version == '3.11' else version
|
||||
}
|
||||
for system in systems
|
||||
for version in (versions if system != 'msys2' else ['3.9'])
|
||||
]
|
||||
print(f'::set-output name=python_jobs::{jobs!s}')
|
||||
print("Python jobs:")
|
||||
|
||||
2
.github/workflows/PublishOnPyPI.yml
vendored
2
.github/workflows/PublishOnPyPI.yml
vendored
@@ -4,7 +4,7 @@
|
||||
# Unai Martinez-Corral #
|
||||
# #
|
||||
# ==================================================================================================================== #
|
||||
# Copyright 2020-2021 The pyTooling Authors #
|
||||
# Copyright 2020-2022 The pyTooling Authors #
|
||||
# #
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); #
|
||||
# you may not use this file except in compliance with the License. #
|
||||
|
||||
2
.github/workflows/PublishTestResults.yml
vendored
2
.github/workflows/PublishTestResults.yml
vendored
@@ -4,7 +4,7 @@
|
||||
# Unai Martinez-Corral #
|
||||
# #
|
||||
# ==================================================================================================================== #
|
||||
# Copyright 2020-2021 The pyTooling Authors #
|
||||
# Copyright 2020-2022 The pyTooling Authors #
|
||||
# #
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); #
|
||||
# you may not use this file except in compliance with the License. #
|
||||
|
||||
2
.github/workflows/PublishToGitHubPages.yml
vendored
2
.github/workflows/PublishToGitHubPages.yml
vendored
@@ -4,7 +4,7 @@
|
||||
# Unai Martinez-Corral #
|
||||
# #
|
||||
# ==================================================================================================================== #
|
||||
# Copyright 2020-2021 The pyTooling Authors #
|
||||
# Copyright 2020-2022 The pyTooling Authors #
|
||||
# #
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); #
|
||||
# you may not use this file except in compliance with the License. #
|
||||
|
||||
2
.github/workflows/Release.yml
vendored
2
.github/workflows/Release.yml
vendored
@@ -4,7 +4,7 @@
|
||||
# Unai Martinez-Corral #
|
||||
# #
|
||||
# ==================================================================================================================== #
|
||||
# Copyright 2020-2021 The pyTooling Authors #
|
||||
# Copyright 2020-2022 The pyTooling Authors #
|
||||
# #
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); #
|
||||
# you may not use this file except in compliance with the License. #
|
||||
|
||||
2
.github/workflows/StaticTypeCheck.yml
vendored
2
.github/workflows/StaticTypeCheck.yml
vendored
@@ -4,7 +4,7 @@
|
||||
# Unai Martinez-Corral #
|
||||
# #
|
||||
# ==================================================================================================================== #
|
||||
# Copyright 2020-2021 The pyTooling Authors #
|
||||
# Copyright 2020-2022 The pyTooling Authors #
|
||||
# #
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); #
|
||||
# you may not use this file except in compliance with the License. #
|
||||
|
||||
126
.github/workflows/TestReleaser.yml
vendored
126
.github/workflows/TestReleaser.yml
vendored
@@ -3,7 +3,7 @@
|
||||
# Unai Martinez-Corral #
|
||||
# #
|
||||
# ==================================================================================================================== #
|
||||
# Copyright 2020-2021 The pyTooling Authors #
|
||||
# Copyright 2020-2022 The pyTooling Authors #
|
||||
# #
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); #
|
||||
# you may not use this file except in compliance with the License. #
|
||||
@@ -39,65 +39,27 @@ env:
|
||||
|
||||
jobs:
|
||||
|
||||
test:
|
||||
|
||||
Image:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DOCKER_BUILDKIT: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- run: echo "Build some tool and generate some (versioned) artifacts" > artifact-$(date -u +"%Y-%m-%dT%H-%M-%SZ").txt
|
||||
- name: Build container image
|
||||
run: docker build -t ghcr.io/pytooling/releaser -f releaser/Dockerfile releaser
|
||||
|
||||
- name: Single
|
||||
uses: ./releaser
|
||||
- name: Push container image
|
||||
uses: ./with-post-step
|
||||
with:
|
||||
rm: true
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
files: artifact-*.txt
|
||||
|
||||
- name: List
|
||||
uses: ./releaser
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
files: |
|
||||
artifact-*.txt
|
||||
README.md
|
||||
|
||||
- name: Add artifacts/*.txt
|
||||
run: |
|
||||
mkdir artifacts
|
||||
echo "Build some tool and generate some artifacts" > artifacts/artifact.txt
|
||||
touch artifacts/empty_file.txt
|
||||
|
||||
- name: Single in subdir
|
||||
uses: ./releaser
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
files: artifacts/artifact.txt
|
||||
|
||||
- name: Add artifacts/*.md
|
||||
run: |
|
||||
echo "releaser hello" > artifacts/hello.md
|
||||
echo "releaser world" > artifacts/world.md
|
||||
|
||||
- name: Directory wildcard
|
||||
uses: ./releaser
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
files: artifacts/*
|
||||
|
||||
- name: Add artifacts/subdir
|
||||
run: |
|
||||
mkdir artifacts/subdir
|
||||
echo "Test recursive glob" > artifacts/subdir/deep_file.txt
|
||||
|
||||
- name: Directory wildcard (recursive)
|
||||
uses: ./releaser
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
files: artifacts/**
|
||||
main: |
|
||||
echo '${{ github.token }}' | docker login ghcr.io -u GitHub-Actions --password-stdin
|
||||
docker push ghcr.io/pytooling/releaser
|
||||
post: docker logout ghcr.io
|
||||
|
||||
|
||||
test-composite:
|
||||
needs: test
|
||||
Composite:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -152,3 +114,63 @@ jobs:
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
files: artifacts/**
|
||||
|
||||
|
||||
Test:
|
||||
needs:
|
||||
- Image
|
||||
- Composite
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- run: echo "Build some tool and generate some (versioned) artifacts" > artifact-$(date -u +"%Y-%m-%dT%H-%M-%SZ").txt
|
||||
|
||||
- name: Single
|
||||
uses: ./releaser
|
||||
with:
|
||||
rm: true
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
files: artifact-*.txt
|
||||
|
||||
- name: List
|
||||
uses: ./releaser
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
files: |
|
||||
artifact-*.txt
|
||||
README.md
|
||||
|
||||
- name: Add artifacts/*.txt
|
||||
run: |
|
||||
mkdir artifacts
|
||||
echo "Build some tool and generate some artifacts" > artifacts/artifact.txt
|
||||
touch artifacts/empty_file.txt
|
||||
|
||||
- name: Single in subdir
|
||||
uses: ./releaser
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
files: artifacts/artifact.txt
|
||||
|
||||
- name: Add artifacts/*.md
|
||||
run: |
|
||||
echo "releaser hello" > artifacts/hello.md
|
||||
echo "releaser world" > artifacts/world.md
|
||||
|
||||
- name: Directory wildcard
|
||||
uses: ./releaser
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
files: artifacts/*
|
||||
|
||||
- name: Add artifacts/subdir
|
||||
run: |
|
||||
mkdir artifacts/subdir
|
||||
echo "Test recursive glob" > artifacts/subdir/deep_file.txt
|
||||
|
||||
- name: Directory wildcard (recursive)
|
||||
uses: ./releaser
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
files: artifacts/**
|
||||
|
||||
68
.github/workflows/UnitTesting.yml
vendored
68
.github/workflows/UnitTesting.yml
vendored
@@ -4,7 +4,7 @@
|
||||
# Unai Martinez-Corral #
|
||||
# #
|
||||
# ==================================================================================================================== #
|
||||
# Copyright 2020-2021 The pyTooling Authors #
|
||||
# Copyright 2020-2022 The pyTooling Authors #
|
||||
# #
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); #
|
||||
# you may not use this file except in compliance with the License. #
|
||||
@@ -26,7 +26,7 @@ on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
jobs:
|
||||
description: 'JSON list with field <python>, telling the versions to run tests with.'
|
||||
description: 'JSON list with environment fields, telling the system and Python versions to run tests with.'
|
||||
required: true
|
||||
type: string
|
||||
requirements:
|
||||
@@ -34,6 +34,25 @@ on:
|
||||
required: false
|
||||
default: '-r tests/requirements.txt'
|
||||
type: string
|
||||
pacboy:
|
||||
description: 'MSYS2 dependencies to be installed through pacboy (pacman).'
|
||||
required: false
|
||||
default: >-
|
||||
python-pip:p
|
||||
python-wheel:p
|
||||
python-coverage:p
|
||||
python-lxml:p
|
||||
type: string
|
||||
mingw_requirements:
|
||||
description: 'Override Python dependencies to be installed through pip on MSYS2 (MINGW64) only.'
|
||||
required: false
|
||||
default: ''
|
||||
type: string
|
||||
unittest_directory:
|
||||
description: 'Path to the directory containing unit tests.'
|
||||
required: false
|
||||
default: 'tests/unit'
|
||||
type: string
|
||||
artifact:
|
||||
description: "Generate unit test report with junitxml and upload results as an artifact."
|
||||
required: false
|
||||
@@ -43,38 +62,71 @@ on:
|
||||
jobs:
|
||||
|
||||
UnitTesting:
|
||||
name: ${{ matrix.icon }} Unit Tests using Python ${{ matrix.python }}
|
||||
runs-on: ubuntu-latest
|
||||
name: ${{ matrix.sysicon }} ${{ matrix.pyicon }} Unit Tests using Python ${{ matrix.python }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include: ${{ fromJson(inputs.jobs) }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: ${{ matrix.shell }}
|
||||
|
||||
steps:
|
||||
- name: ⏬ Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: '🟦 Setup MSYS2'
|
||||
if: matrix.system == 'msys2'
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: MINGW64
|
||||
update: true
|
||||
pacboy: ${{ inputs.pacboy }}
|
||||
|
||||
- name: 🐍 Setup Python ${{ matrix.python }}
|
||||
if: matrix.system != 'msys2'
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: 🔧 Install dependencies
|
||||
- name: ⚙️ Update pip
|
||||
run: python -m pip install -U pip
|
||||
|
||||
- name: 🔧 Install wheel and pip dependencies
|
||||
if: matrix.system != 'msys2'
|
||||
run: |
|
||||
python -m pip install -U pip
|
||||
python -m pip install -U wheel
|
||||
python -m pip install ${{ inputs.requirements }}
|
||||
|
||||
- name: 🔧 Install pip dependencies
|
||||
if: matrix.system == 'msys2'
|
||||
run: |
|
||||
if [ 'x${{ inputs.mingw_requirements }}' != 'x' ]; then
|
||||
python -m pip install ${{ inputs.mingw_requirements }}
|
||||
else
|
||||
python -m pip install ${{ inputs.requirements }}
|
||||
fi
|
||||
|
||||
- name: ☑ Run unit tests
|
||||
if: matrix.system == 'windows'
|
||||
run: |
|
||||
$PYTEST_ARGS = if ("${{ inputs.artifact }}".length -gt 0) { "--junitxml=TestReport.xml" } else { "" }
|
||||
python -m pytest -rA ${{ inputs.unittest_directory }} $PYTEST_ARGS --color=yes
|
||||
|
||||
- name: ☑ Run unit tests
|
||||
if: matrix.system != 'windows'
|
||||
run: |
|
||||
[ 'x${{ inputs.artifact }}' != 'x' ] && PYTEST_ARGS='--junitxml=TestReport.xml' || unset PYTEST_ARGS
|
||||
python -m pytest -rA tests/unit $PYTEST_ARGS --color=yes
|
||||
python -m pytest -rA ${{ inputs.unittest_directory }} $PYTEST_ARGS --color=yes
|
||||
|
||||
- name: 📤 Upload 'TestReport.xml' artifact
|
||||
if: inputs.artifact != ''
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ inputs.artifact }}-${{ matrix.python }}
|
||||
name: ${{ inputs.artifact }}-${{ matrix.system }}-${{ matrix.python }}
|
||||
path: TestReport.xml
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
2
.github/workflows/VerifyDocs.yml
vendored
2
.github/workflows/VerifyDocs.yml
vendored
@@ -4,7 +4,7 @@
|
||||
# Unai Martinez-Corral #
|
||||
# #
|
||||
# ==================================================================================================================== #
|
||||
# Copyright 2020-2021 The pyTooling Authors #
|
||||
# Copyright 2020-2022 The pyTooling Authors #
|
||||
# #
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); #
|
||||
# you may not use this file except in compliance with the License. #
|
||||
|
||||
8
.idea/Actions.iml
generated
Normal file
8
.idea/Actions.iml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/Actions.iml" filepath="$PROJECT_DIR$/.idea/Actions.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
@@ -4,7 +4,7 @@
|
||||
# Unai Martinez-Corral #
|
||||
# #
|
||||
# ==================================================================================================================== #
|
||||
# Copyright 2020-2021 The pyTooling Authors #
|
||||
# Copyright 2020-2022 The pyTooling Authors #
|
||||
# #
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); #
|
||||
# you may not use this file except in compliance with the License. #
|
||||
@@ -34,6 +34,7 @@ jobs:
|
||||
with:
|
||||
name: ToolName
|
||||
# Optional
|
||||
system_list: 'ubuntu windows msys2 macos'
|
||||
python_version: '3.10'
|
||||
python_version_list: '3.8 3.9 3.10'
|
||||
|
||||
@@ -45,6 +46,13 @@ jobs:
|
||||
jobs: ${{ needs.Params.outputs.python_jobs }}
|
||||
# Optional
|
||||
requirements: '-r tests/requirements.txt'
|
||||
pacboy: >-
|
||||
python-pip:p
|
||||
python-wheel:p
|
||||
python-coverage:p
|
||||
python-lxml:p
|
||||
mingw_requirements: '-r tests/requirements.mingw.txt'
|
||||
unittest_directory: 'tests/unit'
|
||||
artifact: ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}
|
||||
|
||||
Coverage:
|
||||
@@ -148,7 +156,7 @@ jobs:
|
||||
uses: pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml@main
|
||||
needs:
|
||||
- Params
|
||||
- UnitTesting
|
||||
- PublishTestResults
|
||||
- Coverage
|
||||
- StaticTypeCheck
|
||||
- BuildTheDocs
|
||||
|
||||
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
BIN
ExamplePipeline_light.png
Normal file
BIN
ExamplePipeline_light.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 135 KiB |
56
README.md
56
README.md
@@ -7,24 +7,30 @@ 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:
|
||||
GitHub Actions supports five procedures to reuse code:
|
||||
|
||||
- JavaScript Action.
|
||||
- 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.
|
||||
- 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.
|
||||
- Container Step:
|
||||
- [docs.github.com: actions/learn-github-actions/workflow-syntax-for-github-actions#example-using-a-docker-public-registry-action](https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions#example-using-a-docker-public-registry-action)
|
||||
- [docs.github.com: actions/learn-github-actions/workflow-syntax-for-github-actions#jobsjob_idstepswithargs](https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions#jobsjob_idstepswithargs)
|
||||
- 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.
|
||||
- Reusable Workflow:
|
||||
- [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:
|
||||
Container Actions and Container Steps are almost equivalent: Actions use a configuration file (`action.yml`), while
|
||||
Steps do not.
|
||||
Leaving JavaScript and Container Actions and Steps 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}`),
|
||||
@@ -74,6 +80,7 @@ It allows using the `post` feature with scripts written in bash, python or any o
|
||||
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,
|
||||
@@ -81,20 +88,25 @@ This repository provides 10+ Reusable Workflows based on the CI pipelines of the
|
||||
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)
|
||||
[](ExamplePipeline_dark.png)
|
||||
|
||||
As shown in the screenshot above, the expected order is:
|
||||
[](ExamplePipeline_light.png)
|
||||
|
||||
As shown in the screenshots above, the expected order is:
|
||||
|
||||
- Global:
|
||||
- [Parameters](.github/workflows/Parameters.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.
|
||||
It generates outputs with artifact names and job matrices to be used in later running 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.
|
||||
optionally upload results as XML reports. Configuration options to `pytest` should be given via section
|
||||
`[tool.pytest.ini_options]` in a `pyproject.toml` file.
|
||||
- [CoverageCollection](.github/workflows/CoverageCollection.yml): collect code coverage data (incl. branch coverage)
|
||||
with `pytest`/`pytest-cov`/`coverage.py` using a single version of Python (latest). It generates HTML and Cobertura
|
||||
(XML)reports, upload the HTML report as an artifact, and upload the test results to Codecov and Codacy. Configuration
|
||||
options to `pytest` and `coverage.py` should be given via section `[tool.pytest.ini_options]` and `[tool.coverage.*]`
|
||||
in a `pyproject.toml` file.
|
||||
- [StaticTypeCheck](.github/workflows/StaticTypeCheck.yml): collect static type check result with `mypy`, and
|
||||
optionally upload results as an HTML report.
|
||||
Example `commands`:
|
||||
@@ -121,7 +133,7 @@ As shown in the screenshot above, the expected order is:
|
||||
mypy --html-report ../htmlmypy -p ToolName
|
||||
```
|
||||
|
||||
- [VerifyDocs](.github/workflows/VerifyDocs.yml): extract code examples from the README and test.
|
||||
- [VerifyDocs](.github/workflows/VerifyDocs.yml): extract code examples from the README and test these code snippets.
|
||||
- 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.
|
||||
@@ -150,19 +162,23 @@ Find further usage cases in the following list of projects:
|
||||
- [VHDL/pyVHDLModel](https://github.com/VHDL/pyVHDLModel/tree/main/.github/workflows)
|
||||
|
||||
|
||||
## References
|
||||
|
||||
- [hdl/containers#48](https://github.com/hdl/containers/issues/48)
|
||||
|
||||
|
||||
## Contributors
|
||||
|
||||
* [Patrick Lehmann](https://GitHub.com/Paebbels)
|
||||
* [Unai Martinez-Corral](https://GitHub.com/umarcor) (Maintainer)
|
||||
* [and more...](https://GitHub.com/pyTooling/Actions/graphs/contributors)
|
||||
* [and more...](https://GitHub.com/pyTooling/Actions/graphs/contributors)
|
||||
|
||||
|
||||
## License
|
||||
|
||||
This Python package (source code) licensed under [Apache License 2.0](LICENSE.md).
|
||||
This Python package (source code) licensed under [Apache License 2.0](LICENSE.md).
|
||||
The accompanying documentation is licensed under [Creative Commons - Attribution 4.0 (CC-BY 4.0)](doc/Doc-License.rst).
|
||||
|
||||
---
|
||||
|
||||
-------------------------
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
8
releaser/DEVELOPMENT.md
Normal file
8
releaser/DEVELOPMENT.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Releaser Development
|
||||
|
||||
- [pyTooling/pyAttributes](https://github.com/pyTooling/pyAttributes) or
|
||||
[willmcgugan/rich](https://github.com/willmcgugan/rich) might be used to enhance the UX.
|
||||
|
||||
- It might be desirable to have pyTooling.Version.SemVersion handle the regular expression from
|
||||
[semver.org](https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string), and use
|
||||
proper Python classes in **Releaser**.
|
||||
@@ -1,4 +1,12 @@
|
||||
FROM python:3.9-slim-bullseye
|
||||
COPY releaser.py /releaser.py
|
||||
RUN pip install PyGithub --progress-bar off
|
||||
RUN pip install PyGithub --progress-bar off \
|
||||
&& apt update -qq \
|
||||
&& apt install -y curl \
|
||||
&& curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | \
|
||||
dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
|
||||
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | \
|
||||
tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
|
||||
&& apt update -qq \
|
||||
&& apt install -y gh
|
||||
CMD ["/releaser.py"]
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
Combined with a workflow that is executed periodically, **Releaser** allows to provide a fixed release name for users willing
|
||||
to use daily/nightly artifacts of a project.
|
||||
|
||||
Furthermore, when any [semver](https://semver.org) compilant tagged commit is pushed, **Releaser** can create a release and
|
||||
upload assets.
|
||||
Furthermore, when any [semver](https://semver.org) compilant tagged commit is pushed, **Releaser** can create a release
|
||||
and upload assets.
|
||||
|
||||
## Context
|
||||
|
||||
@@ -17,16 +17,19 @@ GitHub provides official clients for the GitHub API through [github.com/octokit]
|
||||
- [octokit.rb](https://github.com/octokit/octokit.rb) ([octokit.github.io/octokit.rb](http://octokit.github.io/octokit.rb))
|
||||
- [octokit.net](https://github.com/octokit/octokit.net) ([octokitnet.rtfd.io](https://octokitnet.rtfd.io))
|
||||
|
||||
When GitHub Actions was released in 2019, two Actions were made available through [github.com/actions](https://github.com/actions) for dealing with GitHub Releases:
|
||||
When GitHub Actions was released in 2019, two Actions were made available through
|
||||
[github.com/actions](https://github.com/actions) for dealing with GitHub Releases:
|
||||
|
||||
- [actions/create-release](https://github.com/actions/create-release)
|
||||
- [actions/upload-release-asset](https://github.com/actions/upload-release-asset)
|
||||
|
||||
However, those Actions were contributed by an employee in spare time, not officially supported by GitHub.
|
||||
Therefore, they were unmaintained before GitHub Actions was out of the private beta (see [actions/upload-release-asset#58](https://github.com/actions/upload-release-asset/issues/58)) and, a year later, archived.
|
||||
Therefore, they were unmaintained before GitHub Actions was out of the private beta
|
||||
(see [actions/upload-release-asset#58](https://github.com/actions/upload-release-asset/issues/58))
|
||||
and, a year later, archived.
|
||||
Those Actions are based on [actions/toolkit](https://github.com/actions/toolkit)'s hydrated version of octokit.js.
|
||||
|
||||
From a practical point of view, [actions/github-script](https://github.com/actions/github-script) is the natural replacement to those Actions, since it allows to use a pre-authenticated octokit.js client along with the workflow run context.
|
||||
From a practical point of view, [actions/github-script](https://github.com/actions/github-script) is the natural replacement to those Actions, since it allows to use a pre-authenticated *octokit.js* client along with the workflow run context.
|
||||
Still, it requires writing plain JavaScript.
|
||||
|
||||
Alternatively, there are non-official GitHub API libraries available in other languages (see [docs.github.com: rest/overview/libraries](https://docs.github.com/en/rest/overview/libraries)).
|
||||
@@ -45,6 +48,20 @@ as assets.
|
||||
In this context, one of the main use cases of **Releaser** is pushing artifacts as release assets.
|
||||
Thus, the name of the Action.
|
||||
|
||||
GitHub provides an official CLI tool, written in golang: [cli/cli](https://github.com/cli/cli).
|
||||
When the Python version of **Releaser** was written, `cli` was evaluated as an alternative to *PyGitHub*.
|
||||
`gh release` was (and still is) not flexible enough to update the reference of a release, without deleting and
|
||||
recreating it (see [cli.github.com: manual/gh_release_create](https://cli.github.com/manual/gh_release_create)).
|
||||
Deletion and recreation is unfortunate, because it notifies all the watchers of a repository
|
||||
(see [eine/tip#111](https://github.com/eine/tip/issues/111)).
|
||||
However, [cli.github.com: manual/gh_release_upload](https://cli.github.com/manual/gh_release_upload) handles uploading
|
||||
artifacts as assets faster and with better stability for larger files than *PyGitHub*
|
||||
(see [msys2/msys2-installer#36](https://github.com/msys2/msys2-installer/pull/36)).
|
||||
Furthermore, the GitHub CLI is installed on GitHub Actions' default virtual environments.
|
||||
Although `gh` does not support login through SSH (see [cli/cli#3715](https://github.com/cli/cli/issues/3715)), on GitHub
|
||||
Actions a token is available `${{ github.token }}`.
|
||||
Therefore, **Releaser** uses `gh release upload` internally.
|
||||
|
||||
## Usage
|
||||
|
||||
The following block shows a minimal YAML workflow file:
|
||||
@@ -72,7 +89,7 @@ jobs:
|
||||
# Update tag and pre-release
|
||||
# - Update (force-push) tag to the commit that is used in the workflow.
|
||||
# - Upload artifacts defined by the user.
|
||||
- uses: pyTooling/Actions/releaser@main
|
||||
- uses: pyTooling/Actions/releaser@r0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
files: |
|
||||
@@ -80,52 +97,19 @@ jobs:
|
||||
README.md
|
||||
```
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
GitHub's internal connections seem not to be very stable; as a result, uploading artifacts as assets does produce
|
||||
failures rather frequently, particularly if large tarballs are to be published.
|
||||
When failures are produced, some assets are left in a broken state within the release.
|
||||
**Releaser** tries to handle those cases by first uploading assets with a `tmp.*` name and then renaming them; if an existing
|
||||
`tmp.*` is found, it is removed and the upload is retried.
|
||||
Therefore, restarting the **Releaser** job should suffice for "fixing" a failing run.
|
||||
|
||||
Note:
|
||||
Currently, GitHub Actions does not allow restarting a single job.
|
||||
That is unfortunate, because **Releaser** is typically used as the last dependent job in the workflows.
|
||||
Hence, running **Releaser** again requires restarting the whole workflow.
|
||||
Fortunately, restarting individual jobs is expected to be supported on GitHub Actions in the future.
|
||||
See [github/roadmap#271](https://github.com/github/roadmap/issues/271) and [actions/runner#432](https://github.com/actions/runner/issues/432).
|
||||
|
||||
If the tip/nightly release generated with **Releaser** is broken, and restarting the run cannot fix it, the recommended
|
||||
procedure is the following:
|
||||
|
||||
1. Go to `https://github.com/<name>/<repo>/releases/edit/<tag>`.
|
||||
2. Edit the assets to:
|
||||
- Remove the ones with a warning symbol and/or named starting with `tmp.*`.
|
||||
- Or, remove all of them.
|
||||
3. Save the changes (click the `Update release` button) and restart the **Releaser** job in CI.
|
||||
5. If that does still not work, remove the release and restart the **Releaser** job in CI.
|
||||
|
||||
See also [eine/tip#160](https://github.com/eine/tip/issues/160).
|
||||
|
||||
Note:
|
||||
If all the assets are removed, or if the release itself is removed, tip/nightly assets won't be available for
|
||||
users until the workflow is successfully run.
|
||||
For instance, Action [setup-ghdl-ci](https://github.com/ghdl/setup-ghdl-ci) uses assets from [ghdl/ghdl: releases/tag/nightly](https://github.com/ghdl/ghdl/releases/tag/nightly).
|
||||
Hence, it is recommended to try removing the conflictive assets only, in order to maximise the availability.
|
||||
|
||||
### Composite Action
|
||||
|
||||
The default implementation of **Releaser** is a Container Action.
|
||||
Therefore, in each run, the container image is built before starting the job.
|
||||
Therefore, a pre-built container image is pulled before starting the job.
|
||||
Alternatively, a Composite Action version is available: `uses: pyTooling/Actions/releaser/composite@main`.
|
||||
The Composite version installs the dependencies on the host (the runner environment), instead of using a container.
|
||||
Both implementations are functionally equivalent from **Releaser**'s point of view; however, the Composite Action allows users
|
||||
to tweak the version of Python by using [actions/setup-python](https://github.com/actions/setup-python) before.
|
||||
Both implementations are functionally equivalent from **Releaser**'s point of view; however, the Composite Action allows
|
||||
users to tweak the version of Python by using [actions/setup-python](https://github.com/actions/setup-python) before.
|
||||
|
||||
## Options
|
||||
|
||||
All options can be optionally provided as environment variables: `INPUT_TOKEN`, `INPUT_FILES`, `INPUT_TAG`, `INPUT_RM` and/or `INPUT_SNAPSHOTS`.
|
||||
All options can be optionally provided as environment variables: `INPUT_TOKEN`, `INPUT_FILES`, `INPUT_TAG`, `INPUT_RM`
|
||||
and/or `INPUT_SNAPSHOTS`.
|
||||
|
||||
### token (required)
|
||||
|
||||
@@ -133,7 +117,8 @@ Token to make authenticated API calls; can be passed in using `{{ secrets.GITHUB
|
||||
|
||||
### files (required)
|
||||
|
||||
Either a single filename/pattern or a multi-line list can be provided. All the artifacts are uploaded regardless of the hierarchy.
|
||||
Either a single filename/pattern or a multi-line list can be provided. All the artifacts are uploaded regardless of the
|
||||
hierarchy.
|
||||
|
||||
For creating/updating a release without uploading assets, set `files: none`.
|
||||
|
||||
@@ -143,18 +128,28 @@ The default tag name for the tip/nightly pre-release is `tip`, but it can be opt
|
||||
|
||||
### rm
|
||||
|
||||
Set option `rm` to `true` for systematically removing previous artifacts (e.g. old versions). Otherwise (by default), all previours artifacts are preserved or overwritten.
|
||||
Set option `rm` to `true` for systematically removing previous artifacts (e.g. old versions).
|
||||
Otherwise (by default), all previours artifacts are preserved or overwritten.
|
||||
|
||||
Note:
|
||||
If all the assets are removed, or if the release itself is removed, tip/nightly assets won't be available for
|
||||
users until the workflow is successfully run.
|
||||
For instance, Action [setup-ghdl-ci](https://github.com/ghdl/setup-ghdl-ci) uses assets from [ghdl/ghdl: releases/tag/nightly](https://github.com/ghdl/ghdl/releases/tag/nightly).
|
||||
Hence, it is recommended to try removing the conflictive assets only, in order to maximise the availability.
|
||||
|
||||
### snapshots
|
||||
|
||||
Whether to create releases from any tag or to treat some as snapshots. By default, all the tags with non-empty `prerelease` field (see [semver.org: Is there a suggested regular expression (RegEx) to check a SemVer string?](https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string)) are considered snapshots; neither a release is created nor assets are uploaded.
|
||||
Whether to create releases from any tag or to treat some as snapshots.
|
||||
By default, all the tags with non-empty `prerelease` field (see [semver.org: Is there a suggested regular expression (RegEx) to check a SemVer string?](https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string))
|
||||
are considered snapshots; neither a release is created nor assets are uploaded.
|
||||
|
||||
## Advanced/complex use cases
|
||||
|
||||
**Releaser** is essentially a very fine wrapper to use the GitHub Actions context data along with the classes
|
||||
**Releaser** is essentially a very thin wrapper to use the GitHub Actions context data along with the classes
|
||||
and methods of PyGithub.
|
||||
|
||||
Similarly to [actions/github-script](https://github.com/actions/github-script), users with advanced/complex requirements might find it desirable to write their own Python script, instead of using **Releaser**.
|
||||
Similarly to [actions/github-script](https://github.com/actions/github-script), users with advanced/complex requirements
|
||||
might find it desirable to write their own Python script, instead of using **Releaser**.
|
||||
In fact, since `shell: python` is supported in GitHub Actions, using Python does *not* require any Action.
|
||||
For prototyping purposes, the following job might be useful:
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# Unai Martinez-Corral #
|
||||
# #
|
||||
# ==================================================================================================================== #
|
||||
# Copyright 2020-2021 The pyTooling Authors #
|
||||
# Copyright 2020-2022 The pyTooling Authors #
|
||||
# #
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); #
|
||||
# you may not use this file except in compliance with the License. #
|
||||
@@ -42,4 +42,4 @@ inputs:
|
||||
default: true
|
||||
runs:
|
||||
using: 'docker'
|
||||
image: 'Dockerfile'
|
||||
image: 'docker://ghcr.io/pytooling/releaser'
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# Unai Martinez-Corral #
|
||||
# #
|
||||
# ==================================================================================================================== #
|
||||
# Copyright 2020-2021 The pyTooling Authors #
|
||||
# Copyright 2020-2022 The pyTooling Authors #
|
||||
# #
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); #
|
||||
# you may not use this file except in compliance with the License. #
|
||||
@@ -48,7 +48,7 @@ runs:
|
||||
run: pip install PyGithub --progress-bar off
|
||||
|
||||
- shell: bash
|
||||
run: ${{ github.action_path }}/../releaser.py
|
||||
run: '''${{ github.action_path }}/../releaser.py'''
|
||||
env:
|
||||
INPUT_TOKEN: ${{ inputs.token }}
|
||||
INPUT_FILES: ${{ inputs.files }}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
# Unai Martinez-Corral #
|
||||
# #
|
||||
# ==================================================================================================================== #
|
||||
# Copyright 2020-2021 The pyTooling Authors #
|
||||
# Copyright 2020-2022 The pyTooling Authors #
|
||||
# #
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); #
|
||||
# you may not use this file except in compliance with the License. #
|
||||
@@ -22,196 +22,169 @@
|
||||
# SPDX-License-Identifier: Apache-2.0 #
|
||||
# ==================================================================================================================== #
|
||||
import re
|
||||
from sys import argv, stdout, exit as sys_exit
|
||||
from sys import argv as sys_argv, stdout, exit as sys_exit
|
||||
from os import environ, getenv
|
||||
from glob import glob
|
||||
from pathlib import Path
|
||||
from github import Github, GithubException
|
||||
from subprocess import check_call
|
||||
|
||||
print("· Get list of artifacts to be uploaded")
|
||||
|
||||
args = []
|
||||
files = []
|
||||
paramTag = getenv("INPUT_TAG", "tip")
|
||||
paramFiles = getenv("INPUT_FILES", None).split()
|
||||
paramRM = getenv("INPUT_RM", "false") == "true"
|
||||
paramSnapshots = getenv("INPUT_SNAPSHOTS", "true").lower() == "true"
|
||||
paramToken = (
|
||||
environ["GITHUB_TOKEN"]
|
||||
if "GITHUB_TOKEN" in environ
|
||||
else environ["INPUT_TOKEN"]
|
||||
if "INPUT_TOKEN" in environ
|
||||
else None
|
||||
)
|
||||
paramRepo = getenv("GITHUB_REPOSITORY", None)
|
||||
paramRef = getenv("GITHUB_REF", None)
|
||||
paramSHA = getenv("GITHUB_SHA", None)
|
||||
|
||||
if "INPUT_FILES" in environ:
|
||||
args = environ["INPUT_FILES"].split()
|
||||
|
||||
if len(argv) > 1:
|
||||
args = args + argv[1:]
|
||||
|
||||
if len(args) == 1 and args[0] == "none":
|
||||
files = []
|
||||
print("! Skipping 'files' because it's set to 'none")
|
||||
elif len(args) == 0:
|
||||
stdout.flush()
|
||||
raise (Exception("Glob patterns need to be provided as positional arguments or through envvar 'INPUT_FILES'!"))
|
||||
else:
|
||||
for item in args:
|
||||
print(f" glob({item!s}):")
|
||||
for fname in [fname for fname in glob(item, recursive=True) if not Path(fname).is_dir()]:
|
||||
if Path(fname).stat().st_size == 0:
|
||||
print(f" - ! Skipping empty file {fname!s}")
|
||||
continue
|
||||
print(f" - {fname!s}")
|
||||
files.append(fname)
|
||||
|
||||
if len(files) < 1:
|
||||
def GetListOfArtifacts(argv, files):
|
||||
print("· Get list of artifacts to be uploaded")
|
||||
args = files if files is not None else []
|
||||
if len(argv) > 1:
|
||||
args += argv[1:]
|
||||
if len(args) == 1 and args[0].lower() == "none":
|
||||
print("! Skipping 'files' because it's set to 'none")
|
||||
return []
|
||||
elif len(args) == 0:
|
||||
stdout.flush()
|
||||
raise (Exception("Empty list of files to upload/update!"))
|
||||
raise (Exception("Glob patterns need to be provided as positional arguments or through envvar 'INPUT_FILES'!"))
|
||||
else:
|
||||
flist = []
|
||||
for item in args:
|
||||
print(f" glob({item!s}):")
|
||||
for fname in [fname for fname in glob(item, recursive=True) if not Path(fname).is_dir()]:
|
||||
if Path(fname).stat().st_size == 0:
|
||||
print(f" - ! Skipping empty file {fname!s}")
|
||||
continue
|
||||
print(f" - {fname!s}")
|
||||
flist.append(fname)
|
||||
if len(flist) < 1:
|
||||
stdout.flush()
|
||||
raise (Exception("Empty list of files to upload/update!"))
|
||||
return flist
|
||||
|
||||
print("· Get GitHub API handler (authenticate)")
|
||||
|
||||
if "GITHUB_TOKEN" in environ:
|
||||
gh = Github(environ["GITHUB_TOKEN"])
|
||||
elif "INPUT_TOKEN" in environ:
|
||||
gh = Github(environ["INPUT_TOKEN"])
|
||||
else:
|
||||
if "GITHUB_USER" not in environ or "GITHUB_PASS" not in environ:
|
||||
def GetGitHubAPIHandler(token):
|
||||
print("· Get GitHub API handler (authenticate)")
|
||||
if token is not None:
|
||||
return Github(token)
|
||||
raise (Exception("Need credentials to authenticate! Please, provide 'GITHUB_TOKEN' or 'INPUT_TOKEN'"))
|
||||
|
||||
|
||||
def CheckRefSemVer(gh_ref, tag, snapshots):
|
||||
print("· Check SemVer compliance of the reference/tag")
|
||||
env_tag = None
|
||||
if gh_ref[0:10] == "refs/tags/":
|
||||
env_tag = gh_ref[10:]
|
||||
if env_tag != tag:
|
||||
rexp = r"^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$"
|
||||
semver = re.search(rexp, env_tag)
|
||||
if semver == None and env_tag[0] == "v":
|
||||
semver = re.search(rexp, env_tag[1:])
|
||||
tag = env_tag
|
||||
if semver == None:
|
||||
print(f"! Could not get semver from {gh_ref!s}")
|
||||
print(f"! Treat tag '{tag!s}' as a release")
|
||||
return (tag, env_tag, False)
|
||||
else:
|
||||
if semver.group("prerelease") is None:
|
||||
# is a regular semver compilant tag
|
||||
return (tag, env_tag, False)
|
||||
elif snapshots:
|
||||
# is semver compilant prerelease tag, thus a snapshot (we skip it)
|
||||
print("! Skipping snapshot prerelease")
|
||||
sys_exit()
|
||||
|
||||
return (tag, env_tag, True)
|
||||
|
||||
|
||||
def GetRepositoryHandler(gh, repo):
|
||||
print("· Get Repository handler")
|
||||
if repo is None:
|
||||
stdout.flush()
|
||||
raise (
|
||||
Exception(
|
||||
"Need credentials to authenticate! Please, provide 'GITHUB_TOKEN', 'INPUT_TOKEN', or 'GITHUB_USER' and 'GITHUB_PASS'"
|
||||
raise (Exception("Repository name not defined! Please set 'GITHUB_REPOSITORY"))
|
||||
return gh.get_repo(repo)
|
||||
|
||||
|
||||
def GetOrCreateRelease(gh_repo, tag, sha, is_prerelease):
|
||||
print("· Get Release handler")
|
||||
gh_tag = None
|
||||
try:
|
||||
gh_tag = gh_repo.get_git_ref(f"tags/{tag!s}")
|
||||
except Exception:
|
||||
stdout.flush()
|
||||
|
||||
if gh_tag:
|
||||
try:
|
||||
return (gh_repo.get_release(tag), False)
|
||||
except Exception:
|
||||
return (gh_repo.create_git_release(tag, tag, "", draft=True, prerelease=is_prerelease), True)
|
||||
else:
|
||||
err_msg = f"Tag/release '{tag!s}' does not exist and could not create it!"
|
||||
if sha is None:
|
||||
raise (Exception(err_msg))
|
||||
try:
|
||||
return (
|
||||
gh_repo.create_git_tag_and_release(
|
||||
tag, "", tag, "", sha, "commit", draft=True, prerelease=is_prerelease
|
||||
),
|
||||
True,
|
||||
)
|
||||
except Exception:
|
||||
raise (Exception(err_msg))
|
||||
|
||||
|
||||
def UpdateReference(gh_release, tag, sha, is_prerelease, is_draft):
|
||||
print("· Update Release reference (force-push tag)")
|
||||
|
||||
if is_draft:
|
||||
# Unfortunately, it seems not possible to update fields 'created_at' or 'published_at'.
|
||||
print(" > Update (pre-)release")
|
||||
gh_release.update_release(
|
||||
gh_release.title,
|
||||
"" if gh_release.body is None else gh_release.body,
|
||||
draft=False,
|
||||
prerelease=is_prerelease,
|
||||
tag_name=gh_release.tag_name,
|
||||
target_commitish=gh_release.target_commitish,
|
||||
)
|
||||
gh = Github(environ["GITHUB_USER"], environ["GITHUB_PASS"])
|
||||
|
||||
print("· Get Repository handler")
|
||||
|
||||
if "GITHUB_REPOSITORY" not in environ:
|
||||
stdout.flush()
|
||||
raise (Exception("Repository name not defined! Please set 'GITHUB_REPOSITORY"))
|
||||
|
||||
gh_repo = gh.get_repo(environ["GITHUB_REPOSITORY"])
|
||||
|
||||
print("· Get Release handler")
|
||||
|
||||
tag = getenv("INPUT_TAG", "tip")
|
||||
|
||||
env_tag = None
|
||||
gh_ref = environ["GITHUB_REF"]
|
||||
is_prerelease = True
|
||||
is_draft = False
|
||||
|
||||
if gh_ref[0:10] == "refs/tags/":
|
||||
env_tag = gh_ref[10:]
|
||||
if env_tag != tag:
|
||||
rexp = r"^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$"
|
||||
semver = re.search(rexp, env_tag)
|
||||
if semver == None and env_tag[0] == "v":
|
||||
semver = re.search(rexp, env_tag[1:])
|
||||
tag = env_tag
|
||||
if semver == None:
|
||||
print(f"! Could not get semver from {gh_ref!s}")
|
||||
print(f"! Treat tag '{tag!s}' as a release")
|
||||
is_prerelease = False
|
||||
else:
|
||||
if semver.group("prerelease") is None:
|
||||
# is a regular semver compilant tag
|
||||
is_prerelease = False
|
||||
elif getenv("INPUT_SNAPSHOTS", "true") == "true":
|
||||
# is semver compilant prerelease tag, thus a snapshot (we skip it)
|
||||
print("! Skipping snapshot prerelease")
|
||||
sys_exit()
|
||||
|
||||
gh_tag = None
|
||||
try:
|
||||
gh_tag = gh_repo.get_git_ref(f"tags/{tag!s}")
|
||||
except Exception:
|
||||
stdout.flush()
|
||||
|
||||
if gh_tag:
|
||||
try:
|
||||
gh_release = gh_repo.get_release(tag)
|
||||
except Exception:
|
||||
gh_release = gh_repo.create_git_release(tag, tag, "", draft=True, prerelease=is_prerelease)
|
||||
is_draft = True
|
||||
else:
|
||||
err_msg = f"Tag/release '{tag!s}' does not exist and could not create it!"
|
||||
if "GITHUB_SHA" not in environ:
|
||||
raise (Exception(err_msg))
|
||||
try:
|
||||
gh_release = gh_repo.create_git_tag_and_release(
|
||||
tag, "", tag, "", environ["GITHUB_SHA"], "commit", draft=True, prerelease=is_prerelease
|
||||
)
|
||||
is_draft = True
|
||||
except Exception:
|
||||
raise (Exception(err_msg))
|
||||
|
||||
print("· Cleanup and/or upload artifacts")
|
||||
|
||||
artifacts = files
|
||||
|
||||
assets = gh_release.get_assets()
|
||||
if sha is not None:
|
||||
print(f" > Force-push '{tag!s}' to {sha!s}")
|
||||
gh_repo.get_git_ref(f"tags/{tag!s}").edit(sha)
|
||||
|
||||
|
||||
def delete_asset_by_name(name):
|
||||
for asset in assets:
|
||||
if asset.name == name:
|
||||
asset.delete_asset()
|
||||
return
|
||||
files = GetListOfArtifacts(sys_argv, paramFiles)
|
||||
stdout.flush()
|
||||
[tag, env_tag, is_prerelease] = CheckRefSemVer(paramRef, paramTag, paramSnapshots)
|
||||
stdout.flush()
|
||||
gh_repo = GetRepositoryHandler(GetGitHubAPIHandler(paramToken), paramRepo)
|
||||
stdout.flush()
|
||||
[gh_release, is_draft] = GetOrCreateRelease(gh_repo, tag, paramSHA, is_prerelease)
|
||||
stdout.flush()
|
||||
|
||||
|
||||
def upload_asset(artifact, name):
|
||||
try:
|
||||
return gh_release.upload_asset(artifact, name=name)
|
||||
except GithubException as ex:
|
||||
if "already_exists" in [err["code"] for err in ex.data["errors"]]:
|
||||
print(f" - {name} exists already! deleting...")
|
||||
delete_asset_by_name(name)
|
||||
else:
|
||||
print(f" - uploading failed: {ex}")
|
||||
except Exception as ex:
|
||||
print(f" - uploading failed: {ex}")
|
||||
|
||||
print(f" - retry uploading {name}...")
|
||||
return gh_release.upload_asset(artifact, name=name)
|
||||
|
||||
|
||||
def replace_asset(artifacts, asset):
|
||||
print(f" > {asset!s}\n {asset.name!s}:")
|
||||
for artifact in artifacts:
|
||||
aname = str(Path(artifact).name)
|
||||
if asset.name == aname:
|
||||
print(f" - uploading tmp.{aname!s}...")
|
||||
new_asset = upload_asset(artifact, name=f"tmp.{aname!s}")
|
||||
print(f" - removing...{aname!s}")
|
||||
asset.delete_asset()
|
||||
print(f" - renaming tmp.{aname!s} to {aname!s}...")
|
||||
new_asset.update_asset(aname, label=aname)
|
||||
artifacts.remove(artifact)
|
||||
return
|
||||
print(" - keep")
|
||||
|
||||
|
||||
if getenv("INPUT_RM", "false") == "true":
|
||||
if paramRM:
|
||||
print("· RM set. All previous assets are being cleared...")
|
||||
for asset in assets:
|
||||
for asset in gh_release.get_assets():
|
||||
print(f" - {asset.name}")
|
||||
asset.delete_asset()
|
||||
else:
|
||||
for asset in assets:
|
||||
replace_asset(artifacts, asset)
|
||||
|
||||
for artifact in artifacts:
|
||||
print(f" > {artifact!s}:\n - uploading...")
|
||||
gh_release.upload_asset(artifact)
|
||||
|
||||
stdout.flush()
|
||||
print("· Update Release reference (force-push tag)")
|
||||
|
||||
if is_draft:
|
||||
# Unfortunately, it seems not possible to update fields 'created_at' or 'published_at'.
|
||||
print(" > Update (pre-)release")
|
||||
gh_release.update_release(
|
||||
gh_release.title,
|
||||
"" if gh_release.body is None else gh_release.body,
|
||||
draft=False,
|
||||
prerelease=is_prerelease,
|
||||
tag_name=gh_release.tag_name,
|
||||
target_commitish=gh_release.target_commitish,
|
||||
)
|
||||
print("· Cleanup and/or upload artifacts")
|
||||
env = environ.copy()
|
||||
env["GITHUB_TOKEN"] = paramToken
|
||||
cmd = ["gh", "release", "upload", "--repo", paramRepo, "--clobber", tag] + files
|
||||
print(f" > {' '.join(cmd)}")
|
||||
check_call(cmd, env=env)
|
||||
stdout.flush()
|
||||
|
||||
if ("GITHUB_SHA" in environ) and (env_tag is None):
|
||||
sha = environ["GITHUB_SHA"]
|
||||
print(f" > Force-push '{tag!s}' to {sha!s}")
|
||||
gh_repo.get_git_ref(f"tags/{tag!s}").edit(sha)
|
||||
UpdateReference(gh_release, tag, paramSHA if env_tag is None else None, is_prerelease, is_draft)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# Unai Martinez-Corral #
|
||||
# #
|
||||
# ==================================================================================================================== #
|
||||
# Copyright 2020-2021 The pyTooling Authors #
|
||||
# Copyright 2020-2022 The pyTooling Authors #
|
||||
# #
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); #
|
||||
# you may not use this file except in compliance with the License. #
|
||||
|
||||
Reference in New Issue
Block a user