release #16
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: | |
| push: | |
| tags: | |
| - "pika/v*" | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "Version to release (e.g. 0.2.9) — must already be bumped on master" | |
| required: true | |
| type: string | |
| permissions: | |
| contents: write | |
| jobs: | |
| approve-release: | |
| runs-on: ubuntu-latest | |
| environment: | |
| name: release | |
| steps: | |
| - name: Enforce allowed release actors | |
| run: | | |
| set -euo pipefail | |
| case "${GITHUB_ACTOR}" in | |
| justinmoon|futurepaul|benthecarman|AnthonyRonning) ;; | |
| *) | |
| echo "error: actor '${GITHUB_ACTOR}' is not allowed to run release workflow" | |
| exit 1 | |
| ;; | |
| esac | |
| - run: echo "release approved" | |
| tag-release: | |
| if: github.event_name == 'workflow_dispatch' | |
| needs: approve-release | |
| runs-on: ubuntu-latest | |
| outputs: | |
| tag: ${{ steps.tag.outputs.tag }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: master | |
| - name: Validate and tag | |
| id: tag | |
| run: | | |
| set -euo pipefail | |
| version="${{ inputs.version }}" | |
| tag="pika/v${version}" | |
| if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| echo "error: version must be x.y.z (got: $version)" | |
| exit 1 | |
| fi | |
| # Verify the VERSION file already has the right version | |
| current_version="$(./scripts/version-read --name)" | |
| if [ "$current_version" != "$version" ]; then | |
| echo "error: VERSION file says $current_version but expected $version" | |
| echo "hint: bump VERSION first via PR, then run this workflow" | |
| exit 1 | |
| fi | |
| if git rev-parse -q --verify "refs/tags/$tag" >/dev/null 2>&1; then | |
| echo "error: tag already exists: $tag" | |
| exit 1 | |
| fi | |
| git tag "$tag" | |
| git push origin "$tag" | |
| echo "tag=$tag" >> "$GITHUB_OUTPUT" | |
| resolve-tag: | |
| needs: [approve-release, tag-release] | |
| if: always() && needs.approve-release.result == 'success' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| tag: ${{ steps.resolve.outputs.tag }} | |
| steps: | |
| - name: Resolve tag | |
| id: resolve | |
| run: | | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| echo "tag=${{ needs.tag-release.outputs.tag }}" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "tag=${{ github.ref_name }}" >> "$GITHUB_OUTPUT" | |
| fi | |
| check: | |
| needs: resolve-tag | |
| if: always() && needs.resolve-tag.result == 'success' | |
| runs-on: blacksmith-16vcpu-ubuntu-2404 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.resolve-tag.outputs.tag }} | |
| - uses: useblacksmith/stickydisk@v1 | |
| with: | |
| key: ${{ github.repository }}-nix-v1-${{ runner.os }} | |
| path: /nix | |
| - name: Fix /nix ownership | |
| run: | | |
| if [ -d /nix ] && [ "$(stat -c %u /nix)" != "$(id -u)" ]; then | |
| sudo chown -R "$(id -u):$(id -g)" /nix | |
| fi | |
| - uses: nixbuild/nix-quick-install-action@v30 | |
| - uses: useblacksmith/stickydisk@v1 | |
| with: | |
| key: ${{ github.repository }}-cargo-home-v1-${{ runner.os }} | |
| path: ~/.cargo | |
| - uses: useblacksmith/stickydisk@v1 | |
| with: | |
| key: ${{ github.repository }}-cargo-target-v1-${{ runner.os }} | |
| path: target | |
| - uses: useblacksmith/stickydisk@v1 | |
| with: | |
| key: ${{ github.repository }}-gradle-v1-${{ runner.os }} | |
| path: ~/.gradle | |
| - name: Validate tag matches VERSION | |
| run: | | |
| set -euo pipefail | |
| expected_tag="pika/v$(./scripts/version-read --name)" | |
| tag="${{ needs.resolve-tag.outputs.tag }}" | |
| if [ "$tag" != "$expected_tag" ]; then | |
| echo "error: tag/version mismatch: tag=${tag}, expected=${expected_tag}" | |
| exit 1 | |
| fi | |
| - run: nix develop .#default -c just pre-merge-pika | |
| build-android: | |
| needs: [resolve-tag, check] | |
| if: always() && needs.resolve-tag.result == 'success' && needs.check.result == 'success' | |
| runs-on: blacksmith-16vcpu-ubuntu-2404 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.resolve-tag.outputs.tag }} | |
| - uses: useblacksmith/stickydisk@v1 | |
| with: | |
| key: ${{ github.repository }}-nix-v1-${{ runner.os }} | |
| path: /nix | |
| - name: Fix /nix ownership | |
| run: | | |
| if [ -d /nix ] && [ "$(stat -c %u /nix)" != "$(id -u)" ]; then | |
| sudo chown -R "$(id -u):$(id -g)" /nix | |
| fi | |
| - uses: nixbuild/nix-quick-install-action@v30 | |
| - uses: useblacksmith/stickydisk@v1 | |
| with: | |
| key: ${{ github.repository }}-cargo-home-v1-${{ runner.os }} | |
| path: ~/.cargo | |
| - uses: useblacksmith/stickydisk@v1 | |
| with: | |
| key: ${{ github.repository }}-cargo-target-v1-${{ runner.os }} | |
| path: target | |
| - uses: useblacksmith/stickydisk@v1 | |
| with: | |
| key: ${{ github.repository }}-gradle-v1-${{ runner.os }} | |
| path: ~/.gradle | |
| - name: Build signed release APK | |
| env: | |
| AGE_SECRET_KEY: ${{ secrets.AGE_SECRET_KEY }} | |
| run: nix develop .#default -c just android-release | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: release-android | |
| path: dist/*.apk | |
| if-no-files-found: error | |
| build-macos: | |
| needs: [resolve-tag, check] | |
| if: always() && needs.resolve-tag.result == 'success' && needs.check.result == 'success' | |
| runs-on: macos-14 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.resolve-tag.outputs.tag }} | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: aarch64-apple-darwin,x86_64-apple-darwin | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Build macOS universal DMG | |
| run: ./scripts/build-macos-release | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: release-macos | |
| path: dist/*.dmg | |
| if-no-files-found: error | |
| publish: | |
| needs: [resolve-tag, build-android, build-macos] | |
| if: always() && needs.resolve-tag.result == 'success' && needs.build-android.result == 'success' && needs.build-macos.result == 'success' | |
| runs-on: blacksmith-16vcpu-ubuntu-2404 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.resolve-tag.outputs.tag }} | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: release-android | |
| path: dist | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: release-macos | |
| path: dist | |
| - name: Generate checksums | |
| run: sha256sum dist/*.apk dist/*.dmg > dist/SHA256SUMS | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ needs.resolve-tag.outputs.tag }} | |
| name: Pika ${{ needs.resolve-tag.outputs.tag }} | |
| generate_release_notes: true | |
| overwrite_files: true | |
| files: | | |
| dist/*.apk | |
| dist/*.dmg | |
| dist/SHA256SUMS | |
| publish-zapstore: | |
| needs: [resolve-tag, build-android, publish] | |
| if: always() && needs.resolve-tag.result == 'success' && needs.build-android.result == 'success' && needs.publish.result == 'success' | |
| runs-on: blacksmith-16vcpu-ubuntu-2404 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.resolve-tag.outputs.tag }} | |
| - name: Check Zapstore signing secret | |
| id: zapstore_secret | |
| run: | | |
| set -euo pipefail | |
| if [ -f secrets/zapstore-signing.env.age ]; then | |
| echo "present=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "present=false" >> "$GITHUB_OUTPUT" | |
| echo "note: secrets/zapstore-signing.env.age not found; skipping Zapstore publish" | |
| fi | |
| - uses: useblacksmith/stickydisk@v1 | |
| if: steps.zapstore_secret.outputs.present == 'true' | |
| with: | |
| key: ${{ github.repository }}-nix-v1-${{ runner.os }} | |
| path: /nix | |
| - name: Fix /nix ownership | |
| if: steps.zapstore_secret.outputs.present == 'true' | |
| run: | | |
| if [ -d /nix ] && [ "$(stat -c %u /nix)" != "$(id -u)" ]; then | |
| sudo chown -R "$(id -u):$(id -g)" /nix | |
| fi | |
| - uses: nixbuild/nix-quick-install-action@v30 | |
| if: steps.zapstore_secret.outputs.present == 'true' | |
| - uses: actions/download-artifact@v4 | |
| if: steps.zapstore_secret.outputs.present == 'true' | |
| with: | |
| name: release-android | |
| path: dist | |
| - name: Publish to Zapstore | |
| if: steps.zapstore_secret.outputs.present == 'true' | |
| env: | |
| AGE_SECRET_KEY: ${{ secrets.AGE_SECRET_KEY }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| set +x | |
| apk_count="$(find dist -maxdepth 1 -type f -name '*.apk' | wc -l | xargs)" | |
| if [ "$apk_count" -ne 1 ]; then | |
| echo "error: expected exactly one APK artifact in dist/, found $apk_count" | |
| find dist -maxdepth 1 -type f -name '*.apk' -print | |
| exit 1 | |
| fi | |
| apk_path="$(find dist -maxdepth 1 -type f -name '*.apk' | head -n 1)" | |
| nix develop .#default -c ./scripts/zapstore-publish "$apk_path" "https://github.com/${GITHUB_REPOSITORY}" | |
| announce-release: | |
| needs: [resolve-tag, publish] | |
| if: always() && needs.resolve-tag.result == 'success' && needs.publish.result == 'success' | |
| runs-on: blacksmith-16vcpu-ubuntu-2404 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.resolve-tag.outputs.tag }} | |
| - name: Ensure announcement signing secret exists | |
| run: | | |
| set -euo pipefail | |
| if [ ! -f secrets/zapstore-signing.env.age ]; then | |
| echo "error: missing secrets/zapstore-signing.env.age (required for release announcement signing)" | |
| exit 1 | |
| fi | |
| - uses: useblacksmith/stickydisk@v1 | |
| with: | |
| key: ${{ github.repository }}-nix-v1-${{ runner.os }} | |
| path: /nix | |
| - name: Fix /nix ownership | |
| run: | | |
| if [ -d /nix ] && [ "$(stat -c %u /nix)" != "$(id -u)" ]; then | |
| sudo chown -R "$(id -u):$(id -g)" /nix | |
| fi | |
| - uses: nixbuild/nix-quick-install-action@v30 | |
| - name: Publish release announcement (kind 1) | |
| env: | |
| AGE_SECRET_KEY: ${{ secrets.AGE_SECRET_KEY }} | |
| run: | | |
| set -euo pipefail | |
| set +x | |
| tag="${{ needs.resolve-tag.outputs.tag }}" | |
| repo_url="https://github.com/${GITHUB_REPOSITORY}" | |
| nix develop .#default -c ./scripts/post-release-announcement "$tag" "$repo_url" |