Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 32 additions & 24 deletions .github/workflows/desktop-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ jobs:
with:
persist-credentials: false

# apps/desktop/package.json is the single source of truth for the version.
# The tag only selects which commit ships. Stamping the tag's version into
# the tree instead would let `git tag desktop-v9.9.9 <any commit>` silently
# rewrite that commit into a release nobody reviewed, so a disagreement is
# a hard failure rather than a rewrite.
- name: Resolve the desktop version
id: resolve
shell: bash
Expand All @@ -41,23 +46,43 @@ jobs:
IS_TAG: ${{ startsWith(github.ref, 'refs/tags/desktop-v') }}
run: |
set -euo pipefail
# A tag build is authoritative; a manual run falls back to whatever
# version the checked-out tree declares.
if [ "$IS_TAG" = 'true' ]; then
version="${TAG_NAME#desktop-v}"
else
version="$(node -p 'require("./apps/desktop/package.json").version')"
fi
version="$(node -p 'require("./apps/desktop/package.json").version')"
if [ -z "$version" ]; then
echo 'Could not resolve a desktop version' >&2
exit 1
fi
if [ "$IS_TAG" = 'true' ]; then
tag_version="${TAG_NAME#desktop-v}"
if [ "$tag_version" != "$version" ]; then
echo "::error::Tag ${TAG_NAME} does not match apps/desktop/package.json (${version})." >&2
echo 'Bump the package version on main first, then tag that commit.' >&2
exit 1
fi
fi
# electron-builder names the GitHub release `v${version}`; the tag we
# push here (`desktop-v*`) only triggers the workflow.
echo "version=${version}" >> "$GITHUB_OUTPUT"
echo "tag=v${version}" >> "$GITHUB_OUTPUT"
echo "Releasing desktop ${version} as tag v${version}."

# A release built from a commit that never landed on main ships code no
# review gate ever saw. `compare` reports `identical` or `behind` when the
# commit is an ancestor of main, and `diverged`/`ahead` when it is not.
- name: Require the tagged commit to be on main
if: startsWith(github.ref, 'refs/tags/desktop-v')
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
status="$(gh api "repos/${GITHUB_REPOSITORY}/compare/main...${GITHUB_SHA}" --jq '.status')"
case "$status" in
identical|behind) echo "${GITHUB_SHA} is on main (${status})." ;;
*)
echo "::error::${GITHUB_SHA} is not on main (compare status: ${status})." >&2
exit 1 ;;
esac

- name: Mint releases-repo token
id: releases_token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # pinned from v3.2.0
Expand Down Expand Up @@ -107,14 +132,6 @@ jobs:

- run: pnpm install --frozen-lockfile

- name: Stamp desktop version for tag builds
if: startsWith(github.ref, 'refs/tags/desktop-v')
env:
TAG_NAME: ${{ github.ref_name }}
run: |
export DESKTOP_VERSION="${TAG_NAME#desktop-v}"
node -e 'const fs = require("node:fs"); const path = "apps/desktop/package.json"; const packageJson = JSON.parse(fs.readFileSync(path, "utf8")); packageJson.version = process.env.DESKTOP_VERSION; fs.writeFileSync(path, `${JSON.stringify(packageJson, null, 2)}\n`);'

- name: Build workspace
run: pnpm --workspace-root run build

Expand Down Expand Up @@ -233,15 +250,6 @@ jobs:

- run: pnpm install --frozen-lockfile

- name: Stamp desktop version for tag builds
if: startsWith(github.ref, 'refs/tags/desktop-v')
shell: bash
env:
TAG_NAME: ${{ github.ref_name }}
run: |
export DESKTOP_VERSION="${TAG_NAME#desktop-v}"
node -e 'const fs = require("node:fs"); const path = "apps/desktop/package.json"; const packageJson = JSON.parse(fs.readFileSync(path, "utf8")); packageJson.version = process.env.DESKTOP_VERSION; fs.writeFileSync(path, `${JSON.stringify(packageJson, null, 2)}\n`);'

- name: Build workspace
run: pnpm --workspace-root run build

Expand Down
54 changes: 54 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,60 @@ jobs:
# No NPM_TOKEN on purpose: changesets prefers it over OIDC when set, so
# defining it would silently downgrade publishing to a long-lived token.

# apps/desktop is a private workspace package: changesets bumps its
# version but nothing publishes it, and Desktop Release only fires on a
# `desktop-v*` tag. A bump that nobody tags therefore leaves every
# installed desktop client on the previous version with nothing red to
# show for it — which is exactly how 0.2.0 sat unreleased behind 0.1.6.
# Read both versions out of git rather than the working tree: the
# changesets action rewrites package.json in place on the run that opens
# the version PR, and that rewrite is not a release.
# The App token matters: a tag pushed with GITHUB_TOKEN would not start
# Desktop Release, because GitHub refuses to trigger workflows from it.
# A desktop tag that fails to cut costs one manual `git tag`; a failed step
# here would block npm, the Marketplace and the CDN behind it. Never let
# this be the thing that stops a release — but annotate every skip, since
# a silent no-op is the exact failure being fixed.
- name: Cut the desktop release tag on a version bump
continue-on-error: true
env:
GH_TOKEN: ${{ steps.release-bot.outputs.token }}
run: |
set -uo pipefail
# Read the pushed commit by sha, never HEAD. The changesets action
# checks out changeset-release/main and commits the bumped
# package.json files on the run that opens the version PR, so by the
# time this step runs HEAD can be a bump that is not a release — and
# tagging it would burn the next real version's tag name, which the
# already-exists check below would then treat as done.
read_version() { git show "$1:apps/desktop/package.json" 2>/dev/null | node -p 'JSON.parse(require("fs").readFileSync(0, "utf8")).version' 2>/dev/null; }
prev="$(read_version "${GITHUB_SHA}^" || true)"
curr="$(read_version "${GITHUB_SHA}" || true)"
if [ -z "$prev" ] || [ -z "$curr" ] || [ "$prev" = "$curr" ]; then
echo "::notice::Desktop version unchanged or unreadable (prev='${prev}' curr='${curr}'); no tag cut."
exit 0
fi
tag="desktop-v${curr}"
# A tag on this commit is a re-run. A tag on a different commit is an
# anomaly that would otherwise silently swallow this release.
existing="$(git ls-remote --tags origin "refs/tags/${tag}" "refs/tags/${tag}^{}" |
awk '$2 ~ /\^\{\}$/ { peeled=$1 } $2 !~ /\^\{\}$/ { direct=$1 } END { print (peeled ? peeled : direct) }')"
if [ -n "$existing" ]; then
if [ "$existing" = "$GITHUB_SHA" ]; then
echo "::notice::${tag} already points here; nothing to do."
else
echo "::error::${tag} exists at ${existing}, not ${GITHUB_SHA}. Desktop ${curr} will not ship until that is resolved."
fi
exit 0
fi
if ! gh api "repos/${GITHUB_REPOSITORY}/git/refs" \
-f ref="refs/tags/${tag}" \
-f sha="${GITHUB_SHA}" > /dev/null; then
echo "::error::Could not cut ${tag}. Desktop ${curr} will not ship until someone pushes it: git tag ${tag} ${GITHUB_SHA} && git push origin ${tag}"
exit 1
fi
echo "Cut ${tag} at ${GITHUB_SHA} (desktop ${prev} -> ${curr})."

- name: Request CodeRabbit review on version PR
if: steps.changesets.outputs.published != 'true'
env:
Expand Down
Loading