Skip to content

Merge pull request #1050 from aio-libs/dependabot/github_actions/acti… #2053

Merge pull request #1050 from aio-libs/dependabot/github_actions/acti…

Merge pull request #1050 from aio-libs/dependabot/github_actions/acti… #2053

Workflow file for this run

name: CI/CD
on:
push:
branches-ignore:
- dependabot/**
pull_request:
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
release-commitish:
# github.event_name == 'workflow_dispatch'
# && github.event.inputs.release-commitish
default: ''
description: >-
The commit to be released to PyPI and tagged
in Git as `release-version`. Normally, you
should keep this empty.
required: false
YOLO:
default: false
description: >-
Flag whether test results should block the
release (true/false). Only use this under
extraordinary circumstances to ignore the
test failures and cut the release regardless.
required: false
schedule:
- cron: 1 0 * * * # Run daily at 0:01 UTC
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
permissions:
contents: read
jobs:
pre-setup:
name: ⚙️ Pre-set global build settings
runs-on: ubuntu-latest
defaults:
run:
shell: python
outputs:
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
}}
cache-key-for-dep-files: >-
${{ steps.calc-cache-key-files.outputs.cache-key-for-dep-files }}
git-tag: ${{ steps.git-tag.outputs.tag }}
sdist-artifact-name: ${{ steps.artifact-name.outputs.sdist }}
wheel-artifact-name: ${{ steps.artifact-name.outputs.wheel }}
steps:
- name: Switch to using Python 3.13 by default
uses: actions/setup-python@v6
with:
python-version: >-
3.13
- 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: >-
echo "is-untagged-devel=true" >> "$GITHUB_OUTPUT"
shell: bash
- name: Mark the build as "release request"
id: request-check
if: github.event_name == 'workflow_dispatch'
run: >-
echo "release-requested=true" >> "$GITHUB_OUTPUT"
shell: bash
- name: Check out src from Git
if: >-
steps.request-check.outputs.release-requested != 'true'
uses: actions/checkout@v5
with:
show-progress: false
persist-credentials: false
fetch-depth: 0
ref: ${{ github.event.inputs.release-commitish }}
- 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: >-
echo "cache-key-for-dep-files=${{
hashFiles(
'pyproject.toml',
'setup.cfg',
'requirements-dev.txt',
'.pre-commit-config.yaml'
)
}}" >> "$GITHUB_OUTPUT"
shell: bash
- name: Set up pip cache
if: >-
steps.request-check.outputs.release-requested != 'true'
uses: re-actors/cache-python-deps@810325a232f2a28ea124dfba85c7c72fd1774b38 # v1.0.0
with:
cache-key-for-dependency-files: >-
${{ steps.calc-cache-key-files.outputs.cache-key-for-dep-files }}
- 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
--upgrade
"setuptools-scm>= 9.2, < 10"
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
import setuptools_scm
ver = setuptools_scm.get_version(
${{
steps.untagged-check.outputs.is-untagged-devel == 'true'
&& 'local_scheme="no-local-version"' || ''
}}
)
with open(environ['GITHUB_OUTPUT'], mode='a') as github_output:
print('dist-version={ver}'.format(ver=ver), file=github_output)
- name: Set the target Git tag
id: git-tag
run: >-
echo "tag=v${{
steps.request-check.outputs.release-requested == 'true'
&& github.event.inputs.release-version
|| steps.scm-version.outputs.dist-version
}}" >> "$GITHUB_OUTPUT"
shell: bash
- name: Set the expected dist artifact names
id: artifact-name
run: |
echo "sdist=aiomysql-${{
steps.request-check.outputs.release-requested == 'true'
&& github.event.inputs.release-version
|| steps.scm-version.outputs.dist-version
}}.tar.gz" >> "$GITHUB_OUTPUT"
echo "wheel=aiomysql-${{
steps.request-check.outputs.release-requested == 'true'
&& github.event.inputs.release-version
|| steps.scm-version.outputs.dist-version
}}-py3-none-any.whl" >> "$GITHUB_OUTPUT"
shell: bash
build:
name: >-
👷 dists ${{ needs.pre-setup.outputs.git-tag }}
[mode: ${{
fromJSON(needs.pre-setup.outputs.is-untagged-devel)
&& 'nightly' || ''
}}${{
fromJSON(needs.pre-setup.outputs.release-requested)
&& 'release' || ''
}}${{
(
!fromJSON(needs.pre-setup.outputs.is-untagged-devel)
&& !fromJSON(needs.pre-setup.outputs.release-requested)
) && 'test' || ''
}}]
needs:
- pre-setup # transitive, for accessing settings
runs-on: ubuntu-latest
outputs:
dists-artifact-id: ${{ steps.dist-artifact-upload.outputs.artifact-id }}
env:
PY_COLORS: 1
steps:
- name: Switch to using Python v3.13
uses: actions/setup-python@v6
with:
python-version: >-
3.13
- name: Set up pip cache
uses: re-actors/cache-python-deps@810325a232f2a28ea124dfba85c7c72fd1774b38 # v1.0.0
with:
cache-key-for-dependency-files: >-
${{ needs.pre-setup.outputs.cache-key-for-dep-files }}
- name: Install build tools
run: >-
python -m
pip install
--user
--upgrade
build
- name: Grab the source from Git
uses: actions/checkout@v5
with:
show-progress: false
persist-credentials: false
fetch-depth: >-
${{
steps.request-check.outputs.release-requested == 'true'
&& 1 || 0
}}
ref: ${{ github.event.inputs.release-commitish }}
- name: Setup git user as [bot]
if: >-
fromJSON(needs.pre-setup.outputs.is-untagged-devel)
|| fromJSON(needs.pre-setup.outputs.release-requested)
uses: fregante/setup-git-user@024bc0b8e177d7e77203b48dab6fb45666854b35
- 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 "${GIT_TAG}"
"${GIT_TAG}"
--
${RELEASE_COMMITISH}
env:
GIT_TAG: ${{ needs.pre-setup.outputs.git-tag }}
RELEASE_COMMITISH: ${{ github.event.inputs.release-commitish }}
- name: Build dists
run: >-
python
-m
build
- name: Verify that the artifacts with expected names got created
run: >-
ls -1
"dist/${SDIST_ARTIFACT_NAME}"
"dist/${WHEEL_ARTIFACT_NAME}"
env:
SDIST_ARTIFACT_NAME: ${{ needs.pre-setup.outputs.sdist-artifact-name }}
WHEEL_ARTIFACT_NAME: ${{ needs.pre-setup.outputs.wheel-artifact-name }}
- name: Store the distribution packages
id: dist-artifact-upload
uses: actions/upload-artifact@v5
with:
name: python-package-distributions
# 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: >-
${{
needs.pre-setup.outputs.release-requested == 'true'
&& 90
|| 30
}}
lint:
name: 🧹 Lint
needs:
- build
- pre-setup # transitive, for accessing settings
runs-on: ubuntu-latest
env:
PY_COLORS: 1
steps:
- name: Switch to using Python 3.13 by default
uses: actions/setup-python@v6
with:
python-version: >-
3.13
- name: Set up pip cache
uses: re-actors/cache-python-deps@810325a232f2a28ea124dfba85c7c72fd1774b38 # v1.0.0
with:
cache-key-for-dependency-files: >-
${{ needs.pre-setup.outputs.cache-key-for-dep-files }}
- name: Grab the source from Git
uses: actions/checkout@v5
with:
show-progress: false
persist-credentials: false
ref: ${{ github.event.inputs.release-commitish }}
- name: Download all the dists
uses: actions/download-artifact@v6
with:
artifact-ids: >-
${{ needs.build.outputs.dists-artifact-id }}
path: dist/
- name: Install build tools
run: >-
python -m
pip install
--user
--requirement requirements-dev.txt
- name: flake8 Lint
uses: py-actions/flake8@84ec6726560b6d5bd68f2a5bed83d62b52bb50ba # v2.3.0
with:
flake8-version: 7.3.0
path: aiomysql
args: tests examples
- name: Check package description
run: |
python -m twine check --strict dist/*
tests:
name: >-
🧪 🐍${{
matrix.py
}} @ ${{
matrix.os
}} on ${{
join(matrix.db, '-')
}}
needs:
- build
- pre-setup # transitive, for accessing settings
strategy:
# when updating matrix jobs make sure to adjust the expected reports in
# codecov.notify.after_n_builds in .codecov.yml
matrix:
# service containers are only supported on ubuntu currently
os:
- ubuntu-latest
py:
- '3.9'
- '3.10'
- '3.11'
- '3.12'
- '3.13'
db:
- [mysql, '8.0']
- [mysql, '8.4']
- [mysql, '9.4']
- [mariadb, '10.6']
- [mariadb, '10.11']
- [mariadb, '11.4']
- [mariadb, '11.8']
- [mariadb, '12.0']
fail-fast: false
runs-on: ${{ matrix.os }}
timeout-minutes: 15
continue-on-error: >-
${{
(
(
needs.pre-setup.outputs.release-requested == 'true' &&
!toJSON(github.event.inputs.YOLO)
) ||
contains(matrix.py, '-dev')
) && true || false
}}
env:
MYSQL_ROOT_PASSWORD: rootpw
PY_COLORS: 1
services:
mysql:
image: "${{ join(matrix.db, ':') }}"
ports:
- 3306:3306
volumes:
- "/tmp/run-${{ join(matrix.db, '-') }}/:/socket-mount/"
options: '--name=mysqld'
env:
MYSQL_ROOT_PASSWORD: rootpw
steps:
- name: Setup Python ${{ matrix.py }}
id: python-install
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.py }}
- name: Set up pip cache
uses: re-actors/cache-python-deps@810325a232f2a28ea124dfba85c7c72fd1774b38 # v1.0.0
with:
cache-key-for-dependency-files: >-
${{ needs.pre-setup.outputs.cache-key-for-dep-files }}
- name: Update pip
run: >-
python -m
pip install
--user
--upgrade
pip
- name: Grab the source from Git
uses: actions/checkout@v5
with:
show-progress: false
persist-credentials: false
ref: ${{ github.event.inputs.release-commitish }}
- name: Remove aiomysql source to avoid accidentally using it
run: >-
rm -rf aiomysql
- name: Download all the dists
uses: actions/download-artifact@v6
with:
artifact-ids: >-
${{ needs.build.outputs.dists-artifact-id }}
path: dist/
- name: Install dependencies
run: >-
python -m
pip install
--user
--requirement requirements-dev.txt
- name: Install previously built wheel
run: >-
python -m
pip install
--user
"dist/${WHEEL_ARTIFACT_NAME}"
env:
WHEEL_ARTIFACT_NAME: ${{ needs.pre-setup.outputs.wheel-artifact-name }}
- name: >-
Log platform.platform()
run: >-
python -m platform
- name: >-
Log platform.version()
run: >-
python -c "import platform;
print(platform.version())"
- name: >-
Log platform.uname()
run: >-
python -c "import platform;
print(platform.uname())"
- name: >-
Log platform.release()
run: >-
python -c "import platform;
print(platform.release())"
- name: Log stdlib OpenSSL version
run: >-
python -c
"import ssl; print('\nOPENSSL_VERSION: '
+ ssl.OPENSSL_VERSION + '\nOPENSSL_VERSION_INFO: '
+ repr(ssl.OPENSSL_VERSION_INFO)
+ '\nOPENSSL_VERSION_NUMBER: '
+ repr(ssl.OPENSSL_VERSION_NUMBER))"
# this ensures our database is ready. typically by the time the preparations have completed its first start logic.
# unfortunately we need this hacky workaround as GitHub Actions service containers can't reference data from our repo.
- name: Prepare mysql
run: |
# ensure server is started up
while :
do
sleep 1
mysql -h127.0.0.1 -uroot "-p$MYSQL_ROOT_PASSWORD" -e 'select version()' && break
done
# inject tls configuration
docker container stop mysqld
docker container cp "${{ github.workspace }}/tests/ssl_resources/ssl" mysqld:/etc/mysql/ssl
docker container cp "${{ github.workspace }}/tests/ssl_resources/tls.cnf" mysqld:/etc/mysql/conf.d/aiomysql-tls.cnf
# use custom socket path
# we need to ensure that the socket path is writable for the user running the DB process in the container
sudo chmod 0777 /tmp/run-${{ join(matrix.db, '-') }}
docker container cp "${{ github.workspace }}/tests/ssl_resources/socket.cnf" mysqld:/etc/mysql/conf.d/aiomysql-socket.cnf
docker container start mysqld
# ensure server is started up
while :
do
sleep 1
mysql -h127.0.0.1 -uroot "-p$MYSQL_ROOT_PASSWORD" -e 'select version()' && break
done
mysql -h127.0.0.1 -uroot "-p$MYSQL_ROOT_PASSWORD" -e "SET GLOBAL local_infile=on"
- name: Run tests
run: |
# timeout ensures a more or less clean stop by sending a KeyboardInterrupt which will still provide useful logs
timeout --preserve-status --signal=INT --verbose 570s \
pytest \
--capture=no \
--verbosity 2 \
--cov-report term \
--cov-report xml \
--junitxml=junit.xml \
-o junit_family=legacy \
--cov aiomysql \
--cov tests \
./tests \
--mysql-unix-socket "unix-${{ join(matrix.db, '') }}=/tmp/run-${{ join(matrix.db, '-') }}/mysql.sock" \
--mysql-address "tcp-${{ join(matrix.db, '') }}=127.0.0.1:3306"
env:
PYTHONUNBUFFERED: 1
timeout-minutes: 10
- name: Upload coverage
if: ${{ github.event_name != 'schedule' }}
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
with:
files: ./coverage.xml
flags: >-
CI-GHA,
OS-${{ runner.os }},
VM-${{ matrix.os }},
Py-${{ steps.python-install.outputs.python-version }},
DB-${{ join(matrix.db, '-') }},
${{ matrix.os }}_${{ matrix.py }}_${{ join(matrix.db, '-') }}
fail_ci_if_error: true
- name: Upload test results to Codecov
if: ${{ !cancelled() }}
uses: codecov/test-results-action@47f89e9acb64b76debcd5ea40642d25a4adced9f # v1.1.1
with:
files: ./junit.xml
flags: >-
CI-GHA,
OS-${{ runner.os }},
VM-${{ matrix.os }},
Py-${{ steps.python-install.outputs.python-version }},
DB-${{ join(matrix.db, '-') }},
${{ matrix.os }}_${{ matrix.py }}_${{ join(matrix.db, '-') }}
fail_ci_if_error: true
check: # This job does nothing and is only used for the branch protection
if: always()
needs:
- lint
- tests
runs-on: ubuntu-latest
steps:
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2
with:
jobs: ${{ toJSON(needs) }}
publish-pypi:
name: Publish 🐍📦 ${{ needs.pre-setup.outputs.git-tag }} to PyPI
needs:
- check
- build
- pre-setup # transitive, for accessing settings
if: >-
fromJSON(needs.pre-setup.outputs.release-requested)
runs-on: ubuntu-latest
environment:
name: pypi
url: >-
https://pypi.org/project/aiomysql/${{
needs.pre-setup.outputs.dist-version
}}
permissions:
id-token: write # this permission is mandatory for trusted publishing
steps:
- name: Download all the dists
uses: actions/download-artifact@v6
with:
artifact-ids: >-
${{ needs.build.outputs.dists-artifact-id }}
path: dist/
- name: >-
Publish 🐍📦 ${{ needs.pre-setup.outputs.git-tag }} to PyPI
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
with:
print-hash: true
publish-testpypi:
name: Publish 🐍📦 ${{ needs.pre-setup.outputs.git-tag }} to TestPyPI
needs:
- check
- build
- pre-setup # transitive, for accessing settings
if: >-
fromJSON(needs.pre-setup.outputs.is-untagged-devel)
|| fromJSON(needs.pre-setup.outputs.release-requested)
runs-on: ubuntu-latest
environment:
name: testpypi
url: >-
https://test.pypi.org/project/aiomysql/${{
needs.pre-setup.outputs.dist-version
}}
permissions:
id-token: write # this permission is mandatory for trusted publishing
steps:
- name: Download all the dists
uses: actions/download-artifact@v6
with:
artifact-ids: >-
${{ needs.build.outputs.dists-artifact-id }}
path: dist/
- name: >-
Publish 🐍📦 ${{ needs.pre-setup.outputs.git-tag }} to TestPyPI
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
with:
repository-url: https://test.pypi.org/legacy/
print-hash: true
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
runs-on: ubuntu-latest
permissions:
contents: write # Required for pushing git tag
steps:
- name: Fetch the src snapshot
uses: actions/checkout@v5
with:
show-progress: false
persist-credentials: true
fetch-depth: 1
ref: ${{ github.event.inputs.release-commitish }}
- name: Setup git user as [bot]
uses: fregante/setup-git-user@024bc0b8e177d7e77203b48dab6fb45666854b35
- name: >-
Tag the release in the local Git repo
as ${{ needs.pre-setup.outputs.git-tag }}
run: >-
git tag
-m "${GIT_TAG}"
"${GIT_TAG}"
--
${RELEASE_COMMITISH}
env:
GIT_TAG: ${{ needs.pre-setup.outputs.git-tag }}
RELEASE_COMMITISH: ${{ github.event.inputs.release-commitish }}
- name: >-
Push ${{ needs.pre-setup.outputs.git-tag }} tag corresponding
to the just published release back to GitHub
run: >-
git push --atomic origin "${GIT_TAG}"
env:
GIT_TAG: ${{ needs.pre-setup.outputs.git-tag }}
publish-github-release:
name: >-
Publish a tag and GitHub release for
${{ needs.pre-setup.outputs.git-tag }}
needs:
- post-release-repo-update
- build
- pre-setup # transitive, for accessing settings
runs-on: ubuntu-latest
permissions:
contents: write
discussions: write
steps:
- name: Fetch the src snapshot
uses: actions/checkout@v5
with:
show-progress: false
persist-credentials: false
fetch-depth: 1
ref: ${{ github.event.inputs.release-commitish }}
- name: Download all the dists
uses: actions/download-artifact@v6
with:
artifact-ids: >-
${{ needs.build.outputs.dists-artifact-id }}
path: dist/
- name: >-
Publish a GitHub Release for
${{ needs.pre-setup.outputs.git-tag }}
uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1.20.0
with:
artifacts: |
dist/${{ needs.pre-setup.outputs.sdist-artifact-name }}
dist/${{ needs.pre-setup.outputs.wheel-artifact-name }}
artifactContentType: raw # Because whl and tgz are of different types
# FIXME: Use Towncrier once it is integrated.
bodyFile: CHANGES.txt
discussionCategory: Announcements
name: ${{ needs.pre-setup.outputs.git-tag }}
tag: ${{ needs.pre-setup.outputs.git-tag }}
immutableCreate: true