Bump gha-utils from 4.17.2 to 4.17.3 #6159
Workflow file for this run
This file contains hidden or 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 | |
inputs: | |
unstable-targets: | |
description: List of unstable targets on which the build should be attempted but not fatal | |
required: false | |
# XXX This has to be a string because workflow inputs are not allowed to be complex types. | |
# Still, thanks to gha-utils resilience, we can pass a list of targets as a string, separated | |
# by arbitrary sepatarators. | |
type: string | |
timeout: | |
description: Timeout in seconds for each binary test | |
required: false | |
type: number | |
test-plan-file: | |
description: YAML file containing the full test plan for binaries | |
required: false | |
type: string | |
default: ./tests/cli-test-plan.yaml | |
test-plan: | |
description: Test plan for binaries | |
required: false | |
# XXX This has to be a string because workflow inputs are not allowed to be complex types. | |
type: string | |
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: | |
# Defaults sets in workflow_call.inputs or workflow_dispatch.inputs are not propagated to other events. | |
# We have to manually manage them: https://github.com/orgs/community/discussions/39357#discussioncomment-7500641 | |
env: | |
test-plan-file: >- | |
${{ inputs.test-plan-file == null && './tests/cli-test-plan.yaml' || inputs.test-plan-file }} | |
test-plan: ${{ inputs.test-plan }} | |
concurrency: | |
# Group workflow jobs so new commits cancels in-progress execution triggered by previous commits. Source: | |
# https://mail.python.org/archives/list/[email protected]/thread/PCBCQMJF64JGRBOX7E2EE4YLKHT4DI55/ | |
# https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/control-the-concurrency-of-workflows-and-jobs | |
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} | |
cancel-in-progress: true | |
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/[email protected] | |
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 | |
- name: Install uv | |
run: | | |
python -m pip install -r https://raw.githubusercontent.com/kdeldycke/workflows/main/requirements/uv.txt | |
- name: Run gha-utils metadata | |
id: project-metadata | |
env: | |
GITHUB_CONTEXT: ${{ toJSON(github) }} | |
run: > | |
uvx | |
--with-requirements https://raw.githubusercontent.com/kdeldycke/workflows/main/requirements/gha-utils.txt | |
-- | |
gha-utils --verbosity DEBUG metadata | |
${{ inputs.unstable-targets != null && format('--unstable-targets "{0}"', inputs.unstable-targets) || '' }} | |
--overwrite "$GITHUB_OUTPUT" | |
package-build: | |
name: Build 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/[email protected] | |
with: | |
ref: ${{ matrix.commit }} | |
- name: Install uv | |
run: | | |
python -m pip install -r https://raw.githubusercontent.com/kdeldycke/workflows/main/requirements/uv.txt | |
- name: Build package | |
run: | | |
uv --no-progress build | |
- name: Upload artifacts | |
uses: actions/[email protected] | |
with: | |
name: ${{ github.event.repository.name }}-${{ matrix.short_sha }} | |
path: ./dist/* | |
# TODO: Should we also attest the archive created here? See: | |
# https://github.com/actions/attest-build-provenance?tab=readme-ov-file#integration-with-actionsupload-artifact | |
compile-binaries: | |
name: "${{ matrix.state == 'stable' && '✅' || '⁉️' }} Build on ${{ matrix.os }}, ${{ matrix.short_sha }}" | |
needs: | |
- project-metadata | |
if: needs.project-metadata.outputs.nuitka_matrix | |
strategy: | |
matrix: ${{ fromJSON(needs.project-metadata.outputs.nuitka_matrix) }} | |
runs-on: ${{ matrix.os }} | |
# We keep going when a job flagged as not stable fails. | |
continue-on-error: ${{ matrix.state == 'unstable' }} | |
steps: | |
- uses: actions/[email protected] | |
with: | |
ref: ${{ matrix.commit }} | |
# XXX We use setup-python to install ARM64 flavor of Python on Windows because uv does not support it yet: | |
# https://github.com/astral-sh/python-build-standalone/issues/386 | |
# https://github.com/Nuitka/Nuitka/issues/3449#issuecomment-2889794114 | |
- uses: actions/[email protected] | |
if: matrix.os == 'windows-11-arm' | |
with: | |
# XXX Python version here does not really matter but we had to set it for the action to pick-up the | |
# architecture parameter. | |
python-version: "3.13.3" | |
architecture: arm64 | |
- name: Install uv | |
run: | | |
python -m pip install -r https://raw.githubusercontent.com/kdeldycke/workflows/main/requirements/uv.txt | |
- name: Build binary | |
# XXX Python for Windows ARM64 has not been shipped yet, so don't make uv try to fall back on x64 Python by | |
# forcing the --no-managed-python option. See: | |
# https://github.com/astral-sh/python-build-standalone/pull/387#issuecomment-2836029040 | |
# https://github.com/astral-sh/uv/issues/12906 | |
# https://github.com/astral-sh/uv/pull/13719 | |
# https://github.com/astral-sh/uv/pull/13724 | |
run: > | |
uv run | |
${{ matrix.os == 'windows-11-arm' && '--no-managed-python' || '' }} | |
--with-requirements https://raw.githubusercontent.com/kdeldycke/workflows/main/requirements/nuitka.txt | |
-- | |
nuitka --onefile --assume-yes-for-downloads --output-filename=${{ matrix.bin_name }} ${{ matrix.module_path }} | |
- name: Install exiftool - Linux | |
if: runner.os == 'Linux' | |
run: | | |
sudo apt --quiet --yes install exiftool | |
- name: Install exiftool - macOS | |
if: runner.os == 'macOS' | |
run: | | |
brew install exiftool | |
- name: Install exiftool - Windows | |
if: runner.os == 'Windows' | |
run: | | |
choco install exiftool --no-progress --yes | |
- name: Binary metadata | |
# Ubuntu: | |
# CPU Type : Arm 64-bits (Armv8/AArch64) | |
# CPU Type : AMD x86-64 | |
# macOS: | |
# CPU Type : ARM 64-bit | |
# CPU Type : x86 64-bit | |
# Windows | |
# Machine Type : ARM64 little endian | |
# Machine Type : AMD AMD64 | |
run: | | |
exiftool${{ runner.os == 'Windows' && '.exe' || '' }} ${{ matrix.bin_name }} | |
- name: Upload binaries | |
uses: actions/[email protected] | |
with: | |
name: ${{ matrix.bin_name }} | |
if-no-files-found: warn | |
path: ${{ matrix.bin_name }} | |
test-binaries: | |
name: "${{ matrix.state == 'stable' && '✅' || '⁉️' }} Test on ${{ matrix.os }}, ${{ matrix.short_sha }}" | |
needs: | |
- project-metadata | |
- compile-binaries | |
if: needs.project-metadata.outputs.nuitka_matrix | |
strategy: | |
matrix: ${{ fromJSON(needs.project-metadata.outputs.nuitka_matrix) }} | |
runs-on: ${{ matrix.os }} | |
# We keep going when a job flagged as not stable fails. | |
continue-on-error: ${{ matrix.state == 'unstable' }} | |
steps: | |
- uses: actions/[email protected] | |
with: | |
ref: ${{ matrix.commit }} | |
- name: Download artifact | |
uses: actions/[email protected] | |
id: artifacts | |
with: | |
name: ${{ matrix.bin_name }} | |
- name: Set binary permissions | |
if: runner.os != 'Windows' | |
run: | | |
chmod +x ${{ steps.artifacts.outputs.download-path }}/${{ matrix.bin_name }} | |
- name: Install uv | |
run: | | |
python -m pip install -r https://raw.githubusercontent.com/kdeldycke/workflows/main/requirements/uv.txt | |
- name: Run test plan for binary | |
run: > | |
uvx | |
--with-requirements https://raw.githubusercontent.com/kdeldycke/workflows/main/requirements/gha-utils.txt | |
-- | |
gha-utils test-plan | |
${{ inputs.timeout != null && format('--timeout {0}', inputs.timeout) || '' }} | |
--binary "${{ steps.artifacts.outputs.download-path }}/${{ matrix.bin_name }}" | |
--plan-file "${{ env.test-plan-file }}" | |
${{ env.test-plan != null && '--plan-envvar test-plan' || '' }} | |
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/[email protected] | |
with: | |
ref: ${{ matrix.commit }} | |
# XXX We need custom PAT with workflows permissions because tag generation will work but it will not trigger | |
# any other workflows that use `on.push.tags` triggers. See: | |
# https://stackoverflow.com/questions/60963759/use-github-actions-to-create-a-tag-but-not-a-release#comment135891921_64479344 | |
# https://github.com/orgs/community/discussions/27028 | |
token: ${{ secrets.WORKFLOW_UPDATE_GITHUB_PAT || secrets.GITHUB_TOKEN }} | |
- name: Check if tag exists | |
id: tag_exists | |
run: | | |
echo "tag_exists=$(git show-ref --tags "v${{ matrix.current_version }}" --quiet )" | tee -a "$GITHUB_OUTPUT" | |
- name: Push tag | |
# Skip the tag creation if it already exists instead of failing flat. This allows us to re-run the workflow if | |
# it was interrupted the first time. Which is really useful if the tagging fails during a release: we can | |
# simply push the new tag by hand and re-launch the workflow run. | |
if: ${{ ! steps.tag_exists.outputs.tag_exists }} | |
run: | | |
git tag "v${{ matrix.current_version }}" "${{ matrix.commit }}" | |
git push origin "v${{ matrix.current_version }}" | |
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 | |
permissions: | |
# Allow GitHub's OIDC provider to create a JSON Web Token: | |
# https://github.blog/changelog/2023-06-15-github-actions-securing-openid-connect-oidc-token-permissions-in-reusable-workflows/ | |
# https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect#adding-permissions-settings | |
id-token: write | |
attestations: write | |
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/[email protected] | |
id: download | |
with: | |
name: ${{ github.event.repository.name }}-${{ matrix.short_sha }} | |
- name: Generate attestations | |
uses: actions/[email protected] | |
with: | |
subject-path: "${{ steps.download.outputs.download-path }}/*" | |
- 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 | |
- package-build | |
- compile-binaries | |
- git-tag | |
# 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 | |
permissions: | |
# Allow GitHub's OIDC provider to create a JSON Web Token: | |
# https://github.blog/changelog/2023-06-15-github-actions-securing-openid-connect-oidc-token-permissions-in-reusable-workflows/ | |
# https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect#adding-permissions-settings | |
id-token: write | |
attestations: write | |
# Allow project without WORKFLOW_UPDATE_GITHUB_PAT to create a GitHub release. | |
contents: write | |
steps: | |
- name: Download all artifacts | |
# Do not fetch build artifacts if all jobs producing them were skipped. | |
if: needs.package-build.result != 'skipped' || needs.compile-binaries.result != 'skipped' | |
uses: actions/[email protected] | |
id: artifacts | |
with: | |
path: release_artifact | |
# Only consider artifacts produced by the release commit. | |
pattern: "*-${{ matrix.short_sha }}*" | |
merge-multiple: true | |
- name: Collect all artefacts, rename binaries | |
# Do not try to rename artifacts if none have been downloaded. | |
if: steps.artifacts.outputs.download-path | |
id: collect_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 = """${{ needs.project-metadata.outputs.nuitka_matrix }}""" | |
binaries = {} | |
if nuitka_matrix_json: | |
nuitka_matrix = json.loads(nuitka_matrix_json) | |
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("""-${{ 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: Generate attestations | |
# Do not try to attest artifacts if none have been produced. | |
if: steps.collect_artifacts.outputs.artifacts_path | |
uses: actions/[email protected] | |
with: | |
subject-path: ${{ steps.collect_artifacts.outputs.artifacts_path }} | |
- name: Create GitHub release | |
uses: softprops/[email protected] | |
# XXX We need custom PAT with workflows permissions because tag generation will work but it will not trigger | |
# any other workflows that use `on.push.tags` triggers. See: | |
# https://stackoverflow.com/questions/60963759/use-github-actions-to-create-a-tag-but-not-a-release#comment135891921_64479344 | |
# https://github.com/orgs/community/discussions/27028 | |
env: | |
GITHUB_TOKEN: ${{ secrets.WORKFLOW_UPDATE_GITHUB_PAT || secrets.GITHUB_TOKEN }} | |
with: | |
tag_name: v${{ matrix.current_version }} | |
target_commitish: ${{ matrix.commit }} | |
files: ${{ steps.collect_artifacts.outputs.artifacts_path }} | |
body: ${{ needs.project-metadata.outputs.release_notes }} |