Skip to content

Release

Release #52

Workflow file for this run

name: Release
on:
workflow_dispatch:
inputs:
dry_run:
description: Build and upload artifacts without creating releases or publishing.
required: true
type: boolean
default: true
create_github_release:
description: Create a GitHub release and attach the built artifacts.
required: true
type: boolean
default: false
tag_name:
description: Release tag to create, for example v0.1.1. Required when create_github_release is true.
required: false
type: string
target:
description: Branch, tag, or commit SHA to build and release.
required: true
type: string
default: main
draft:
description: Create the GitHub release as a draft.
required: true
type: boolean
default: true
prerelease:
description: Mark the GitHub release as a prerelease.
required: true
type: boolean
default: true
publish_testpypi:
description: Publish the built artifacts to TestPyPI with Trusted Publishing.
required: true
type: boolean
default: false
publish_pypi:
description: Publish the built artifacts to PyPI with Trusted Publishing.
required: true
type: boolean
default: false
force_flatpak_runtime_smoke:
description: Run the Flatpak routing runtime smoke even when runtime-sensitive files did not change.
required: true
type: boolean
default: false
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.target }}-${{ inputs.tag_name || 'dry-run' }}
cancel-in-progress: false
jobs:
validate-inputs:
name: Validate release inputs
runs-on: ubuntu-24.04
steps:
- name: Check out repository
uses: actions/checkout@v6
with:
ref: ${{ inputs.target }}
- name: Validate dispatch inputs
env:
CREATE_GITHUB_RELEASE: ${{ inputs.create_github_release }}
DRY_RUN: ${{ inputs.dry_run }}
PUBLISH_PYPI: ${{ inputs.publish_pypi }}
PUBLISH_TESTPYPI: ${{ inputs.publish_testpypi }}
TAG_NAME: ${{ inputs.tag_name }}
run: |
set -euo pipefail
project_version="$(
python3 -c 'from pathlib import Path; import tomllib; print(tomllib.loads(Path("pyproject.toml").read_text())["project"]["version"])'
)"
expected_tag="v${project_version}"
needs_tag=false
if [ "$CREATE_GITHUB_RELEASE" = "true" ] || [ "$PUBLISH_TESTPYPI" = "true" ] || [ "$PUBLISH_PYPI" = "true" ]; then
needs_tag=true
fi
if [ "$needs_tag" = "true" ] && [ -z "$TAG_NAME" ]; then
echo "::error::tag_name is required when creating a release or publishing to a package index"
exit 1
fi
if [ -n "$TAG_NAME" ] && [ "$TAG_NAME" != "$expected_tag" ]; then
echo "::error::tag_name must be ${expected_tag} for pyproject version ${project_version}"
exit 1
fi
if [ "$DRY_RUN" = "true" ] && { [ "$CREATE_GITHUB_RELEASE" = "true" ] || [ "$PUBLISH_TESTPYPI" = "true" ] || [ "$PUBLISH_PYPI" = "true" ]; }; then
echo "::warning::dry_run=true prevents release creation and package publishing jobs from running"
fi
if [ "$PUBLISH_PYPI" = "true" ] && [ "$CREATE_GITHUB_RELEASE" != "true" ]; then
echo "::warning::publishing PyPI without creating the GitHub release uses a separate build from release assets"
fi
runtime-risk:
name: Detect runtime smoke gate
needs: validate-inputs
runs-on: ubuntu-24.04
outputs:
required: ${{ steps.detect.outputs.required }}
base_tag: ${{ steps.detect.outputs.base_tag }}
steps:
- name: Check out repository
uses: actions/checkout@v6
with:
ref: ${{ inputs.target }}
fetch-depth: 0
- name: Detect runtime-sensitive changes
id: detect
env:
CREATE_GITHUB_RELEASE: ${{ inputs.create_github_release }}
DRY_RUN: ${{ inputs.dry_run }}
FORCE_FLATPAK_RUNTIME_SMOKE: ${{ inputs.force_flatpak_runtime_smoke }}
PUBLISH_PYPI: ${{ inputs.publish_pypi }}
PUBLISH_TESTPYPI: ${{ inputs.publish_testpypi }}
TAG_NAME: ${{ inputs.tag_name }}
run: |
set -euo pipefail
release_requested=false
if { [ "$CREATE_GITHUB_RELEASE" = "true" ] || [ "$PUBLISH_TESTPYPI" = "true" ] || [ "$PUBLISH_PYPI" = "true" ]; } \
&& [ "$DRY_RUN" = "false" ]; then
release_requested=true
fi
python3 tools/release_runtime_gate.py \
--scope flatpak \
--tag "$TAG_NAME" \
--release-requested "$release_requested" \
--force "$FORCE_FLATPAK_RUNTIME_SMOKE" \
--github-output "$GITHUB_OUTPUT" \
--github-summary "$GITHUB_STEP_SUMMARY"
flatpak-runtime-smoke:
name: Flatpak routing runtime smoke
needs: runtime-risk
if: ${{ needs['runtime-risk'].outputs.required == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 90
steps:
- name: Check out repository
uses: actions/checkout@v6
with:
ref: ${{ inputs.target }}
- name: Install Flatpak and PipeWire test dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
dbus-user-session \
flatpak \
jq \
pipewire \
pipewire-bin \
wireplumber
- name: Build and run Flatpak routing runtime smoke
run: tools/run_flatpak_runtime_smoke_ci.sh
runtime-gate:
name: Require runtime smoke gate
needs:
- runtime-risk
- flatpak-runtime-smoke
if: ${{ always() }}
runs-on: ubuntu-24.04
steps:
- name: Require runtime-risk job
if: ${{ needs['runtime-risk'].result != 'success' }}
run: exit 1
- name: Require Flatpak routing smoke
if: ${{ needs['runtime-risk'].outputs.required == 'true' && needs['flatpak-runtime-smoke'].result != 'success' }}
run: |
echo "::error::Flatpak routing runtime smoke is required for this release and did not pass."
exit 1
- name: Confirm runtime gate
run: |
if [ "${{ needs['runtime-risk'].outputs.required }}" = "true" ]; then
echo "Required Flatpak routing runtime smoke passed."
else
echo "Flatpak routing runtime smoke was not required for this dispatch."
fi
preflight:
name: Release preflight
needs: validate-inputs
runs-on: ubuntu-24.04
timeout-minutes: 60
steps:
- name: Check out repository
uses: actions/checkout@v6
with:
ref: ${{ inputs.target }}
fetch-depth: 0
- name: Run release preflight
run: tools/run_release_preflight_container.sh
build:
name: Build release artifacts
needs:
- validate-inputs
- runtime-gate
- preflight
runs-on: ubuntu-24.04
steps:
- name: Check out repository
uses: actions/checkout@v6
with:
ref: ${{ inputs.target }}
- name: Install packaging tools
run: |
python3 -m venv .venv
.venv/bin/python -m pip install --upgrade pip
.venv/bin/python -m pip install build twine
- name: Build package
run: .venv/bin/python -m build
- name: Check package metadata
run: .venv/bin/python -m twine check dist/*
- name: Upload package artifacts
uses: actions/upload-artifact@v7
with:
name: mini-eq-dist
path: dist/*
if-no-files-found: error
create-release:
name: Create GitHub release
needs: build
if: ${{ inputs.dry_run == false && inputs.create_github_release == true }}
runs-on: ubuntu-24.04
permissions:
contents: write
steps:
- name: Check out repository
uses: actions/checkout@v6
with:
ref: ${{ inputs.target }}
- name: Download package artifacts
uses: actions/download-artifact@v8
with:
name: mini-eq-dist
path: dist
- name: Create GitHub release
env:
DRAFT: ${{ inputs.draft }}
GH_TOKEN: ${{ github.token }}
PRERELEASE: ${{ inputs.prerelease }}
TAG_NAME: ${{ inputs.tag_name }}
TARGET: ${{ inputs.target }}
run: |
set -euo pipefail
args=(
release create "$TAG_NAME"
dist/*
--repo "$GITHUB_REPOSITORY"
--target "$TARGET"
--title "$TAG_NAME"
--generate-notes
--fail-on-no-commits
)
if [ "$DRAFT" = "true" ]; then
args+=(--draft)
fi
if [ "$PRERELEASE" = "true" ]; then
args+=(--prerelease)
fi
gh "${args[@]}"
publish-testpypi:
name: Publish to TestPyPI
needs: build
if: ${{ inputs.dry_run == false && inputs.publish_testpypi == true }}
runs-on: ubuntu-24.04
environment:
name: testpypi
url: https://test.pypi.org/project/mini-eq/
permissions:
contents: read
id-token: write
steps:
- name: Download package artifacts
uses: actions/download-artifact@v8
with:
name: mini-eq-dist
path: dist
- name: Publish package distributions to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
publish-pypi:
name: Publish to PyPI
needs: build
if: ${{ inputs.dry_run == false && inputs.publish_pypi == true }}
runs-on: ubuntu-24.04
environment:
name: pypi
url: https://pypi.org/project/mini-eq/
permissions:
contents: read
id-token: write
steps:
- name: Download package artifacts
uses: actions/download-artifact@v8
with:
name: mini-eq-dist
path: dist
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1