Release lifecycle
Prepare a release through Release Please, validate the tagged revision, and publish the exact packages that passed validation. Use the version in the release manifest and tag; the examples on this page do not identify the current release.
- Merge reviewed product changes into
main. Release Please accumulates them in one release PR. Let it updateCHANGELOG.md,.release-please-manifest.json, and the version inbuild/config.yml. - Review and merge the refreshed release PR. Merging creates a draft release
and a
v-prefixed SemVer tag, then starts Windows and macOS validation and packaging. Merging an ordinary fix does not publish a release. - Let tagged validation finish. The same CI workflow used by PRs runs every workload and checks the expected version before packaging.
- Publish the validated artifact set. Automation downloads the complete Windows/macOS artifacts from that same run, creates checksums and attestations, then makes the draft public as a non-prerelease and GitHub’s latest release. It verifies the published flags and latest tag.
Version policy
Section titled “Version policy”0.1.0 is the first non-alpha baseline. It remains pre-1.0 SemVer, without a
frozen 1.0 compatibility contract.
| PR title type | Use for | Example bump from 0.1.0 |
|---|---|---|
fix |
Corrective changes | 0.1.1 |
feat |
New capabilities | 0.2.0 |
| Explicit breaking change | A breaking contract change | 1.0.0 |
Write the PR title as a Conventional Commit, for example:
fix(security): block inference redirects and sanitize response metadataThe repository squash-merges with the PR title as commit subject and an empty
body. Release Please reads that title; branch commit messages and PR descriptions
do not create separate release-note entries. Do not add ! or force a release
version for an ordinary bug fix.
Release Please version strategy and ownership
Release Please uses prerelease: false with its prerelease versioning strategy.
Despite that strategy’s name, this combination graduates an existing alpha to
its non-prerelease base version and then applies ordinary SemVer bumps to stable
versions. There is no release-as override pinning future releases. Keep the
release manifest and application version in the bot-owned release PR; changing
initial-version alone does not override an existing release’s version.
Merge reviewed product fixes before the pending release PR so Release Please can
refresh its changelog. Leave CHANGELOG.md, .release-please-manifest.json, and
the version in build/config.yml to that release PR rather than editing them in
fix branches. Merging a fix is not publication: merging the refreshed release PR
starts the draft/tag and Windows and macOS packaging flow described below.
Keep one release identity
Section titled “Keep one release identity”| Metadata | Source or derivation |
|---|---|
| Human version and About | build/config.yml; About reads its embedded value |
| Windows resources and installer | Four-part numeric version derived from the same SemVer value |
macOS CFBundleShortVersionString |
The three release components |
macOS CFBundleVersion |
Numeric build derived from the same SemVer value; no separate counter |
Numeric Windows and macOS version rules
build/config.yml is the release identity source. The release build derives
Windows’ required four-part numeric version from that SemVer value before it
generates the executable resources and installer metadata. About reads the same
embedded source. macOS build numbers are derived from that same SemVer value;
there is no separate counter to update. CFBundleShortVersionString uses the
three release components. CFBundleVersion is numeric major.minor.build,
where build = patch × 262144 + stage × 65536 + revision; stages are alpha (0),
beta (1), rc (2), and stable (3, revision 0). This keeps prerelease-to-stable
ordering while satisfying Apple’s numeric bundle format. Supported prereleases
are alpha.N, beta.N, and rc.N, with the same positive uint16 revision
bound used by the Windows resources. For example, 0.1.0-alpha.5 produces
bundle build 0.1.5, without changing its displayed release version.
Prerelease versions end in a positive numeric revision. For example,
0.1.0-alpha.1 maps to Windows 0.1.0.1; a stable release uses a zero revision.
Release Please uses its generic line updater and the
x-release-please-version annotation on that one field. Do not switch it to a
YAML updater: serializing the complete Wails configuration strips comments and
creates unrelated formatting churn.
After changing product identity or a version manually, synchronize the derived assets:
wails3 task common:update:build-assetsReview the generated diff. build/windows/nsis/project.nsi contains
application-specific policy that must survive an upstream Wails asset refresh.
Do not repair derived Windows version fields individually.
Build the Windows package
Section titled “Build the Windows package”Freehand requires CGo for native audio. CI tests and packages x64 on
windows-latest and ARM64 on windows-11-arm. The ARM64 job uses checksum-pinned
LLVM-MinGW; the x64 job uses MinGW-w64 GCC. Native interactive acceptance still
requires execution on the corresponding architecture.
Build the public default locally:
wails3 task package CGO_ENABLED=1 ARCH=amd64For an intentional machine-wide installation, add INSTALL_SCOPE=machine:
wails3 task package CGO_ENABLED=1 ARCH=amd64 INSTALL_SCOPE=machineBoth paths write bin/freehand-amd64-installer.exe. The all-users installer
requires elevation and is not the public default.
For ARM64, use ARCH=arm64 with LLVM-MinGW’s ARM64 compiler. The pinned setup
script can also install an x64-hosted compiler for local cross-compilation:
./.github/workflows/scripts/install-arm64-toolchain.ps1 -Destination "$PWD/bin/toolchains"$env:CC = "$PWD/bin/toolchains/llvm-mingw-20260922-ucrt-aarch64/bin/aarch64-w64-mingw32-clang.exe"wails3 task package CGO_ENABLED=1 ARCH=arm64On an x64 host, use the script’s -HostArchitecture X64 option and the
llvm-mingw-20260922-ucrt-x86_64 directory instead. ARM64 packaging writes
bin/freehand.exe and bin/freehand-arm64-installer.exe. Local cross-compilation
does not establish ARM64 runtime acceptance.
| Check | Expected installer behavior |
|---|---|
| Shortcuts | One Start Menu shortcut; no Desktop shortcut |
| After install | Freehand is not launched automatically |
| App already running | Install/uninstall stops with instructions; it does not terminate the tray process |
| Uninstall | Settings, WebView state, and Credential Manager entries are retained |
Public artifact contract
Section titled “Public artifact contract”New cross-platform releases must contain the complete asset set below. Older releases may lack ARM64 Windows or macOS assets; never present a missing asset as downloadable.
| Required asset | Purpose |
|---|---|
freehand-windows-amd64.exe |
Bare executable selected by Wails updater |
freehand-windows-amd64-installer.exe |
Per-user NSIS installer |
freehand-windows-arm64.exe |
Native Windows ARM64 executable selected by Wails updater |
freehand-windows-arm64-installer.exe |
Per-user ARM64 NSIS installer |
freehand-darwin-arm64.zip |
Apple Silicon app bundle |
freehand-darwin-amd64.zip |
Intel app bundle |
SHA256SUMS |
Exactly the six assets above; no missing or extra entries |
Publication must reject incomplete sets and use only the artifacts from the
same trusted, validated tagged workflow run. Do not rebuild or mix runs.
Windows CI checks each executable’s PE architecture before upload and renames
the two freehand.exe outputs to freehand-amd64.exe and freehand-arm64.exe
so release assembly cannot overwrite one architecture with the other.
The website reads public release assets on page load and enables an architecture’s
download links only when its files exist in that release.
macOS packaging and updates
Section titled “macOS packaging and updates”Build on native macOS with cgo, the pinned Wails CLI, and Xcode Command Line Tools. The deployment target is macOS 13+.
wails3 task package ARCH=arm64wails3 task package ARCH=amd64Before publication, validate architecture, bundle identity, version, resources, ZIP contents, and ad-hoc signature. Build checks do not establish real microphone, keyboard, insertion, Intel hardware, or update/relaunch acceptance.
The pinned Wails updater selects the exact Darwin architecture ZIP, verifies
SHA256SUMS, extracts its .app, and uses a helper to swap the app bundle on
Restart. Freehand configures no update public key. Ad-hoc signatures and checksums
are not Developer ID trust or notarization; native install/relaunch acceptance
is separate. Keep manual ZIP replacement
available as recovery.
Windows in-app updates
Section titled “Windows in-app updates”| Trigger or stage | Behavior |
|---|---|
| Automatic checks | Enabled by default; shortly after startup and once per day; disable under Settings → General |
| Already current | Reads GitHub release metadata only |
| Update found | Wails opens its update window, downloads and verifies the executable |
| Check now | Opens the same flow on demand |
| Restart | Requires user action |
The current Wails v3 updater stages a verified bare executable by replacing the installed executable. It does not run the NSIS installer. Installer-level migrations or additional packaged files would require a future updater policy change.
There is no Ed25519 release key in the first public lifecycle. The standard
Wails GitHub provider verifies the downloaded executable with the release’s
SHA256SUMS; adding an unused private signing key would not strengthen that
path. Authenticode is the separate future mechanism for Windows publisher
identity.
Future Authenticode signing procedure
When Authenticode is introduced, both the executable and final installer must
be signed with a trusted timestamp. Wails reads a configured certificate from
wails3 setup signing or the SIGN_CERTIFICATE, SIGN_THUMBPRINT, and
TIMESTAMP_SERVER task variables. The ordered local task is:
wails3 task package:signed CGO_ENABLED=1 ARCH=amd64No signing credential is configured today. Never commit certificates, passwords, or thumbprints.