From ee1e95d1c7c40ff670ad7233636408404d381510 Mon Sep 17 00:00:00 2001 From: bflatastic Date: Wed, 7 Oct 2026 09:17:10 +0700 Subject: [PATCH] Fix #231: docs/releasing.md describes the release pipeline release.yml's comments sent readers to docs/releasing.md, which never existed: the workflow was ported from a sibling app without its document. docs/releasing.md now covers how a release is cut (version bump with cargo xtask version, the release branch, the draft, publishing, test runs with a version override), what each job builds and how (macOS universal signing and notarization, Windows x64/x86/arm64 with the PE checks and the two signing options, Linux packages and the glibc 2.35 floor behind the ubuntu-22.04 runners, FreeBSD in a VM, the web build, craft-fonts), the draft release job and the signing secrets. Every detail is checked against this repository's workflow and packaging scripts. The README links it. Co-Authored-By: Claude Opus 5.5 --- README.md | 3 +- docs/releasing.md | 163 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 docs/releasing.md diff --git a/README.md b/README.md index dd9763533..846a20c24 100644 --- a/README.md +++ b/README.md @@ -197,7 +197,8 @@ the UI can be swapped without touching the engine. Agent and contributor rules (clean-room, the asset policy, no panics in shipped code, quality gates) are in [`AGENTS.md`](AGENTS.md); [`docs/development.md`](docs/development.md#robustness-vectorcraft-never-crashes) -explains how VectorCraft avoids crashing. Every bundled asset is listed with its licence in [`ASSETS.md`](ASSETS.md). +explains how VectorCraft avoids crashing, and [`docs/releasing.md`](docs/releasing.md) how releases are built, +signed and published. Every bundled asset is listed with its licence in [`ASSETS.md`](ASSETS.md). ## The Crafting Apps diff --git a/docs/releasing.md b/docs/releasing.md new file mode 100644 index 000000000..31e9579a3 --- /dev/null +++ b/docs/releasing.md @@ -0,0 +1,163 @@ +# Releasing VectorCraft + +Every push to the `release` branch runs `.github/workflows/release.yml`. The workflow builds +installers for macOS, Windows, Linux and FreeBSD, plus the web build, and creates or updates a +**draft** GitHub Release named `VectorCraft v`. Nobody sees a draft until a maintainer +publishes it. + +User-facing names say **VectorCraft**. Files, binaries and ids stay lowercase +(`vectorcraft---.`, `ai.storyteller.vectorcraft`). + +## 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: + + ```sh + 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: VectorCraft 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 `VectorCraft 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 jobs run in the `release` +environment, which only the `release` branch can use, so pick that branch in the dialog. + +## What gets built + +| Platform | Artifacts | Built on | +|---|---|---| +| macOS 11+ (universal: Apple silicon and Intel) | `vectorcraft--macos-universal.dmg`, `vectorcraft-cli--macos-universal.zip` | `macos-15` | +| Windows x64 | `vectorcraft--windows-x64.msi`, `vectorcraft--windows-x64-portable.zip` | `windows-latest` | +| Windows x86 (32-bit) | `vectorcraft--windows-x86.msi`, `vectorcraft--windows-x86-portable.zip` | `windows-latest` | +| Windows on ARM64 | `vectorcraft--windows-arm64.msi`, `vectorcraft--windows-arm64-portable.zip` | `windows-latest` (cross-compiled) | +| Linux x86_64 | `vectorcraft--linux-x86_64.{AppImage,deb,rpm,tar.gz}` | `ubuntu-22.04` | +| Linux aarch64 | `vectorcraft--linux-aarch64.{AppImage,deb,rpm,tar.gz}` | `ubuntu-22.04-arm` | +| FreeBSD 14 x86_64 | `vectorcraft--freebsd-x86_64.tar.gz` | a FreeBSD 14.3 VM on `ubuntu-latest` | +| Web | `vectorcraft-web-.zip`, a static site (see [`packaging/web/README.md`](../packaging/web/README.md)) | `ubuntu-latest` | + +`vectorcraft --version` and `vectorcraft-cli --version` print the version from `Cargo.toml`. + +**Fonts.** Every build job checks out [craft-fonts](https://github.com/storytold/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-.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`](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 `VectorCraft.app`: + +- `Info.plist` is generated from `Info.plist.in` (bundle id `ai.storyteller.vectorcraft`, + `LSMinimumSystemVersion` 11.0, the version and the build commit). The icon is + `assets/app-icon/vectorcraft.icns`. +- **Signing** goes inside-out with the hardened runtime and a secure timestamp: the executable + first, then the bundle, with no `--deep` on the final signature. The entitlements + (`entitlements.plist`) are deliberately empty. +- **Notarization:** the app is zipped and sent with `xcrun notarytool submit --wait`, then the + ticket is stapled and checked with `stapler validate` and `spctl`. The app goes on a DMG + (`hdiutil makehybrid` and `convert`, with an `Applications` link to drag onto), which is signed, + notarized and stapled too. +- **CLI:** the universal `vectorcraft-cli` is signed the same way, zipped, and the zip is + notarized. A bare executable can't hold a stapled ticket, so Gatekeeper looks the CLI's ticket up + online the first time a downloaded copy runs. + +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: + +```sh +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. + +- Before packaging, the script checks each binary's PE machine type against `-Arch` (an x64 build + can never ship labelled arm64), that `vectorcraft.exe` is a GUI program (no console window) and + that `vectorcraft-cli.exe` is a console program. +- `vectorcraft.wxs` (WiX) is a per-machine install into Program Files with a Start Menu shortcut + and an App Paths entry (Win+R `vectorcraft`). Same-version upgrades are allowed, so release + candidates replace each other. +- The ARM64 build is cross-compiled on the x64 runner. `.github/workflows/windows-arm64.yml` + installs that MSI on a Windows 11 ARM64 runner, checks that both installed programs are ARM64, + runs `vectorcraft-cli --version` natively and uninstalls. +- **Signing:** `packaging/windows/sign.ps1` signs both `.exe` files and then the `.msi` with + `signtool` (SHA-256, RFC 3161 timestamp), using whichever material is present: + 1. a `.pfx` certificate (`WINDOWS_CERTIFICATE`, base64, and `WINDOWS_CERTIFICATE_PASSWORD`), or + 2. Azure Trusted Signing (`AZURE_TENANT_ID`, `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, + `AZURE_SIGNING_ENDPOINT`, `AZURE_SIGNING_ACCOUNT`, `AZURE_CERT_PROFILE`). + + With neither, it warns and leaves the files unsigned, so test builds still produce installers. + It is the one place to adapt when the signing setup changes; `WINDOWS_TIMESTAMP_URL` and + `SIGNTOOL` override the timestamp server and the `signtool.exe` path. + +### Linux + +`packaging/linux/package.sh` builds the release binaries and packages them as an AppImage, a +`.deb` and an `.rpm` (with [nfpm](https://nfpm.goreleaser.com), 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 VectorCraft documents, the icons and the licence files. + +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`. + +`packaging/linux/flatpak/ai.storyteller.vectorcraft.yml` is a Flatpak manifest ready for a +Flathub submission (its header says how to build it). The release doesn't build a Flatpak; the +workflows only check the manifest's id. + +### 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 vectorcraft-*-freebsd-x86_64.tar.gz --strip-components 1 -C /usr/local`. + +### Web + +The web job builds `apps/vectorcraft-web` with trunk and zips the static site +(`packaging/web/package.sh`). [`packaging/web/README.md`](../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 `VectorCraft v` 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 + +All jobs run in the `release` environment, which only the `release` branch can use and which holds +the signing 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 |