Release #52
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 | |
| 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 |