Prepare the 0.3.1 release (#23) #6
Workflow file for this run
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: | |
| - 'v*' | |
| workflow_dispatch: | |
| inputs: | |
| release_tag: | |
| description: Optional release tag (defaults to `v` plus the synchronised workspace version) | |
| required: false | |
| type: string | |
| release_draft: | |
| description: Create or keep this release as a draft | |
| required: false | |
| default: false | |
| type: boolean | |
| permissions: | |
| contents: write | |
| packages: read | |
| concurrency: | |
| group: release-${{ github.event_name }}-${{ github.event_name == 'workflow_dispatch' && (inputs.release_tag || github.sha) || github.ref_name }} | |
| cancel-in-progress: false | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true' | |
| defaults: | |
| run: | |
| shell: bash | |
| jobs: | |
| prepare-release: | |
| name: Prepare release | |
| runs-on: ubuntu-24.04 | |
| outputs: | |
| release_version: ${{ steps.meta.outputs.release_version }} | |
| tag_name: ${{ steps.meta.outputs.tag_name }} | |
| checkout_ref: ${{ steps.meta.outputs.checkout_ref }} | |
| release_target: ${{ steps.meta.outputs.release_target }} | |
| release_name: ${{ steps.meta.outputs.release_name }} | |
| prerelease: ${{ steps.meta.outputs.prerelease }} | |
| release_id: ${{ steps.release.outputs.release_id }} | |
| release_url: ${{ steps.release.outputs.release_url }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: .node-version | |
| - name: Resolve release metadata | |
| id: meta | |
| env: | |
| MANUAL_RELEASE_TAG: ${{ github.event.inputs.release_tag || '' }} | |
| run: | | |
| set -euo pipefail | |
| git fetch origin main | |
| git fetch --tags origin | |
| CHECKOUT_REF="${GITHUB_SHA}" | |
| RELEASE_TARGET="${GITHUB_SHA}" | |
| if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" && -n "${MANUAL_RELEASE_TAG}" ]]; then | |
| TAG_NAME="${MANUAL_RELEASE_TAG}" | |
| if [[ ! "${TAG_NAME}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then | |
| echo "Invalid release tag: ${TAG_NAME}" >&2 | |
| echo "Expected a tag matching vX.Y.Z or vX.Y.Z-beta.N" >&2 | |
| exit 1 | |
| fi | |
| CHECKOUT_REF="${TAG_NAME}" | |
| RELEASE_TARGET="$(git rev-list -n 1 "${TAG_NAME}")" | |
| elif [[ "${GITHUB_EVENT_NAME}" == "push" ]]; then | |
| TAG_NAME="${GITHUB_REF_NAME}" | |
| if [[ ! "${TAG_NAME}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then | |
| echo "Invalid release tag: ${TAG_NAME}" >&2 | |
| echo "Expected a tag matching vX.Y.Z or vX.Y.Z-beta.N" >&2 | |
| exit 1 | |
| fi | |
| fi | |
| if ! git merge-base --is-ancestor "${RELEASE_TARGET}" "origin/main"; then | |
| echo "Release commit is not on origin/main: ${RELEASE_TARGET}" >&2 | |
| exit 1 | |
| fi | |
| git checkout --quiet "${CHECKOUT_REF}" | |
| RELEASE_VERSION="$(./scripts/check-release-versions.sh --current-version)" | |
| if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" && -z "${MANUAL_RELEASE_TAG}" ]]; then | |
| TAG_NAME="v${RELEASE_VERSION}" | |
| fi | |
| if [[ "${TAG_NAME#v}" != "${RELEASE_VERSION}" ]]; then | |
| echo "Release tag ${TAG_NAME} does not match the synchronised manifest version ${RELEASE_VERSION}" >&2 | |
| exit 1 | |
| fi | |
| PRERELEASE=false | |
| if [[ "${TAG_NAME}" == *-* ]]; then | |
| PRERELEASE=true | |
| fi | |
| echo "release_version=${RELEASE_VERSION}" >> "${GITHUB_OUTPUT}" | |
| echo "tag_name=${TAG_NAME}" >> "${GITHUB_OUTPUT}" | |
| echo "checkout_ref=${CHECKOUT_REF}" >> "${GITHUB_OUTPUT}" | |
| echo "release_target=${RELEASE_TARGET}" >> "${GITHUB_OUTPUT}" | |
| echo "release_name=Emoji Nook ${TAG_NAME}" >> "${GITHUB_OUTPUT}" | |
| echo "prerelease=${PRERELEASE}" >> "${GITHUB_OUTPUT}" | |
| - name: Create or reuse GitHub Release | |
| id: release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| TAG_NAME: ${{ steps.meta.outputs.tag_name }} | |
| RELEASE_TARGET: ${{ steps.meta.outputs.release_target }} | |
| RELEASE_NAME: ${{ steps.meta.outputs.release_name }} | |
| RELEASE_DRAFT: ${{ github.event_name == 'workflow_dispatch' && inputs.release_draft || false }} | |
| RELEASE_PRERELEASE: ${{ steps.meta.outputs.prerelease }} | |
| run: | | |
| set -euo pipefail | |
| RELEASE_MATCH="$( | |
| gh api --paginate "repos/${GITHUB_REPOSITORY}/releases?per_page=100" \ | |
| --jq ".[] | select(.tag_name == \"${TAG_NAME}\") | [.id, .html_url] | @tsv" \ | |
| | head -n 1 || true | |
| )" | |
| if [[ -z "${RELEASE_MATCH}" ]]; then | |
| RELEASE_JSON="$( | |
| gh api \ | |
| --method POST \ | |
| "repos/${GITHUB_REPOSITORY}/releases" \ | |
| -f tag_name="${TAG_NAME}" \ | |
| -f target_commitish="${RELEASE_TARGET}" \ | |
| -f name="${RELEASE_NAME}" \ | |
| -f body='See assets below to download and install.' \ | |
| -F generate_release_notes=true \ | |
| -F draft="${RELEASE_DRAFT}" \ | |
| -F prerelease="${RELEASE_PRERELEASE}" | |
| )" | |
| RELEASE_ID="$(printf '%s' "${RELEASE_JSON}" | jq -r '.id')" | |
| RELEASE_URL="$(printf '%s' "${RELEASE_JSON}" | jq -r '.html_url')" | |
| RELEASE_ACTION="created" | |
| else | |
| RELEASE_ID="$(awk '{print $1}' <<< "${RELEASE_MATCH}")" | |
| RELEASE_URL="$(awk '{print $2}' <<< "${RELEASE_MATCH}")" | |
| gh api \ | |
| --method PATCH \ | |
| "repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}" \ | |
| -f name="${RELEASE_NAME}" \ | |
| -F draft="${RELEASE_DRAFT}" \ | |
| -F prerelease="${RELEASE_PRERELEASE}" \ | |
| >/dev/null | |
| RELEASE_ACTION="reused" | |
| fi | |
| echo "release_id=${RELEASE_ID}" >> "${GITHUB_OUTPUT}" | |
| echo "release_url=${RELEASE_URL}" >> "${GITHUB_OUTPUT}" | |
| echo "release_action=${RELEASE_ACTION}" >> "${GITHUB_OUTPUT}" | |
| - name: Write summary | |
| if: always() | |
| run: | | |
| { | |
| echo "## Emoji Nook Release Summary" | |
| echo | |
| echo "- Resolved version: \`${{ steps.meta.outputs.release_version || 'unavailable' }}\`" | |
| echo "- Release tag: \`${{ steps.meta.outputs.tag_name || 'unavailable' }}\`" | |
| echo "- Checkout ref: \`${{ steps.meta.outputs.checkout_ref || 'unavailable' }}\`" | |
| echo "- Release target: \`${{ steps.meta.outputs.release_target || 'unavailable' }}\`" | |
| echo "- Release name: \`${{ steps.meta.outputs.release_name || 'unavailable' }}\`" | |
| echo "- Release action: \`${{ steps.release.outputs.release_action || 'unavailable' }}\`" | |
| echo "- Draft: \`${{ github.event_name == 'workflow_dispatch' && inputs.release_draft || false }}\`" | |
| echo "- Prerelease: \`${{ steps.meta.outputs.prerelease || 'unavailable' }}\`" | |
| echo "- Release URL: ${{ steps.release.outputs.release_url || 'unavailable' }}" | |
| echo "- Run URL: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" | |
| } >> "${GITHUB_STEP_SUMMARY}" | |
| build-linux: | |
| name: Build Linux release bundles | |
| needs: prepare-release | |
| runs-on: ${{ matrix.runner }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - runner: ubuntu-24.04 | |
| platform_label: linux | |
| arch_label: x64 | |
| artefact_name: linux-release-assets-x64 | |
| - runner: ubuntu-24.04-arm | |
| platform_label: linux | |
| arch_label: arm64 | |
| artefact_name: linux-release-assets-arm64 | |
| container: | |
| image: ghcr.io/liminal-hq/tauri-ci-desktop:latest | |
| credentials: | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ needs.prepare-release.outputs.checkout_ref }} | |
| - name: Trust the GitHub workspace | |
| run: git config --global --add safe.directory "${GITHUB_WORKSPACE}" | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: .node-version | |
| cache: pnpm | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache Rust | |
| uses: swatinem/rust-cache@v2 | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Check release version synchronisation | |
| run: pnpm release:version:check | |
| - name: Build Linux release bundles | |
| id: build_tauri | |
| run: pnpm --filter @emoji-picker/emoji-picker tauri build --bundles deb,rpm | |
| - name: Collect release assets and checksums | |
| id: collect_assets | |
| run: | | |
| set -euo pipefail | |
| bundle_dir="target/release/bundle" | |
| release_dir="release-assets/linux" | |
| mkdir -p "${release_dir}" | |
| if [[ ! -d "${bundle_dir}" ]]; then | |
| echo "Bundle directory not found: ${bundle_dir}" >&2 | |
| exit 1 | |
| fi | |
| mapfile -t bundle_files < <(find "${bundle_dir}" -type f \( -name "*.deb" -o -name "*.rpm" \) | sort) | |
| if [[ "${#bundle_files[@]}" -eq 0 ]]; then | |
| echo "No Linux release artefacts found in ${bundle_dir}" >&2 | |
| exit 1 | |
| fi | |
| for file in "${bundle_files[@]}"; do | |
| cp "${file}" "${release_dir}/" | |
| done | |
| ( | |
| cd "${release_dir}" | |
| sha256sum ./* | sed 's# \./# #' > SHA256SUMS | |
| ) | |
| echo "asset_count=$(find "${release_dir}" -maxdepth 1 -type f | wc -l | tr -d ' ')" >> "${GITHUB_OUTPUT}" | |
| - name: Upload staged release assets | |
| id: upload_assets | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: ${{ matrix.artefact_name }} | |
| path: release-assets/linux/** | |
| if-no-files-found: error | |
| - name: Write summary | |
| if: always() | |
| run: | | |
| release_dir="release-assets/linux" | |
| asset_count=0 | |
| if [[ -d "${release_dir}" ]]; then | |
| asset_count="$(find "${release_dir}" -maxdepth 1 -type f | wc -l | tr -d ' ')" | |
| fi | |
| { | |
| echo "## Emoji Nook Linux Release Summary" | |
| echo | |
| echo "- Runner: \`${{ matrix.runner }}\`" | |
| echo "- Container: \`ghcr.io/liminal-hq/tauri-ci-desktop:latest\`" | |
| echo "- Platform: \`${{ matrix.platform_label }}\`" | |
| echo "- Architecture: \`${{ matrix.arch_label }}\`" | |
| echo "- Release tag: \`${{ needs.prepare-release.outputs.tag_name }}\`" | |
| echo "- Build and bundle: \`${{ steps.build_tauri.outcome }}\`" | |
| echo "- Collect assets: \`${{ steps.collect_assets.outcome }}\`" | |
| echo "- Stage artefacts: \`${{ steps.upload_assets.outcome }}\`" | |
| echo "- Assets prepared: ${asset_count}" | |
| echo "- Release URL: ${{ needs.prepare-release.outputs.release_url }}" | |
| echo "- Run URL: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" | |
| echo | |
| echo "### Staged assets" | |
| if [[ "${asset_count}" -gt 0 ]]; then | |
| find "${release_dir}" -maxdepth 1 -type f -printf '%f\n' | sort | sed 's#^#- `#; s#$#`#' | |
| else | |
| echo "- _No assets were staged_" | |
| fi | |
| } >> "${GITHUB_STEP_SUMMARY}" | |
| package-appimage: | |
| name: Package AppImage | |
| needs: | |
| - prepare-release | |
| - build-linux | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - runner: ubuntu-24.04 | |
| deb-artifact: linux-release-assets-x64 | |
| - runner: ubuntu-24.04-arm | |
| deb-artifact: linux-release-assets-arm64 | |
| uses: liminal-hq/.github/.github/workflows/package-arch-appimage.yml@main | |
| with: | |
| runs-on: ${{ matrix.runner }} | |
| deb-artifact-name: ${{ matrix.deb-artifact }} | |
| app-version: ${{ needs.prepare-release.outputs.release_version }} | |
| secrets: inherit | |
| publish-release: | |
| name: Publish GitHub Release assets | |
| needs: | |
| - prepare-release | |
| - build-linux | |
| - package-appimage | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Download Linux release assets | |
| uses: actions/download-artifact@v7 | |
| with: | |
| pattern: linux-release-assets-* | |
| path: release-assets/linux | |
| merge-multiple: true | |
| - name: Download AppImage assets | |
| uses: actions/download-artifact@v7 | |
| with: | |
| pattern: appimage-* | |
| path: release-assets/linux | |
| merge-multiple: true | |
| - name: Recompute checksums | |
| # build-linux's own SHA256SUMS only covers the deb/rpm files present | |
| # at that point - the AppImage doesn't exist yet in that job. Regenerate | |
| # over the fully merged directory now that everything has converged. | |
| run: | | |
| set -euo pipefail | |
| cd release-assets/linux | |
| rm -f SHA256SUMS | |
| sha256sum ./* | sed 's# \./# #' > SHA256SUMS | |
| - name: Upload assets to GitHub Release | |
| id: upload_release_assets | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| TAG_NAME: ${{ needs.prepare-release.outputs.tag_name }} | |
| run: gh release upload "${TAG_NAME}" release-assets/linux/* --clobber | |
| - name: Write summary | |
| if: always() | |
| run: | | |
| release_dir="release-assets/linux" | |
| asset_count=0 | |
| checksum_present="no" | |
| if [[ -d "${release_dir}" ]]; then | |
| asset_count="$(find "${release_dir}" -maxdepth 1 -type f | wc -l | tr -d ' ')" | |
| if [[ -f "${release_dir}/SHA256SUMS" ]]; then | |
| checksum_present="yes" | |
| fi | |
| fi | |
| { | |
| echo "## Emoji Nook Release Publish Summary" | |
| echo | |
| echo "- Runner: \`ubuntu-24.04\`" | |
| echo "- Release tag: \`${{ needs.prepare-release.outputs.tag_name }}\`" | |
| echo "- Upload assets: \`${{ steps.upload_release_assets.outcome }}\`" | |
| echo "- Attached asset count: ${asset_count}" | |
| echo "- Checksum file present: ${checksum_present}" | |
| echo "- Release URL: ${{ needs.prepare-release.outputs.release_url }}" | |
| echo "- Run URL: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" | |
| echo | |
| echo "### Uploaded assets" | |
| if [[ "${asset_count}" -gt 0 ]]; then | |
| find "${release_dir}" -maxdepth 1 -type f -printf '%f\n' | sort | sed 's#^#- `#; s#$#`#' | |
| else | |
| echo "- _No assets were downloaded_" | |
| fi | |
| } >> "${GITHUB_STEP_SUMMARY}" |