Desktop Release Automation¶
ButtonsCLI can build Windows, macOS, and Linux desktop bundles automatically from GitHub Actions.
This repo now includes .github/workflows/release-desktop.yml for that.
What the workflow does¶
The workflow runs on:
- pushes to
main - manual
workflow_dispatch
It reads the version from package.json.
If a matching Git tag already exists, for example buttonscli-v0.5.0, the
workflow stops without publishing another desktop release.
If the version is new, the workflow:
- builds Linux bundles on Ubuntu
- builds Windows bundles on Windows
- builds a universal macOS bundle on macOS
- attaches the artifacts to a GitHub release draft
- uploads the installers as workflow artifacts named
desktop-<platform>-<version> - skips updater metadata and platform signing by default
- writes a server handoff Markdown artifact that can be given to a server-side agent after the desktop artifacts are reviewed
- can manually upload the built desktop artifacts to the VPS and trigger the
server deploy script when
deploy_to_serveris enabled
That draft-first setup is safer when you are still getting used to the release
flow. Once you trust it, you can flip releaseDraft: true to false in the
workflow file and let it publish immediately.
One-time GitHub setup¶
1. Commit the workflow¶
Make sure .github/workflows/release-desktop.yml is committed on the branch you
plan to merge into main.
2. Optional updater-signing secrets¶
You can skip this while you are just trying to get downloadable installers. Unsigned desktop artifacts are still useful for testing and manual release downloads.
Open your GitHub repository, then go to:
Settings -> Secrets and variables -> Actions
Add these secrets:
TAURI_SIGNING_PRIVATE_KEYTAURI_SIGNING_PRIVATE_KEY_PASSWORD
If you generated your updater key locally with:
then the private key lives at ~/.buttonscli-signing/updater.key.
This repo's local helper generates that key with an empty password by default,
so TAURI_SIGNING_PRIVATE_KEY_PASSWORD can stay blank.
Only enable the signed_release workflow input after these secrets are known to
work. If TAURI_SIGNING_PRIVATE_KEY is malformed or the password is wrong,
Tauri can finish compiling and then fail while creating updater signatures.
3. Optional Windows signing secrets¶
If you already have a real Windows code-signing certificate, also add:
BUTTONSCLI_WINDOWS_CERT_THUMBPRINTBUTTONSCLI_WINDOWS_TIMESTAMP_URLBUTTONSCLI_WINDOWS_DIGEST_ALGORITHM
If you do not have a Windows certificate yet, that is okay. The workflow builds unsigned Windows bundles by default.
4. Optional macOS signing secrets¶
If you want proper macOS public distribution instead of ad-hoc local-style signing, add the usual Apple secrets too:
APPLE_SIGNING_IDENTITYAPPLE_CERTIFICATEAPPLE_CERTIFICATE_PASSWORDAPPLE_IDAPPLE_PASSWORDAPPLE_TEAM_ID
If you skip these for now, the repo can still build, but macOS public distribution trust and notarization will not be fully set up yet.
Do not enable signed_release until the Apple certificate secrets are valid.
If the certificate is missing, malformed, or protected by the wrong password,
macOS can finish compiling and then fail at the security import step.
5. Optional VPS deploy secrets¶
For the VPS deploy lane, add these repository secrets under:
Settings -> Secrets and variables -> Actions
DEPLOY_SSH_KEY: the private deploy keyDEPLOY_HOST: the VPS IP address or DNS nameDEPLOY_USER: the SSH user, currentlydeploy
Do not paste the IP address into DEPLOY_SSH_KEY. The IP address belongs only
in DEPLOY_HOST.
The current server deploy key is a forced-command key. That is intentional: it
can run only the deploy wrapper on the server. Because of that, normal scp
commands will not work unless the wrapper explicitly supports an upload command.
The GitHub workflow expects the server wrapper to support:
The first command streams the release files into the server's incoming release folder. The second command promotes that incoming folder to the live release.
Normal release routine¶
1. Bump the version once¶
Change the version in package.json.
That is the source of truth now.
pnpm run branding:sync mirrors it into:
src-tauri/tauri.conf.jsonsrc-tauri/Cargo.toml
2. Push or merge to main¶
When that new version lands on main, GitHub Actions will build the three OS
artifacts automatically.
3. Find the built files¶
The files go to two GitHub places:
- the draft Release, if the release upload step finishes cleanly
- the workflow run's Artifacts section, always named like
desktop-linux-x64-0.8.3,desktop-windows-x64-0.8.3, anddesktop-macos-universal-0.8.3
In GitHub, open:
Actions -> desktop-release -> the run -> Artifacts
Download the artifact zip for the platform you want. Inside it, expect files like:
- Linux:
.AppImageand.deb - Windows:
.exefrom the NSIS bundle - macOS:
.dmg
The runner's raw build directories are temporary. Anything not uploaded as a Release asset or Actions artifact disappears when the run ends.
4. Open the draft release¶
After the workflow finishes, open the new GitHub release draft and check that the expected bundles are attached.
5. Publish the draft when ready¶
When the artifacts look correct, publish the draft release from GitHub's Releases page.
6. Optional: deploy to the VPS¶
Manual runs have a deploy_to_server checkbox.
When enabled, the workflow:
- downloads the
ButtonsCLI_<version>_*assets from the matching GitHub release - streams those files to the VPS with the forced-command deploy key
- triggers the server deploy script for the same version
Leave this off while you are only testing compile/release packaging. Once the
release draft/assets exist and the server-side wrapper has upload <version>
support, you can run the workflow manually with deploy_to_server enabled.
Local build vs GitHub build¶
For local machine builds, keep using:
That is still the fastest path when you only want a Windows bundle from your own machine.
GitHub Actions is the cross-platform path that creates all three operating-system builds without you needing three separate computers.
Compile checks without releasing¶
The repo also has .github/workflows/desktop-ci.yml.
That workflow runs on pull requests, pushes to main, and manual dispatch. It
does not publish releases. It installs dependencies, builds the frontend, and
runs cargo check for the Tauri backend on:
- Ubuntu
- Windows
- macOS
Use this as the normal "does it compile everywhere?" lane. Use
release-desktop.yml only when you are ready to produce release artifacts.
Server handoff lane¶
release-desktop.yml no longer tries to rsync a half-defined site folder after
desktop builds. Instead, it creates a server handoff .md artifact with the
version, tag, release checklist, and server-agent instructions.
On manual dispatch, you can also enable send_server_handoff. That uploads the
handoff file to the server over SSH.
Do not use send_server_handoff with the forced-command deploy key. That key
is intentionally limited to the deploy wrapper and cannot run arbitrary mkdir
or scp commands. Use a normal SSH account for handoff uploads, or leave this
off and use the workflow artifact directly.
Required secrets for handoff upload:
DEPLOY_SSH_KEYDEPLOY_HOSTDEPLOY_USER
The default remote directory is:
That is intentionally just a handoff. Let Codex on the server read the file, inspect the actual server paths, and propose the concrete deploy commands before anything syncs or restarts.
First things to check when a workflow fails¶
- the version in
package.jsonwas actually bumped - if the log says it built one version but looked for another version, run
pnpm run branding:syncand commit the updatedsrc-taurimetadata - the workflow run has platform artifacts under
Actions -> run -> Artifacts TAURI_SIGNING_PRIVATE_KEYexists only ifsigned_releasewas enabled- Linux system packages installed correctly on the runner
- macOS signing secrets are present only if
signed_releasewas enabled - the repo does not already have a tag for the same version
If you want more aggressive automation later, the main toggle is simple:
- keep
releaseDraft: truefor safer review-first releases - change it to
falsefor auto-published releases