Releasing and desktop installers
The desktop editor (apps/desktop, Tauri 2) is packaged by the Desktop installers workflow
(.github/workflows/release-desktop.yml):
| Platform | Installers | Built on |
|---|---|---|
| Windows | .msi (WiX) and -setup.exe (NSIS, per-user or all users) |
Windows Server 2022 |
| macOS | .dmg and .app, one universal build for Apple Silicon and Intel, macOS 10.15+ |
macOS 15 |
| Linux | .deb (Debian, Ubuntu), .rpm (Fedora, openSUSE), .AppImage (any distribution) |
Ubuntu 22.04, for an older glibc that more distributions have |
- Cutting a release
- Test builds without a release
- Packages
- Code signing
- Building installers locally
- Installing unsigned builds
Cutting a release
Section titled “Cutting a release”-
Set the version everywhere (packages, editor, Tauri config, Cargo) and check it:
Terminal window pnpm version:set 0.2.0Terminal window pnpm version:check -
Add the version’s section to CHANGELOG.md, commit and push.
-
Tag the commit and push the tag:
Terminal window git tag v0.2.0 && git push origin v0.2.0 -
The workflow builds on all three platforms and attaches every installer to a draft GitHub Release named “OpenBone v0.2.0”. Check the files, edit the notes, then publish the release.
-
Pushing the tag also publishes the Swift package and the Flutter plugin; publishing the release publishes the npm, NuGet, Unity, Maven Central and Godot packages (see Packages). Add the version to
runtimes/flutter/openbone/CHANGELOG.mdtoo, or the pub.dev check fails.
Test builds without a release
Section titled “Test builds without a release”Actions → Desktop installers → Run workflow builds all installers without a release. They are kept as workflow artifacts (downloadable from the run’s page for 90 days by default). Tick Create a draft release to make a draft release for the current app version instead.
Packages
Section titled “Packages”Every package carries the same version (pnpm version:set). The workflows below publish them; each also runs by hand
from the Actions tab with dry-run ticked (the default), which builds, tests and packs without publishing.
Put the secrets below on the protected release environment (Settings → Environments → release), like the signing
secrets: the npm, NuGet and Maven jobs run in it, so each publish waits for your approval. Repository variables
(NUGET_USER, NPM_TRUSTED_PUBLISHING) can stay at repository level.
| Registry | Workflow | Runs on | Credentials (one-time setup) |
|---|---|---|---|
npm: @openbone/*, openbone (CLI) |
publish-npm.yml |
release published | Free npm org openbone; granular token (read and write, @openbone scope + all packages) as secret NPM_TOKEN. Later optional: trusted publisher per package (UXCentury/open-bone, publish-npm.yml), variable NPM_TRUSTED_PUBLISHING=true, delete the token |
NuGet: OpenBone.Runtime, .Scripting.Jint, .MonoGame |
publish-dotnet.yml |
release published | nuget.org Trusted Publishing policy (owner UXCentury, repo open-bone, workflow publish-dotnet.yml) + repository variable NUGET_USER (profile name); or secret NUGET_API_KEY (push scope, glob OpenBone.*) |
Unity: com.openbone.runtime |
publish-unity.yml |
release published | None: commits the built package to the upm branch and tags upm/v<version>. Submit once to OpenUPM with gitTagPrefix: 'upm/' |
Maven Central: io.openbone:openbone-* |
publish-maven.yml |
release published | central.sonatype.com account, namespace io.openbone verified by DNS TXT on openbone.io; user token as MAVEN_CENTRAL_USERNAME / MAVEN_CENTRAL_PASSWORD; RSA 4096 GPG key (public part on keyserver.ubuntu.com and keys.openpgp.org) as SIGNING_KEY (armored) / SIGNING_KEY_PASSWORD |
| Godot addon | publish-godot.yml |
release published | None: attaches openbone-godot-<version>.zip and updates the godot-addon branch. Submit to the Asset Library (Download Commit = the hash in the run’s summary) and upload the zip to the Asset Store |
pub.dev: openbone |
publish-pub.yml |
tag v* pushed |
The first version is published by hand (below); then pub.dev → package admin → Automated publishing: repo UXCentury/open-bone, tag pattern v{{version}}, environment pub.dev |
| Swift Package Manager | none | tag v* pushed |
None: Package.swift is at the repository root, so the tag is the release. Submit once to the Swift Package Index |
-
Flutter: the plugin compiles
runtimes/cpp, which a pub.dev package can’t reach, soruntimes/flutter/openbone/tool/bundle.sh OUTstages a copy with the C++ sources undercpp/(symlinks resolved, third-party licences appended). First publish, signed in to pub.dev:Terminal window runtimes/flutter/openbone/tool/bundle.sh /tmp/openbone-pub/openbone && cd /tmp/openbone-pub/openbone && flutter pub get && flutter pub publish -
Swift: the manifest must not use
unsafeFlags(SwiftPM refuses them in dependencies). Exact maths comes fromruntimes/cpp/src/exact_math.hinstead; the Swift workflow builds a consumer of the package by version to catch this. -
Maven Central and npm never accept the same version twice: fix a bad release with a new version. The other workflows skip what is already published, so re-running them is safe.
-
Locally:
pnpm build:packages(npm),dotnet packinruntimes/csharp,python3 runtimes/csharp/unity/build-package.py --out DIR --pack,./gradlew publishToMavenLocalinruntimes/java,runtimes/cpp/engines/godot/package.sh(see its header).
Code signing
Section titled “Code signing”Unsigned installers work, but macOS Gatekeeper and Windows SmartScreen warn the first time they are opened. Signing is
optional and switched on by secrets on the release environment (Settings → Environments → release). The
workflow’s job runs in that environment, so add yourself as a required reviewer there: a run then waits for your
approval before it can read the certificate. (Repository secrets with the same names also work, but are not gated.)
macOS (Developer ID + notarization)
Section titled “macOS (Developer ID + notarization)”Needs an Apple Developer Program membership.
| Secret | Value |
|---|---|
APPLE_CERTIFICATE |
Your Developer ID Application certificate exported as .p12, base64-encoded (base64 -i cert.p12) |
APPLE_CERTIFICATE_PASSWORD |
The password chosen when exporting the .p12 |
APPLE_SIGNING_IDENTITY |
The certificate’s name, e.g. Developer ID Application: UXCentury (TEAMID) |
APPLE_ID |
The Apple ID email used for notarization |
APPLE_PASSWORD |
An app-specific password for that Apple ID |
APPLE_TEAM_ID |
Your 10-character team ID |
With these set, the macOS job signs the app with hardened runtime and sends it to Apple for notarization, so it opens
without a warning. Tauri notarizes only the app inside the .dmg, so a following step notarizes and staples the disk
image as well and replaces it in the release and the workflow artifact. The workflow passes each secret only when it
exists.
Check a downloaded build with spctl -a -vv -t exec /Applications/OpenBone.app (app) or
spctl -a -vv -t open --context context:primary-signature OpenBone_<version>_universal.dmg (disk image): both should
print source=Notarized Developer ID.
Windows
Section titled “Windows”The workflow signs the app .exe, the setup .exe and the .msi with an SSL.com OV code-signing certificate
through eSigner, SSL.com’s cloud signing: the private key stays in SSL.com’s HSM (no USB token), and only each
file’s hash is sent. SSL.com validates organizations in any country. (Microsoft’s Azure Artifact Signing is cheaper
but only open to organizations in the USA, Canada, the EU and the UK.)
-
Buy an OV Code Signing certificate at ssl.com for the company and complete its validation (company registration documents, a verified phone number).
-
In the SSL.com dashboard, enrol the certificate in eSigner and pick an eSigner plan (the smallest tier is plenty: a release signs about 4 files). When you set up the eSigner PIN/2FA, choose to show the TOTP secret (the QR code’s text, not a 6-digit code) and save it.
-
Add the four secrets to the
releaseenvironment:Secret Value ES_USERNAMEYour SSL.com account username ES_PASSWORDYour SSL.com account password ES_CREDENTIAL_IDThe certificate’s eSigner credential ID (certificate order page → Signing Credentials) ES_TOTP_SECRETThe eSigner TOTP secret from step 2
With all four set, the Windows job downloads SSL.com’s
CodeSignTool (pinned version and checksum) and Tauri runs
apps/desktop/src-tauri/scripts/sign-windows.ps1 for each file,
which checks the signature afterwards. Without them the installers are built unsigned, as before.
Linux packages are not signed. The AppImage can be signed with a GPG key if you want to publish signatures; the
.deb and .rpm are usually signed by the repository that serves them, not the file itself.
Building installers locally
Section titled “Building installers locally”pnpm desktop:buildInstallers for the current platform land in apps/desktop/src-tauri/target/release/bundle/. It needs Rust and
Tauri’s system dependencies (on Linux: libwebkit2gtk-4.1-dev,
librsvg2-dev, patchelf; plus libfuse2 to build the AppImage).
Installing unsigned builds
Section titled “Installing unsigned builds”| Platform | First launch |
|---|---|
| macOS | macOS says Apple “could not verify OpenBone is free of malware”. Click Done, then open System Settings → Privacy & Security, and next to “OpenBone was blocked” click Open Anyway (macOS 15 and later no longer offer right-click → Open for this). Or in Terminal: xattr -dr com.apple.quarantine /Applications/OpenBone.app |
| Windows | SmartScreen shows “Windows protected your PC”: More info → Run anyway |
| Linux AppImage | chmod +x OpenBone_*.AppImage and run it. Newer Ubuntu needs sudo apt install libfuse2, or run it with --appimage-extract-and-run |
| Linux .deb / .rpm | sudo apt install ./OpenBone_*.deb or sudo dnf install ./OpenBone-*.rpm (pulls in WebKitGTK) |
OpenBone is MIT licensed and made by UXCentury. The example art, voices and sounds are credited in the repository.