Skip to content

Release v3.5.0

Release v3.5.0 #3541

Workflow file for this run

---
name: Build & release
# Read https://github.com/actions/runner/issues/491 for insights on complex workflow execution logic.
"on":
workflow_call:
secrets:
PYPI_TOKEN:
required: false
outputs:
nuitka_matrix:
description: Nuitka build matrix
value: ${{ jobs.project-metadata.outputs.nuitka_matrix }}
# Target are chosen so that all commits get a chance to have their build tested.
push:
branches:
- main
pull_request:
jobs:
project-metadata:
name: Project metadata
runs-on: ubuntu-22.04
outputs:
# There's a design issue with GitHub actions: matrix outputs are not cumulative. The last job wins
# (see: https://github.community/t/bug-jobs-output-should-return-a-list-for-a-matrix-job/128626).
# This means in a graph of jobs, a matrix-based one is terminal, and cannot be depended on. Same goes for
# (reusable) workflows. We use this preliminary job to produce all matrix we need to trigger depending jobs
# over the dimensions.
new_commits_matrix: ${{ steps.project-metadata.outputs.new_commits_matrix }}
release_commits_matrix: ${{ steps.project-metadata.outputs.release_commits_matrix }}
# Export Python project metadata.
nuitka_matrix: ${{ steps.project-metadata.outputs.nuitka_matrix }}
is_poetry_project: ${{ steps.project-metadata.outputs.is_poetry_project }}
package_name: ${{ steps.project-metadata.outputs.package_name }}
release_notes: ${{ steps.project-metadata.outputs.release_notes }}
steps:
- uses: actions/checkout@v4.1.6
with:
# Checkout pull request HEAD commit to ignore actions/checkout's merge commit. Fallback to push SHA.
ref: ${{ github.event.pull_request.head.sha || github.sha }}
# We're going to browse all new commits.
fetch-depth: 0
- name: List all branches
run: |
git branch --all
- name: List all commits
run: |
git log --decorate=full --oneline
- uses: actions/setup-python@v5.1.0
with:
python-version: "3.12"
cache: "pip"
cache-dependency-path: |
**/pyproject.toml
requirements/*.txt
- name: Install uv
run: |
python -m pip install uv
- name: Install Poetry from pinned requirements.txt
# uv needs a venv. See: https://github.com/astral-sh/uv/issues/1374
run: |
uv venv --system
uv pip install -r https://raw.githubusercontent.com/kdeldycke/workflows/main/requirements/metadata.txt
- name: Project metadata
id: project-metadata
env:
GITHUB_CONTEXT: ${{ toJSON(github) }}
run: >
source .venv/bin/activate && python -c "$(curl -fsSL
https://raw.githubusercontent.com/kdeldycke/workflows/main/.github/metadata.py)"
poetry-build:
name: "Poetry: build & check package"
needs:
- project-metadata
if: fromJSON(needs.project-metadata.outputs.is_poetry_project)
strategy:
matrix: ${{ fromJSON(needs.project-metadata.outputs.new_commits_matrix) }}
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4.1.6
with:
ref: ${{ matrix.commit }}
- uses: actions/setup-python@v5.1.0
with:
python-version: "3.12"
cache: "pip"
cache-dependency-path: |
**/pyproject.toml
requirements/*.txt
- name: Install pip
run: |
python -m pip install --upgrade pip
- name: Install poetry, twine and check-wheel-contents
run: >
python -m pip install --requirement
https://raw.githubusercontent.com/kdeldycke/workflows/main/requirements/build.txt
- name: Build package
run: |
poetry install --no-interaction
poetry build --no-interaction
- name: Upload artifacts
uses: actions/upload-artifact@v4.3.3
with:
name: ${{ github.event.repository.name }}-poetry-build-${{ matrix.short_sha }}
path: ./dist/*
- name: Validates package metadata
run: |
poetry check --no-interaction
poetry run twine check ./dist/*
poetry run check-wheel-contents ./dist/*.whl
- name: Test publishing
run: |
poetry publish --dry-run --no-interaction
compile-binaries:
name: "Nuitka: generate binaries"
needs:
- project-metadata
if: needs.project-metadata.outputs.nuitka_matrix
strategy:
matrix: ${{ fromJSON(needs.project-metadata.outputs.nuitka_matrix) }}
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4.1.6
with:
ref: ${{ matrix.commit }}
- uses: actions/setup-python@v5.1.0
with:
# XXX Nuitka does not support Python 3.12 yet.
# See: https://github.com/Nuitka/Nuitka/issues/2433
python-version: "3.11"
cache: "pip"
cache-dependency-path: |
**/pyproject.toml
requirements/*.txt
- name: Install pip
run: |
python -m pip install --upgrade pip
- name: Install poetry
run: >
python -m pip install --requirement
https://raw.githubusercontent.com/kdeldycke/workflows/main/requirements/build.txt
- name: Re-install Nuitka in Poetry venv
# Nuitka needs to be installed within Poetry's venv to allow for lots of workarounds.
# Additional dependencies:
# - ordered-set: used by Nuitka for compilation performance
# - zstandard: for nuitka's onefile compression
run: >
poetry run python -m pip install --requirement
https://raw.githubusercontent.com/kdeldycke/workflows/main/requirements/nuitka.txt
- name: Nuitka + compilers versions
run: |
poetry run python -m nuitka --version
- name: Install package
run: |
poetry install --no-interaction
- name: Build binary within Poetry venv
run: >
poetry run python ${{ matrix.extra_python_params }} -m nuitka
--onefile
--assume-yes-for-downloads
--nofollow-import-to='*.tests'
--output-filename=${{ matrix.bin_name }}
${{ matrix.module_path }}
- name: Upload binaries
uses: actions/upload-artifact@v4.3.3
with:
name: ${{ matrix.bin_name }}
if-no-files-found: error
path: ${{ matrix.bin_name }}
git-tag:
name: Tag release
needs:
- project-metadata
# Only consider pushes to main branch as triggers for releases.
if: github.ref == 'refs/heads/main' && needs.project-metadata.outputs.release_commits_matrix
strategy:
matrix: ${{ fromJSON(needs.project-metadata.outputs.release_commits_matrix) }}
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4.1.6
with:
ref: ${{ matrix.commit }}
- name: Check if tag exists
id: tag_exists
run: |
echo "tag_exists=$(git show-ref --tags "v${{ matrix.current_version }}" --quiet )" >> "$GITHUB_OUTPUT"
- name: Tag search results
run: |
echo "Does tag exist? ${{ steps.tag_exists.outputs.tag_exists && true || false }}"
- name: Push tag
# If for whatever reason the workflow is re-run because it failed the first time, just
# skip the tag creation if it already exists.
if: ${{ ! steps.tag_exists.outputs.tag_exists }}
uses: tvdias/github-tagger@v0.0.2
with:
# XXX We need custom PAT with workflows permissions BECAUSE ??? in .github/workflows/*.yaml files.
repo-token: ${{ secrets.WORKFLOW_UPDATE_GITHUB_PAT || secrets.GITHUB_TOKEN }}
tag: v${{ matrix.current_version }}
commit-sha: ${{ matrix.commit }}
pypi-publish:
name: Publish to PyPi
needs:
- project-metadata
- poetry-build
- git-tag
if: needs.project-metadata.outputs.package_name
strategy:
matrix: ${{ fromJSON(needs.project-metadata.outputs.release_commits_matrix) }}
runs-on: ubuntu-22.04
steps:
- name: Download build artifacts
uses: actions/download-artifact@v4.1.7
id: download
with:
name: ${{ github.event.repository.name }}-poetry-build-${{ matrix.short_sha }}
- name: Push package to public PyPi repository
uses: pypa/gh-action-pypi-publish@v1.8.14
with:
user: __token__
password: ${{ secrets.PYPI_TOKEN }}
packages-dir: ${{ steps.download.outputs.download-path }}
github-release:
name: Publish GitHub release
needs:
- project-metadata
- compile-binaries
- git-tag
- pypi-publish
# Make sure this job always starts if git-tag ran and succeeded.
if: always() && needs.git-tag.result == 'success'
strategy:
matrix: ${{ fromJSON(needs.project-metadata.outputs.release_commits_matrix) }}
runs-on: ubuntu-22.04
steps:
- name: Download all artifacts
# Do not try to fetch build artifacts if any of the job producing them was skipped.
if: needs.pypi-publish.result != 'skipped' || needs.compile-binaries.result != 'skipped'
uses: actions/download-artifact@v4.1.7
id: artifacts
with:
path: release_artifact
# Only consider artifacts produced by the release commit.
pattern: "*-build-${{ matrix.short_sha }}*"
merge-multiple: true
- name: Rename binary artifacts, collect all others
# Do not try to rename artifacts if the job producing them was skipped.
if: needs.compile-binaries.result != 'skipped'
id: rename_artifacts
shell: python
run: |
import json
import os
from pathlib import Path
from random import randint
download_folder = Path("""${{ steps.artifacts.outputs.download-path }}""")
nuitka_matrix = json.loads("""${{ needs.project-metadata.outputs.nuitka_matrix }}""")
binaries = {entry["bin_name"] for entry in nuitka_matrix["include"] if "bin_name" in entry}
artifacts_path = []
for artifact in download_folder.glob("*"):
print(f"Processing {artifact} ...")
assert artifact.is_file()
# Rename binary artifacts to remove the build ID.
if artifact.name in binaries:
new_name = f'{artifact.stem.split("""-build-${{ matrix.short_sha }}""", 1)[0]}{artifact.suffix}'
new_path = artifact.with_name(new_name)
print(f"Renaming {artifact} to {new_path} ...")
assert not new_path.exists()
artifact.rename(new_path)
artifacts_path.append(new_path)
# Collect other artifacts as-is.
else:
print(f"Collecting {artifact} ...")
artifacts_path.append(artifact)
# Produce a unique delimiter to feed multiline content to GITHUB_OUTPUT:
# https://github.com/orgs/community/discussions/26288#discussioncomment-3876281
delimiter = f"ghadelimiter_{randint(10**8, (10**9) - 1)}"
output = f"artifacts_path<<{delimiter}\n"
output += "\n".join(str(p) for p in artifacts_path)
output += f"\n{delimiter}"
env_file = Path(os.getenv("GITHUB_OUTPUT"))
env_file.write_text(output)
- name: Create GitHub release
uses: softprops/action-gh-release@v2.0.5
env:
# XXX We need custom PAT with workflows permissions BECAUSE ??? in .github/workflows/*.yaml files.
GITHUB_TOKEN: ${{ secrets.WORKFLOW_UPDATE_GITHUB_PAT || secrets.GITHUB_TOKEN }}
with:
tag_name: v${{ matrix.current_version }}
target_commitish: ${{ matrix.commit }}
files: ${{ steps.rename_artifacts.outputs.artifacts_path }}
body: ${{ needs.project-metadata.outputs.release_notes }}