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
31 changes: 16 additions & 15 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
name: Release

# Only ever by hand. A release is a decision, and nothing about merging a pull request says one
# has been taken.
on:
push:
branches: [main]
workflow_dispatch:
inputs:
dry_run:
description: Build and sign, publish nothing
type: boolean
default: false

permissions:
contents: write

jobs:
# A minute of Linux in front of six minutes of Windows. Most pushes to main are not releases, and
# this is what tells them apart before anything is compiled.
# A few seconds of Linux in front of six minutes of Windows, so a version that is already out is
# refused before anything is compiled.
decide:
runs-on: ubuntu-latest
outputs:
Expand All @@ -19,7 +24,7 @@ jobs:
steps:
- uses: actions/checkout@v4

- name: Is this version already released?
- name: Work out what would be released
id: check
env:
GH_TOKEN: ${{ github.token }}
Expand All @@ -28,28 +33,24 @@ jobs:
tag="v$version"
echo "tag=$tag" >> "$GITHUB_OUTPUT"

# A run started by hand is a rehearsal whatever the version says: it builds and signs and
# publishes nothing, which is what makes it safe to press.
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
if [ "${{ inputs.dry_run }}" = "true" ]; then
echo "publish=false" >> "$GITHUB_OUTPUT"
echo "::notice::Rehearsal of $tag. Nothing will be published."
exit 0
fi

# Refused rather than overwritten: a tag that already names a release is the one thing
# somebody may already have downloaded.
if gh api "repos/${{ github.repository }}/git/ref/tags/$tag" >/dev/null 2>&1; then
echo "publish=false" >> "$GITHUB_OUTPUT"
echo "::notice::$tag is already released. Bump version in src-tauri/tauri.conf.json to cut a new one."
exit 0
echo "::error::$tag is already released. Run 'npm version patch', merge that, and trigger again."
exit 1
fi

echo "publish=true" >> "$GITHUB_OUTPUT"
echo "::notice::$tag has no release yet. Building one."
echo "::notice::Releasing $tag. The pipeline creates the tag; nobody has to."

release:
needs: decide
# A push whose version is already out has nothing to do; a rehearsal still builds, because
# finding out that a release *would* work is the whole point of pressing the button.
if: needs.decide.outputs.publish == 'true' || github.event_name == 'workflow_dispatch'
runs-on: windows-latest
defaults:
run:
Expand Down
15 changes: 9 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,15 @@ The public half goes into `plugins.updater.pubkey` in `src-tauri/tauri.conf.json
and its password are the GitHub secrets `TAURI_SIGNING_PRIVATE_KEY` and
`TAURI_SIGNING_PRIVATE_KEY_PASSWORD` and never enter the repository.

**Cutting a release is bumping a number.** `package.json` holds the version; `tauri.conf.json`
points at it and the interface reads it through `__APP_VERSION__`, so it is written once. A push to
`main` whose version has no tag yet builds, signs, tags at that commit and publishes the installer,
its `.sig` and a `latest.json` written from that signature. A push whose version is already out
stops in under a minute. Running the workflow by hand is a rehearsal: it builds and signs and
attaches the result to the run, and publishes nothing.
**A release happens when somebody presses the button, and never otherwise.** `release.yml` runs on
`workflow_dispatch` only — merging a pull request releases nothing. Pressing it builds, signs,
creates the tag `v<version>` at that commit and publishes the installer, its `.sig` and a
`latest.json` written from that signature. Nobody tags by hand. Tick `dry_run` to build and sign
without publishing.

`package.json` holds the version; `tauri.conf.json` points at it and the interface reads it through
`__APP_VERSION__`, so it is written once. A version that already has a tag is refused before
anything is compiled — bump it first:

```bash
npm version patch # or minor, or major
Expand Down
Loading