Release #37
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: 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 | |
| force_live_ui_runtime_smoke: | |
| description: Run the live GTK/AT-SPI/PipeWire smoke even when app-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" | |
| live-ui-risk: | |
| name: Detect live UI 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 app and UI runtime-sensitive changes | |
| id: detect | |
| env: | |
| CREATE_GITHUB_RELEASE: ${{ inputs.create_github_release }} | |
| DRY_RUN: ${{ inputs.dry_run }} | |
| FORCE_LIVE_UI_RUNTIME_SMOKE: ${{ inputs.force_live_ui_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 live-ui \ | |
| --tag "$TAG_NAME" \ | |
| --release-requested "$release_requested" \ | |
| --force "$FORCE_LIVE_UI_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 | |
| live-ui-runtime-smoke: | |
| name: Live UI runtime smoke | |
| needs: live-ui-risk | |
| if: ${{ needs['live-ui-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 live UI runtime dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y \ | |
| appstream \ | |
| at-spi2-core \ | |
| build-essential \ | |
| dbus-user-session \ | |
| desktop-file-utils \ | |
| gir1.2-adw-1 \ | |
| gir1.2-gtk-4.0 \ | |
| git \ | |
| gnome-shell \ | |
| gobject-introspection \ | |
| libebur128-1 \ | |
| libgirepository1.0-dev \ | |
| libglib2.0-dev \ | |
| libpipewire-0.3-dev \ | |
| libspa-0.2-modules \ | |
| meson \ | |
| ninja-build \ | |
| pipewire \ | |
| pipewire-bin \ | |
| pkg-config \ | |
| python3 \ | |
| python3-cairo \ | |
| python3-dev \ | |
| python3-gi \ | |
| python3-numpy \ | |
| python3-pip \ | |
| python3-pyatspi \ | |
| python3-setuptools \ | |
| python3-venv \ | |
| wireplumber | |
| - name: Build source environment and run live UI smoke | |
| run: tools/run_live_ui_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 | |
| live-ui-gate: | |
| name: Require live UI smoke gate | |
| needs: | |
| - live-ui-risk | |
| - live-ui-runtime-smoke | |
| if: ${{ always() }} | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Require live-ui-risk job | |
| if: ${{ needs['live-ui-risk'].result != 'success' }} | |
| run: exit 1 | |
| - name: Require live UI smoke | |
| if: ${{ needs['live-ui-risk'].outputs.required == 'true' && needs['live-ui-runtime-smoke'].result != 'success' }} | |
| run: | | |
| echo "::error::Live GTK/AT-SPI/PipeWire runtime smoke is required for this release and did not pass." | |
| exit 1 | |
| - name: Confirm live UI gate | |
| run: | | |
| if [ "${{ needs['live-ui-risk'].outputs.required }}" = "true" ]; then | |
| echo "Required live UI runtime smoke passed." | |
| else | |
| echo "Live UI 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: Install release preflight dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y \ | |
| appstream \ | |
| build-essential \ | |
| dbus-user-session \ | |
| desktop-file-utils \ | |
| gir1.2-adw-1 \ | |
| gir1.2-gtk-4.0 \ | |
| git \ | |
| gnome-shell \ | |
| gobject-introspection \ | |
| libebur128-1 \ | |
| libgirepository1.0-dev \ | |
| libglib2.0-dev \ | |
| libpipewire-0.3-dev \ | |
| libspa-0.2-modules \ | |
| meson \ | |
| ninja-build \ | |
| pipewire \ | |
| pkg-config \ | |
| python3 \ | |
| python3-cairo \ | |
| python3-dev \ | |
| python3-gi \ | |
| python3-numpy \ | |
| python3-pip \ | |
| python3-setuptools \ | |
| python3-venv \ | |
| wireplumber | |
| - name: Run release preflight | |
| run: docker/run-release-preflight.sh | |
| build: | |
| name: Build release artifacts | |
| needs: | |
| - validate-inputs | |
| - runtime-gate | |
| - live-ui-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 |