NC Stylo · Docs

Releasing Stylo

Last updated: 2026-10-12 · Change: minor (RPM runtime capabilities and metadata checks, #1275)

Every push to the release branch runs .github/workflows/release.yml. The workflow builds installers for macOS, Windows, Linux (including Flatpak bundles and a RISC-V tarball) and FreeBSD, plus the web build, and creates or updates a draft GitHub Release named Stylo v<version>. Nobody sees a draft until a maintainer publishes it.

User-facing names say Stylo. Files, binaries and ids stay lowercase (stylo-<version>-<platform>-<arch>.<ext>, com.ncdev.stylo).

Cutting a release

  1. Bump the version on main. It lives in exactly one place, [workspace.package] version in the root Cargo.toml; every crate inherits it and the packaging scripts read it from there:

    cargo xtask version                 # prints the current version, e.g. 0.3.1
    cargo xtask version set 0.4.0       # or 0.4.0-rc.1; updates Cargo.toml and Cargo.lock

    Commit the change (Cargo.toml + Cargo.lock) through the normal review flow, as a Release: Stylo v0.4.0 commit whose message says what changed for users.

  2. Merge main into release (or fast-forward it) and push. The workflow starts by itself.

  3. Wait for the draft. When every job is done (macOS notarization is the slow part), the Releases page has a draft Stylo v0.4.0, targeting the pushed commit, with every artifact and SHA256SUMS.txt. The notes are generated from the merged pull requests.

  4. Check it. Download an installer or two and read the job summaries. A ::warning:: there means a signing secret was missing and that artifact is unsigned.

  5. Publish the draft in the GitHub UI. Publishing creates the v0.4.0 tag. A version with a pre-release suffix (-rc.1) is marked as a pre-release.

Pushing to release again before you publish rebuilds the same draft and replaces its assets. Once the draft is published, the workflow refuses to touch that version again: bump it first.

Test runs: Actions › Release › Run workflow runs the whole pipeline by hand. The optional version input (such as 0.4.0-rc.1) overrides Cargo.toml for that run only; each build job applies it with cargo xtask version set before building. The signing jobs (macOS, Windows) and the draft-release job run in the release environment, which only the release branch can use, so pick that branch for a full run. Run on any other branch, it is a dry run: the Linux, Flatpak, FreeBSD and web jobs build and upload their artifacts, the environment refuses the signing jobs, and no draft release is created.

What gets built

Platform Artifacts Built on
macOS 11+ (universal: Apple silicon and Intel) stylo-<v>-macos-universal.dmg, stylo-cli-<v>-macos-universal.zip macos-15
Windows x64 stylo-<v>-windows-x64.msi, stylo-<v>-windows-x64-portable.zip windows-latest
Windows x86 (32-bit) stylo-<v>-windows-x86.msi, stylo-<v>-windows-x86-portable.zip windows-latest
Windows on ARM64 stylo-<v>-windows-arm64.msi, stylo-<v>-windows-arm64-portable.zip windows-latest (cross-compiled)
Linux x86_64 stylo-<v>-linux-x86_64.{AppImage,deb,rpm,tar.gz} ubuntu-22.04
Linux aarch64 stylo-<v>-linux-aarch64.{AppImage,deb,rpm,tar.gz} ubuntu-22.04-arm
Linux riscv64 stylo-<v>-linux-riscv64.tar.gz (cross-compiled, glibc >= 2.39; CLI smoke-tested under QEMU) ubuntu-24.04
Linux AppImage updates stylo-<v>-linux-{x86_64,aarch64}.AppImage.zsync with the AppImages
Flatpak x86_64, aarch64 stylo-<v>-linux-{x86_64,aarch64}.flatpak ubuntu-24.04, ubuntu-24.04-arm (repackages the Linux tarball)
FreeBSD 14 x86_64 stylo-<v>-freebsd-x86_64.tar.gz a FreeBSD 14.3 VM on ubuntu-latest
Web stylo-web-<v>.zip, a static site (see packaging/web/README.md) ubuntu-latest

stylo --version and stylo-cli --version print the version from Cargo.toml.

Fonts. Every build job checks out craft-fonts at a pinned commit (the ref: of its craft-fonts checkout step) and builds with CRAFT_FONTS_DIR and CRAFT_FONTS_REQUIRED=1, so releases embed its Japanese fonts and fail rather than ship without them (the web build embeds only the UI font, BIZ UDPGothic Regular, to stay small). The packages carry each embedded font's licence as OFL-<family>.txt (copy_font_licences in packaging/env.sh). Bump the pins deliberately, in every job at once. The rules are in craftrules standards/fonts.md, the build option in docs/development.md › Fonts.

macOS

packaging/macos/package.sh builds aarch64-apple-darwin and x86_64-apple-darwin with MACOSX_DEPLOYMENT_TARGET=11.0, joins them with lipo and assembles Stylo.app:

Locally, without certificates, the script signs ad hoc (codesign -s -) and skips notarization, which is enough to check the bundle and the DMG on your own Mac:

packaging/macos/package.sh --arch universal   # needs both rustup targets
packaging/macos/package.sh --arch aarch64     # quicker, host only

Windows

packaging/windows/package.ps1 -Arch x64|x86|arm64 builds with -C target-feature=+crt-static, so neither the MSI nor the portable zip needs the Visual C++ redistributable. The flag is scoped to the target triple, so host build scripts aren't affected.

Linux

packaging/linux/package.sh builds the release binaries and packages them as an AppImage, a .deb and an .rpm (with nfpm, from nfpm.yaml) and a plain .tar.gz tree (bin/, share/). The packages install both programs, the desktop entry, the AppStream metainfo, the MIME type for Stylo documents, the icons and the licence files.

Document types. The desktop entry's MimeType lists every format File › Open reads but PSD, PSB, .ps PostScript and Targa, so file managers list Stylo under Open With for them. com.ncdev.stylo.mime.xml declares the types that shared-mime-info lacks: Stylo documents and templates, .ait templates and Affinity files (application/x-affinity). An .af file gets the Affinity type by its content, and .afdesign and .afpub files by their extension. A test keeps both files in step with the engine's OPEN_EXTS.

The jobs run on ubuntu-22.04, the oldest GitHub-hosted image, so the binaries only need glibc 2.35 or newer: Ubuntu 22.04+, Debian 12+, Fedora 36+ and RHEL 10. Moving the job to a newer image raises that floor, so do it deliberately. After packaging, the job prints the .deb's metadata and contents, runs ldd on the binary and runs each AppImage with --version.

RPM dependencies. The x86_64 and aarch64 RPMs require 64-bit shared-library capabilities rather than distribution-specific package names: libc, libm's GLIBC_2.35, libgcc_s, libxkbcommon, and either Vulkan or EGL. Windowing and GPU libraries are loaded at runtime, so ldd alone does not list every requirement. The glibc capability enforces the same floor as the .deb; a distribution with an older glibc is not supported by these binaries. It names libm, whose hypot and hypotf are the binaries' GLIBC_2.35 symbols: RHEL 9's glibc 2.34 provides libc.so.6(GLIBC_2.35) through backports, so a libc capability let it install binaries that can't start. Weak recommendations still use package names and do not block installation.

Before publishing a Linux release:

RISC-V. The linux-riscv64 job cross-compiles riscv64gc-unknown-linux-gnu on ubuntu-24.04 against riscv64 libraries from ports.ubuntu.com (so the floor there is glibc 2.39) and runs package.sh --formats tar with CROSS_ARCH, CROSS_TARGET, CROSS_COMPILE and EMULATOR=qemu-riscv64 set: a .tar.gz only, with stylo-cli --version run under QEMU.

AppImage updates. Each AppImage embeds the update information gh-releases-zsync|storytold|stylo|latest|stylo-*-linux-<arch>.AppImage.zsync (appimagetool -u), and package.sh writes the matching .zsync beside it when zsyncmake (the zsync package) is installed. AppImageUpdate and AppImageLauncher then download only the changed blocks from the newest published, non-pre-release release. The job checks both are there.

Flatpak. The flatpak jobs (x86_64 on ubuntu-24.04, aarch64 on ubuntu-24.04-arm) take the Linux job's tarball and repackage it with packaging/linux/flatpak-bundle.sh and packaging/linux/flatpak/com.ncdev.stylo.bundle.yml into a single-file bundle (flatpak install --user stylo-<v>-linux-<arch>.flatpak), then install it and run stylo-cli --version in the sandbox. No Rust build happens there, so the bundle carries the same binaries as the AppImage. packaging/linux/flatpak/com.ncdev.stylo.yml is the from-source manifest for a Flathub submission (its header says how to build it); packaging-lint keeps the runtime and sandbox permissions of the two manifests identical.

FreeBSD

GitHub has no FreeBSD runners, so the job builds in a FreeBSD 14.3 VM on ubuntu-latest (the same image as .github/workflows/freebsd.yml). packaging/freebsd/package.sh writes a /usr/local-style tree; users install it with tar -xzf stylo-*-freebsd-x86_64.tar.gz --strip-components 1 -C /usr/local.

Web

The web job builds apps/stylo-web with trunk and zips the static site (packaging/web/package.sh). packaging/web/README.md covers hosting: any path works, .wasm must be served as application/wasm, and the sample header files for common hosts.

The draft release

The last job waits for every build, downloads their artifacts, writes SHA256SUMS.txt and creates the draft Stylo v<version> with notes generated from the merged pull requests. If the draft already exists, it replaces its assets and keeps it a draft. If that version is already published, the job fails and asks for a version bump (cargo xtask version set).

Secrets

The jobs that sign (macOS, Windows) and the draft-release job run in the release environment, which only the release branch can use and which holds the signing secrets. The other jobs get no secrets. Every secret is optional: a missing one produces unsigned artifacts and a warning, never a failed build.

Secret Used for
APPLE_CERTIFICATE, APPLE_CERTIFICATE_PASSWORD, KEYCHAIN_PASSWORD the Developer ID certificate (base64 .p12), imported into a temporary keychain by packaging/macos/import-cert.sh
APPLE_ID, APPLE_PASSWORD, APPLE_TEAM_ID notarization (APPLE_PASSWORD is an app-specific password)
WINDOWS_CERTIFICATE, WINDOWS_CERTIFICATE_PASSWORD Windows signing with a .pfx
AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_SIGNING_ENDPOINT, AZURE_SIGNING_ACCOUNT, AZURE_CERT_PROFILE Windows signing with Azure Trusted Signing

Revision history

Date Change Summary
2026-10-14 minor RPM glibc floor names libm's GLIBC_2.35: RHEL 9's glibc 2.34 provides libc's through backports
2026-10-12 minor RPM dependencies use 64-bit runtime capabilities and enforce glibc 2.35 (#1275); real distro install/start-up checks remain pending
2026-10-12 minor Portable mode (#1366): the Windows portable zip ships portable.txt; where the marker goes on macOS and Linux, and what Flatpak can't do