Skip to content

release

release #16

Workflow file for this run

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"