Release v4.7.2
#4929
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- | |
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-24.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_python_project: ${{ steps.project-metadata.outputs.is_python_project }} | |
package_name: ${{ steps.project-metadata.outputs.package_name }} | |
release_notes: ${{ steps.project-metadata.outputs.release_notes }} | |
steps: | |
- uses: actions/checkout@v4.2.2 | |
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.3.0 | |
with: | |
python-version: "3.12" | |
cache: "pip" | |
cache-dependency-path: | | |
**/pyproject.toml | |
*requirements.txt | |
requirements/*.txt | |
- name: Install uv | |
run: | | |
python -m pip install -r https://raw.githubusercontent.com/kdeldycke/workflows/main/requirements/uv.txt | |
- name: Install gha-utils | |
run: > | |
uv tool install --with-requirements | |
https://raw.githubusercontent.com/kdeldycke/workflows/main/requirements/gha-utils.txt gha-utils | |
- name: Project metadata | |
id: project-metadata | |
env: | |
GITHUB_CONTEXT: ${{ toJSON(github) }} | |
run: | | |
gha-utils --verbosity DEBUG metadata --overwrite "$GITHUB_OUTPUT" | |
package-build: | |
name: "Build & check package" | |
needs: | |
- project-metadata | |
if: fromJSON(needs.project-metadata.outputs.is_python_project) | |
strategy: | |
matrix: ${{ fromJSON(needs.project-metadata.outputs.new_commits_matrix) }} | |
runs-on: ubuntu-24.04 | |
steps: | |
- uses: actions/checkout@v4.2.2 | |
with: | |
ref: ${{ matrix.commit }} | |
- uses: actions/setup-python@v5.3.0 | |
with: | |
python-version: "3.12" | |
cache: "pip" | |
cache-dependency-path: | | |
**/pyproject.toml | |
*requirements.txt | |
requirements/*.txt | |
- name: Install uv | |
run: | | |
python -m pip install -r https://raw.githubusercontent.com/kdeldycke/workflows/main/requirements/uv.txt | |
- name: Install build dependencies | |
run: | | |
uv --no-progress venv | |
uv --no-progress pip install \ | |
--requirement https://raw.githubusercontent.com/kdeldycke/workflows/main/requirements/build.txt | |
- name: Build package | |
run: | | |
uv --no-progress build | |
- name: Upload artifacts | |
uses: actions/upload-artifact@v4.4.3 | |
with: | |
name: ${{ github.event.repository.name }}-build-${{ matrix.short_sha }} | |
path: ./dist/* | |
- name: Validates package metadata | |
# XXX These checks might be replaced by uv one day: | |
# https://github.com/astral-sh/uv/issues/8641 | |
# https://github.com/astral-sh/uv/issues/8774 | |
run: | | |
uv --no-progress run --frozen -- twine check ./dist/* | |
uv --no-progress run --frozen -- check-wheel-contents ./dist/*.whl | |
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.2.2 | |
with: | |
ref: ${{ matrix.commit }} | |
- uses: actions/setup-python@v5.3.0 | |
with: | |
python-version: "3.12" | |
cache: "pip" | |
cache-dependency-path: | | |
**/pyproject.toml | |
*requirements.txt | |
requirements/*.txt | |
- name: Install uv | |
run: | | |
python -m pip install -r https://raw.githubusercontent.com/kdeldycke/workflows/main/requirements/uv.txt | |
- name: Install Nuitka | |
# XXX We cannot break the long "pip install" line below with a class "\" because it will not be able to run on | |
# Windows' shell: | |
# ParserError: D:\a\_temp\330d7ec7-c0bf-4856-b2d8-407b69be9ee2.ps1:4 | |
# Line | | |
# 4 | --requirement https://raw.githubusercontent.com/kdeldycke/workflows/m … | |
# | ~ | |
# | Missing expression after unary operator '--'. | |
# yamllint disable rule:line-length | |
run: | | |
uv --no-progress venv | |
uv --no-progress pip install --requirement https://raw.githubusercontent.com/kdeldycke/workflows/main/requirements/nuitka.txt | |
# yamllint enable | |
- name: List uv CLIs | |
continue-on-error: true | |
run: | | |
uv run | |
- name: Nuitka + compilers versions - Linux & macOS | |
if: runner.os != 'Windows' | |
run: | | |
uv --no-progress run --frozen -- nuitka --version | |
- name: Build binary - Linux & macOS | |
if: runner.os != 'Windows' | |
run: > | |
uv --no-progress run --frozen -- nuitka | |
--onefile --assume-yes-for-downloads --output-filename=${{ matrix.bin_name }} | |
${{ matrix.module_path }} | |
# XXX We need to call "uv run python -m nuitka" instead of "uv run nuitka" on Windows because of this bug: | |
# https://github.com/Nuitka/Nuitka/issues/3173 | |
# https://github.com/astral-sh/uv/issues/8770 | |
- name: Nuitka + compilers versions - Windows | |
if: runner.os == 'Windows' | |
run: | | |
uv --no-progress run --frozen -- python -m nuitka --version | |
- name: Build binary - Windows | |
if: runner.os == 'Windows' | |
env: | |
# Sets sys.flags.utf8_mode to True, which is like calling Python with the "-X utf8" parameter. | |
# This is a workaround for Windows runners, on redirecting the output of commands to files. See: | |
# https://github.com/databrickslabs/dbx/issues/455#issuecomment-1312770919 | |
# https://github.com/pallets/click/issues/2121#issuecomment-1312773882 | |
# https://gist.github.com/NodeJSmith/e7e37f2d3f162456869f015f842bcf15 | |
# https://github.com/Nuitka/Nuitka/blob/ca1ec9e/nuitka/utils/ReExecute.py#L73-L74 | |
PYTHONUTF8: 1 | |
run: > | |
uv --no-progress run --frozen -- python -m nuitka | |
--onefile --assume-yes-for-downloads --output-filename=${{ matrix.bin_name }} | |
${{ matrix.module_path }} | |
- name: Upload binaries | |
uses: actions/upload-artifact@v4.4.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-24.04 | |
steps: | |
- uses: actions/checkout@v4.2.2 | |
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 | |
- package-build | |
- git-tag | |
if: needs.project-metadata.outputs.package_name | |
strategy: | |
matrix: ${{ fromJSON(needs.project-metadata.outputs.release_commits_matrix) }} | |
runs-on: ubuntu-24.04 | |
steps: | |
- name: Install uv | |
run: | | |
python -m pip install -r https://raw.githubusercontent.com/kdeldycke/workflows/main/requirements/uv.txt | |
- name: Download build artifacts | |
uses: actions/download-artifact@v4.1.8 | |
id: download | |
with: | |
name: ${{ github.event.repository.name }}-build-${{ matrix.short_sha }} | |
- name: Push to PyPi | |
run: | | |
uv --no-progress publish --token "${{ secrets.PYPI_TOKEN }}" "${{ 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-24.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.8 | |
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.9 | |
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 }} |