fix(release): filter desktop-only tags and consolidate release pipeline (#1386)
- release-cut.yml: require `^v[0-9]` tag shape when picking "latest
stable" from GitHub releases. Previously only `-rc.*` was excluded, so
publishing `mobile-v0.0.1` made it the latest stable, `strip_pre()`
reduced it to `mobile`, `Number("mobile")` → NaN → 0, and a patch-bump
produced `v0.0.1` (run 25304336767).
- homebrew-bump.yml: drop the blanket `release.published` trigger and
add tag-shape guards. Any published release in this repo (including
mobile) would otherwise fire the tap bump. Workflow_call from the
desktop pipeline is the only intended path now.
- Inline create-release / build / publish-release / e2e / homebrew-bump
jobs from release.yml into release-cut.yml, and delete release.yml.
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
parent
98b484eb9b
commit
c58e06383c
|
|
@ -80,7 +80,7 @@ All releases are cut from the **Cut Release** GitHub Actions workflow. There is
|
|||
- **ref**: the branch, tag, or SHA to build from. Defaults to `main`.
|
||||
3. Run it.
|
||||
|
||||
The workflow resolves the next version from GitHub Releases, bumps `package.json`, tags, pushes, and kicks off the multi-platform build via `release.yml`.
|
||||
The workflow resolves the next version from GitHub Releases, bumps `package.json`, tags, pushes, and runs the multi-platform build + publish inline.
|
||||
|
||||
**How the next version is chosen:**
|
||||
|
||||
|
|
|
|||
|
|
@ -2,15 +2,21 @@ name: Homebrew Cask Bump
|
|||
|
||||
# Why: keep the stablyai/homebrew-orca tap in sync with GitHub Releases.
|
||||
# Triggers:
|
||||
# - release.published with !prerelease → release workflow just finalized a
|
||||
# stable tag (see publish-release step in release.yml)
|
||||
# - workflow_call → release.yml finished publishing a stable tag
|
||||
# - workflow_call → release-cut.yml finished publishing a stable tag
|
||||
# - workflow_dispatch → manual backfill for a specific tag
|
||||
#
|
||||
# The job computes the sha256 of orca-macos-arm64.dmg + orca-macos-x64.dmg
|
||||
# from the release, templates Casks/orca.rb in this repo with the new
|
||||
# version and hashes, then PRs the result into stablyai/homebrew-orca. It
|
||||
# does NOT run for -rc.* tags: the stable cask should only track GA.
|
||||
#
|
||||
# Why no `release.published` trigger: other products shipped from this repo
|
||||
# (e.g. mobile under `mobile-v*`) also publish GitHub Releases. A blanket
|
||||
# `release.published` trigger would fire this workflow for any of them and
|
||||
# attempt to download `orca-macos-*.dmg` from a non-desktop release, at
|
||||
# best 404ing and at worst (if the filter were weaker) rewriting the cask
|
||||
# to a non-desktop version. The tap is only ever bumped by the desktop
|
||||
# release pipeline calling this workflow directly.
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
|
@ -19,8 +25,6 @@ on:
|
|||
description: Release tag to sync (e.g. v1.3.25)
|
||||
required: true
|
||||
type: string
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
|
|
@ -31,12 +35,15 @@ on:
|
|||
jobs:
|
||||
bump-cask:
|
||||
runs-on: ubuntu-latest
|
||||
# Why: skip prereleases and RC tags. The stable cask tracks GA only; a
|
||||
# beta channel would be a separate cask in a separate job.
|
||||
# Why: defense in depth. The desktop release pipeline already refuses
|
||||
# to invoke this workflow for `-rc.*` tags, but a manual backfill could
|
||||
# still pass one in — reject it here so the stable cask can never point
|
||||
# at an RC. Also requires the desktop tag shape `v<digit>...` so a
|
||||
# mis-typed mobile tag like `mobile-v0.0.1` is rejected up front.
|
||||
if: >
|
||||
inputs.tag != '' ||
|
||||
(github.event.release.prerelease == false &&
|
||||
!contains(github.event.release.tag_name, '-rc.'))
|
||||
inputs.tag != '' &&
|
||||
!contains(inputs.tag, '-rc.') &&
|
||||
startsWith(inputs.tag, 'v')
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
|
|
|
|||
|
|
@ -183,20 +183,28 @@ jobs:
|
|||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# Latest stable release tag, picked by *tag shape* (no `-rc.`)
|
||||
# rather than the GitHub `isPrerelease` flag. electron-builder's
|
||||
# publish step has flipped that flag back to `false` on RC
|
||||
# releases in the past (issue surfaced 2026-04-27 when
|
||||
# v1.3.22-rc.2 became "latest" on GitHub and poisoned the
|
||||
# compute-next-version math here). Filtering by tag format is
|
||||
# the authoritative signal — an `-rc.N` tag is never stable,
|
||||
# regardless of what the release metadata says.
|
||||
# Latest stable release tag, picked by *tag shape*:
|
||||
# - must start with `v<digit>` (desktop convention, e.g. v1.3.32)
|
||||
# - must NOT contain `-rc.` (not a prerelease)
|
||||
#
|
||||
# Why not the GitHub `isPrerelease` flag: electron-builder's publish
|
||||
# step has flipped that flag back to `false` on RC releases before
|
||||
# (v1.3.22-rc.2 on 2026-04-27 briefly became "latest" on GitHub and
|
||||
# poisoned the math here). Tag format is authoritative.
|
||||
#
|
||||
# Why the `^v[0-9]` prefix: other products shipped from this repo
|
||||
# use their own prefixes (e.g. `mobile-v0.0.1`). Without the prefix
|
||||
# gate the latest mobile release would be selected as "latest
|
||||
# stable", then `strip_pre()` would reduce `mobile-v0.0.1` to
|
||||
# `mobile`, `Number("mobile")` → NaN → 0, and a patch-bump would
|
||||
# produce `0.0.1` — exactly the wedge on 2026-05-04 (run
|
||||
# 25304336767). Any non-desktop tag shape must be excluded here.
|
||||
latest_stable="$(gh release list \
|
||||
--repo "$GITHUB_REPOSITORY" \
|
||||
--exclude-drafts \
|
||||
--limit 50 \
|
||||
--json tagName \
|
||||
--jq '[.[] | .tagName | select(test("-rc\\.") | not)] | .[0] // ""')"
|
||||
--jq '[.[] | .tagName | select(test("^v[0-9]") and (test("-rc\\.") | not))] | .[0] // ""')"
|
||||
latest_stable="${latest_stable#v}"
|
||||
echo "Latest stable: ${latest_stable:-<none>}"
|
||||
|
||||
|
|
@ -369,10 +377,227 @@ jobs:
|
|||
git push origin "$TAG"
|
||||
fi
|
||||
|
||||
release:
|
||||
create-release:
|
||||
needs: cut
|
||||
if: needs.cut.outputs.should_release == 'true'
|
||||
uses: ./.github/workflows/release.yml
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: refs/tags/${{ needs.cut.outputs.tag }}
|
||||
|
||||
- name: Create draft release with auto-generated notes
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAG: ${{ needs.cut.outputs.tag }}
|
||||
run: |
|
||||
if gh release view "$TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
|
||||
echo "Release $TAG already exists."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
is_rc=false
|
||||
if [[ "$TAG" == *"-rc."* ]]; then
|
||||
is_rc=true
|
||||
fi
|
||||
|
||||
gh release create "$TAG" \
|
||||
--draft \
|
||||
--generate-notes \
|
||||
--prerelease="$is_rc"
|
||||
|
||||
# Why: E2E runs alongside the release for visibility (failures surface as a
|
||||
# red check on the tag), but is NOT in `publish-release`'s needs list.
|
||||
# Releases already take a while and the suite is already a required check
|
||||
# on PRs, so gating here would mostly delay shipping without adding signal.
|
||||
e2e:
|
||||
needs: cut
|
||||
if: needs.cut.outputs.should_release == 'true'
|
||||
uses: ./.github/workflows/e2e.yml
|
||||
with:
|
||||
ref: refs/tags/${{ needs.cut.outputs.tag }}
|
||||
|
||||
build:
|
||||
needs:
|
||||
- cut
|
||||
- create-release
|
||||
if: needs.cut.outputs.should_release == 'true'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: macos-15
|
||||
platform: mac
|
||||
release_command: ORCA_MAC_RELEASE=1 pnpm exec electron-builder --config config/electron-builder.config.cjs --mac --publish always
|
||||
eb_cache_path: |
|
||||
~/Library/Caches/electron
|
||||
~/Library/Caches/electron-builder
|
||||
- os: windows-latest
|
||||
platform: win
|
||||
release_command: pnpm exec electron-builder --config config/electron-builder.config.cjs --win --publish always
|
||||
eb_cache_path: |
|
||||
~\AppData\Local\electron\Cache
|
||||
~\AppData\Local\electron-builder\Cache
|
||||
- os: ubuntu-latest
|
||||
platform: linux
|
||||
release_command: pnpm exec electron-builder --config config/electron-builder.config.cjs --linux --publish always
|
||||
eb_cache_path: |
|
||||
~/.cache/electron
|
||||
~/.cache/electron-builder
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: refs/tags/${{ needs.cut.outputs.tag }}
|
||||
|
||||
# pnpm must be on PATH before setup-node so setup-node can locate the store for caching.
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
run_install: false
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: package.json
|
||||
cache: pnpm
|
||||
|
||||
# Why: release builds hit the same native-module postinstall path as
|
||||
# PR CI, so keep the pinned node-gyp override here too instead of
|
||||
# relying on pnpm's bundled copy. Scoped to Linux via runner.os (not
|
||||
# a specific matrix image) because the failing postinstall has only
|
||||
# been observed on Linux runners — see run 25081763129. The macOS
|
||||
# and Windows release jobs exercise the same pnpm install path and
|
||||
# have not reproduced it, so keep the gate narrow until we know why.
|
||||
# Using runner.os instead of matrix.os == 'ubuntu-latest' means the
|
||||
# gate still works if another Linux matrix entry is added later.
|
||||
- name: Use external node-gyp to avoid pnpm's bundled copy (Linux only)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
npm install -g node-gyp@11.5.0
|
||||
echo "npm_config_node_gyp=$(npm root -g)/node-gyp/bin/node-gyp.js" >> "$GITHUB_ENV"
|
||||
|
||||
# Cache the Electron binary + electron-builder tool downloads (notarytool,
|
||||
# winCodeSign, nsis, squirrel, AppImage). Saves ~30-90s per job, incl. mac.
|
||||
- name: Cache electron-builder downloads
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ matrix.eb_cache_path }}
|
||||
key: electron-builder-${{ matrix.platform }}-${{ hashFiles('pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
electron-builder-${{ matrix.platform }}-
|
||||
|
||||
# Why: pnpm install triggers electron's postinstall, which downloads the
|
||||
# Electron binary from GitHub release assets. GitHub's download CDN
|
||||
# occasionally returns 504s that fail the whole release. Retry on
|
||||
# failure so transient network errors don't require a manual re-run.
|
||||
- name: Install dependencies
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
retry_wait_seconds: 30
|
||||
command: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Verify macOS signing environment
|
||||
if: matrix.platform == 'mac'
|
||||
run: node config/scripts/verify-macos-release-env.mjs
|
||||
env:
|
||||
CSC_LINK: ${{ secrets.MAC_CERTS }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTS_PASSWORD }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
|
||||
- name: Build app
|
||||
run: pnpm build:release
|
||||
|
||||
# Why: macOS signing secrets (CSC_LINK, CSC_KEY_PASSWORD) must NOT be
|
||||
# passed to non-macOS builds. electron-builder uses CSC_LINK as the
|
||||
# code-signing certificate on any platform, so leaking the Apple
|
||||
# Developer ID cert to the Windows build causes the NSIS installer to
|
||||
# be signed with an Apple cert whose chain Windows cannot validate,
|
||||
# breaking the auto-updater with "certificate chain could not be built
|
||||
# to a trusted root authority" (issue #631).
|
||||
#
|
||||
# Why retry: electron-builder downloads NSIS/winCodeSign/squirrel
|
||||
# binaries and the Electron runtime from GitHub release assets during
|
||||
# publish. GitHub's download CDN occasionally returns 504s that fail
|
||||
# the whole release. Retry on failure so transient network errors
|
||||
# don't require a manual re-run.
|
||||
- name: Publish release artifacts (macOS)
|
||||
if: matrix.platform == 'mac'
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
retry_wait_seconds: 30
|
||||
command: ${{ matrix.release_command }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CSC_LINK: ${{ secrets.MAC_CERTS }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTS_PASSWORD }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
|
||||
- name: Publish release artifacts
|
||||
if: matrix.platform != 'mac'
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
retry_wait_seconds: 30
|
||||
command: ${{ matrix.release_command }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
publish-release:
|
||||
needs:
|
||||
- cut
|
||||
- build
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Publish release
|
||||
# Why: derive `--prerelease` from the tag shape (not from whatever
|
||||
# electron-builder left the release flagged as). On 2026-04-27,
|
||||
# electron-builder's publish step flipped `prerelease` back to
|
||||
# `false` on -rc.N releases, which caused an RC to be marked as
|
||||
# GitHub's "latest" release and broke release-cut.yml's math.
|
||||
# Re-asserting here means the final release state is determined
|
||||
# by the tag — a ground truth electron-builder can't rewrite.
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAG: ${{ needs.cut.outputs.tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [[ "$TAG" == *"-rc."* ]]; then
|
||||
prerelease=true
|
||||
else
|
||||
prerelease=false
|
||||
fi
|
||||
gh release edit "$TAG" \
|
||||
--draft=false \
|
||||
--prerelease="$prerelease" \
|
||||
--repo "$GITHUB_REPOSITORY"
|
||||
|
||||
homebrew-bump:
|
||||
needs:
|
||||
- cut
|
||||
- publish-release
|
||||
if: ${{ !contains(needs.cut.outputs.tag, '-rc.') }}
|
||||
uses: ./.github/workflows/homebrew-bump.yml
|
||||
with:
|
||||
tag: ${{ needs.cut.outputs.tag }}
|
||||
secrets: inherit
|
||||
|
|
|
|||
|
|
@ -1,257 +0,0 @@
|
|||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_call:
|
||||
inputs:
|
||||
tag:
|
||||
description: Tag to release, for example v1.2.3-rc.0
|
||||
required: true
|
||||
type: string
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: Existing tag to release, for example v1.2.3-rc.0
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
resolve-release:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
tag: ${{ steps.resolve.outputs.tag }}
|
||||
ref: ${{ steps.resolve.outputs.ref }}
|
||||
steps:
|
||||
- name: Resolve release tag and ref
|
||||
id: resolve
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "push" ]]; then
|
||||
release_tag="${{ github.ref_name }}"
|
||||
release_ref="${{ github.ref }}"
|
||||
else
|
||||
release_tag="${{ inputs.tag }}"
|
||||
release_ref="refs/tags/${{ inputs.tag }}"
|
||||
fi
|
||||
|
||||
echo "tag=$release_tag" >>"$GITHUB_OUTPUT"
|
||||
echo "ref=$release_ref" >>"$GITHUB_OUTPUT"
|
||||
|
||||
create-release:
|
||||
needs: resolve-release
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.resolve-release.outputs.ref }}
|
||||
|
||||
- name: Create draft release with auto-generated notes
|
||||
run: |
|
||||
if gh release view "${{ needs.resolve-release.outputs.tag }}" --repo "${{ github.repository }}" >/dev/null 2>&1; then
|
||||
echo "Release ${{ needs.resolve-release.outputs.tag }} already exists."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
gh release create "${{ needs.resolve-release.outputs.tag }}" \
|
||||
--draft \
|
||||
--generate-notes \
|
||||
--prerelease=${{ contains(needs.resolve-release.outputs.tag, 'rc') }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Why: E2E runs alongside the release for visibility (failures surface as a
|
||||
# red check on the tag), but it is NOT in `release`'s needs list. Releases
|
||||
# already take a while and the suite is already a required check on PRs, so
|
||||
# gating here would mostly just delay shipping without adding much signal.
|
||||
# Matches the pattern used by noqa's app deploy, which runs E2E with
|
||||
# continue-on-error so failures are visible but don't block the deploy.
|
||||
e2e:
|
||||
needs: resolve-release
|
||||
uses: ./.github/workflows/e2e.yml
|
||||
with:
|
||||
ref: ${{ needs.resolve-release.outputs.ref }}
|
||||
|
||||
release:
|
||||
needs:
|
||||
- resolve-release
|
||||
- create-release
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: macos-15
|
||||
platform: mac
|
||||
release_command: ORCA_MAC_RELEASE=1 pnpm exec electron-builder --config config/electron-builder.config.cjs --mac --publish always
|
||||
eb_cache_path: |
|
||||
~/Library/Caches/electron
|
||||
~/Library/Caches/electron-builder
|
||||
- os: windows-latest
|
||||
platform: win
|
||||
release_command: pnpm exec electron-builder --config config/electron-builder.config.cjs --win --publish always
|
||||
eb_cache_path: |
|
||||
~\AppData\Local\electron\Cache
|
||||
~\AppData\Local\electron-builder\Cache
|
||||
- os: ubuntu-latest
|
||||
platform: linux
|
||||
release_command: pnpm exec electron-builder --config config/electron-builder.config.cjs --linux --publish always
|
||||
eb_cache_path: |
|
||||
~/.cache/electron
|
||||
~/.cache/electron-builder
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.resolve-release.outputs.ref }}
|
||||
|
||||
# pnpm must be on PATH before setup-node so setup-node can locate the store for caching.
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
run_install: false
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: package.json
|
||||
cache: pnpm
|
||||
|
||||
# Why: release builds hit the same native-module postinstall path as
|
||||
# PR CI, so keep the pinned node-gyp override here too instead of
|
||||
# relying on pnpm's bundled copy. Scoped to Linux via runner.os (not
|
||||
# a specific matrix image) because the failing postinstall has only
|
||||
# been observed on Linux runners — see run 25081763129. The macOS
|
||||
# and Windows release jobs exercise the same pnpm install path and
|
||||
# have not reproduced it, so keep the gate narrow until we know why.
|
||||
# Using runner.os instead of matrix.os == 'ubuntu-latest' means the
|
||||
# gate still works if another Linux matrix entry is added later.
|
||||
- name: Use external node-gyp to avoid pnpm's bundled copy (Linux only)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
npm install -g node-gyp@11.5.0
|
||||
echo "npm_config_node_gyp=$(npm root -g)/node-gyp/bin/node-gyp.js" >> "$GITHUB_ENV"
|
||||
|
||||
# Cache the Electron binary + electron-builder tool downloads (notarytool,
|
||||
# winCodeSign, nsis, squirrel, AppImage). Saves ~30-90s per job, incl. mac.
|
||||
- name: Cache electron-builder downloads
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ matrix.eb_cache_path }}
|
||||
key: electron-builder-${{ matrix.platform }}-${{ hashFiles('pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
electron-builder-${{ matrix.platform }}-
|
||||
|
||||
# Why: pnpm install triggers electron's postinstall, which downloads the
|
||||
# Electron binary from GitHub release assets. GitHub's download CDN
|
||||
# occasionally returns 504s that fail the whole release. Retry on
|
||||
# failure so transient network errors don't require a manual re-run.
|
||||
- name: Install dependencies
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
retry_wait_seconds: 30
|
||||
command: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Verify macOS signing environment
|
||||
if: matrix.platform == 'mac'
|
||||
run: node config/scripts/verify-macos-release-env.mjs
|
||||
env:
|
||||
CSC_LINK: ${{ secrets.MAC_CERTS }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTS_PASSWORD }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
|
||||
- name: Build app
|
||||
run: pnpm build:release
|
||||
|
||||
# Why: macOS signing secrets (CSC_LINK, CSC_KEY_PASSWORD) must NOT be
|
||||
# passed to non-macOS builds. electron-builder uses CSC_LINK as the
|
||||
# code-signing certificate on any platform, so leaking the Apple
|
||||
# Developer ID cert to the Windows build causes the NSIS installer to
|
||||
# be signed with an Apple cert whose chain Windows cannot validate,
|
||||
# breaking the auto-updater with "certificate chain could not be built
|
||||
# to a trusted root authority" (issue #631).
|
||||
#
|
||||
# Why retry: electron-builder downloads NSIS/winCodeSign/squirrel
|
||||
# binaries and the Electron runtime from GitHub release assets during
|
||||
# publish. GitHub's download CDN occasionally returns 504s that fail
|
||||
# the whole release. Retry on failure so transient network errors
|
||||
# don't require a manual re-run.
|
||||
- name: Publish release artifacts (macOS)
|
||||
if: matrix.platform == 'mac'
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
retry_wait_seconds: 30
|
||||
command: ${{ matrix.release_command }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CSC_LINK: ${{ secrets.MAC_CERTS }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTS_PASSWORD }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
|
||||
- name: Publish release artifacts
|
||||
if: matrix.platform != 'mac'
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
retry_wait_seconds: 30
|
||||
command: ${{ matrix.release_command }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
publish-release:
|
||||
needs:
|
||||
- resolve-release
|
||||
- release
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Publish release
|
||||
# Why: derive `--prerelease` from the tag shape (not from whatever
|
||||
# electron-builder left the release flagged as). On 2026-04-27,
|
||||
# electron-builder's publish step flipped `prerelease` back to
|
||||
# `false` on -rc.N releases, which caused an RC to be marked as
|
||||
# GitHub's "latest" release and broke release-cut.yml's math.
|
||||
# Re-asserting here means the final release state is determined
|
||||
# by the tag — a ground truth electron-builder can't rewrite.
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAG: ${{ needs.resolve-release.outputs.tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [[ "$TAG" == *"-rc."* ]]; then
|
||||
prerelease=true
|
||||
else
|
||||
prerelease=false
|
||||
fi
|
||||
gh release edit "$TAG" \
|
||||
--draft=false \
|
||||
--prerelease="$prerelease" \
|
||||
--repo "$GITHUB_REPOSITORY"
|
||||
|
||||
homebrew-bump:
|
||||
needs:
|
||||
- resolve-release
|
||||
- publish-release
|
||||
if: ${{ !contains(needs.resolve-release.outputs.tag, '-rc.') }}
|
||||
uses: ./.github/workflows/homebrew-bump.yml
|
||||
with:
|
||||
tag: ${{ needs.resolve-release.outputs.tag }}
|
||||
secrets: inherit
|
||||
Loading…
Reference in New Issue