mirror of
https://github.com/storytold/filmcraft.git
synced 2026-10-25 02:27:47 -06:00
- release.yml: new `linux-riscv64` job, cross-compiled on ubuntu-24.04 against ports.ubuntu.com riscv64 libs (ported from GridCraft), tar.gz only, CLI smoke-tested under QEMU. Installs libasound2-dev:riscv64 for cpal/ALSA; the VA-API and NVDEC/NVENC paths are loaded with libloading at run time, so they need no riscv64 libraries. The draft-release job now needs it, so its tarball is uploaded and covered by SHA256SUMS.txt. - packaging/linux/package.sh: CROSS_ARCH / CROSS_TARGET / CROSS_COMPILE / EMULATOR support, as in GridCraft. - README and docs/releasing.md list the riscv64 tarball. - cargo xtask version set 0.6.0 (Cargo.toml, Cargo.lock); refresh contributors. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
184 lines
12 KiB
Markdown
184 lines
12 KiB
Markdown
# Releasing FilmCraft
|
||
|
||
Every push to the `release` branch runs `.github/workflows/release.yml`. The workflow builds
|
||
installers for macOS, Windows, Linux (AppImage, deb, rpm, tarball, Flatpak) and FreeBSD, plus
|
||
the web build, and creates or updates a **draft**
|
||
GitHub Release named `FilmCraft v<version>`. Nobody sees a draft until a maintainer publishes it.
|
||
|
||
User-facing names say **FilmCraft**. Files, binaries and ids stay lowercase
|
||
(`filmcraft-<version>-<platform>-<arch>.<ext>`, `ai.storyteller.filmcraft`).
|
||
|
||
## 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.2.1
|
||
cargo xtask version set 0.3.0 # or 0.3.0-rc.1; updates Cargo.toml and Cargo.lock
|
||
```
|
||
|
||
Commit the change (`Cargo.toml` + `Cargo.lock`) through the normal review flow, as a
|
||
`Release: FilmCraft v0.3.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 `FilmCraft v0.3.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.3.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.3.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 from any other branch, it is a dry run: the jobs that
|
||
sign nothing (Linux, Flatpak, FreeBSD, web) build and upload their artifacts to the run, the
|
||
signing jobs are refused, and the draft-release job, which needs them, is skipped.
|
||
|
||
## What gets built
|
||
|
||
| Platform | Artifacts | Built on |
|
||
|---|---|---|
|
||
| macOS 11+ (universal: Apple silicon and Intel) | `filmcraft-<v>-macos-universal.dmg`, `filmcraft-cli-<v>-macos-universal.zip` | `macos-15` |
|
||
| Windows x64 | `filmcraft-<v>-windows-x64.msi`, `filmcraft-<v>-windows-x64-portable.zip` | `windows-latest` |
|
||
| Windows x86 (32-bit) | `filmcraft-<v>-windows-x86.msi`, `filmcraft-<v>-windows-x86-portable.zip` | `windows-latest` |
|
||
| Windows on ARM64 | `filmcraft-<v>-windows-arm64.msi`, `filmcraft-<v>-windows-arm64-portable.zip` | `windows-latest` (cross-compiled) |
|
||
| Linux x86_64 | `filmcraft-<v>-linux-x86_64.{AppImage,deb,rpm,tar.gz}` | `ubuntu-22.04` |
|
||
| Linux aarch64 | `filmcraft-<v>-linux-aarch64.{AppImage,deb,rpm,tar.gz}` | `ubuntu-22.04-arm` |
|
||
| Linux riscv64 | `filmcraft-<v>-linux-riscv64.tar.gz` | `ubuntu-24.04`, cross-compiled (glibc 2.39+; CLI smoke-tested under QEMU) |
|
||
| AppImage updates | `filmcraft-<v>-linux-{x86_64,aarch64}.AppImage.zsync` | with the AppImage |
|
||
| Flatpak | `filmcraft-<v>-linux-x86_64.flatpak`, `filmcraft-<v>-linux-aarch64.flatpak` | `ubuntu-24.04`, `ubuntu-24.04-arm` (repackaged Linux tarball) |
|
||
| FreeBSD 14 x86_64 | `filmcraft-<v>-freebsd-x86_64.tar.gz` | FreeBSD 14.3 VM on `ubuntu-latest` |
|
||
| Web | `filmcraft-web-<v>.zip`, a static site (see [`packaging/web/README.md`](../packaging/web/README.md)) | `ubuntu-latest` |
|
||
|
||
`filmcraft --version` and `filmcraft-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, marked `# bump deliberately`) 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, to stay small). The packages
|
||
carry each embedded font's licence (`copy_font_licences` in `packaging/env.sh`). Bump the pins
|
||
deliberately, in every job at once.
|
||
|
||
### 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 `FilmCraft.app`:
|
||
|
||
- `Info.plist` is generated from `Info.plist.in` (bundle id `ai.storyteller.filmcraft`, the
|
||
version and the build commit).
|
||
- **Signing** uses the hardened runtime and a secure timestamp, with the entitlements in
|
||
`entitlements.plist`. The Developer ID certificate is imported into a temporary keychain by
|
||
`packaging/macos/import-cert.sh`.
|
||
- **Notarization:** the app is sent with `xcrun notarytool submit`, then the ticket is stapled
|
||
and checked with `stapler validate`. The app goes on a DMG (`hdiutil makehybrid`), which is
|
||
signed and notarized too. The universal `filmcraft-cli` is signed the same way, zipped, and the zip is notarized.
|
||
Its Finder window (background, icon size and positions) comes from
|
||
[`packaging/macos/dmg/`](../packaging/macos/dmg/README.md), and its volume is named `FilmCraft`
|
||
without the version, which the window's background needs; the DMG file name keeps the version.
|
||
|
||
Locally, without certificates, the script signs ad hoc and skips notarization, which is enough to
|
||
check the bundle and the DMG on your own Mac (`packaging/macos/package.sh`).
|
||
|
||
### Windows
|
||
|
||
`packaging/windows/package.ps1 -Arch x64|x86|arm64` builds with `+crt-static`, so neither the MSI
|
||
nor the portable zip needs the Visual C++ redistributable.
|
||
|
||
- `filmcraft.wxs` (WiX) is a per-machine install into Program Files with a Start Menu shortcut and
|
||
an App Paths entry (Win+R `filmcraft`). 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, runs `filmcraft-cli --version` natively and
|
||
uninstalls.
|
||
- **Signing:** `packaging/windows/sign.ps1` signs the executables and then the `.msi` with
|
||
`signtool`, using whichever material is present: a `.pfx` certificate (`WINDOWS_CERTIFICATE`,
|
||
base64, and `WINDOWS_CERTIFICATE_PASSWORD`) or Azure Trusted Signing (the `AZURE_*` secrets). With
|
||
neither, it warns and leaves the files unsigned, so test builds still produce installers.
|
||
|
||
### 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. The packages install both programs, the desktop entry, the AppStream metainfo, the
|
||
icons and the licence files.
|
||
|
||
The Linux tarball includes `install.sh`. Extract it, open a terminal in the extracted
|
||
directory and run `./install.sh` to install both programs and their desktop integration
|
||
under `~/.local` (no administrator permissions needed). The application-menu entry uses
|
||
the installed binary's absolute path; add `~/.local/bin` to `PATH` for terminal commands.
|
||
Run the installer again to update. For a system-wide installation, use
|
||
`sudo ./install.sh --prefix /usr/local`. A custom absolute `--prefix` is also supported;
|
||
its `share` directory must be in the desktop's data search path to appear in the menu.
|
||
Prefixes containing `=`, `%` or control characters are rejected for desktop-launcher compatibility.
|
||
|
||
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. The deb (`libc6 (>= 2.35)`)
|
||
and the rpm (`glibc >= 2.35`) both declare that floor, so older systems refuse the install. Moving
|
||
the job to a newer image raises the floor, so do it deliberately and update `nfpm.yaml` with it.
|
||
After packaging, the job prints the `.deb`'s metadata and contents, runs `ldd` on the binary and
|
||
runs each AppImage with `--version`.
|
||
|
||
**AppImage updates.** Each AppImage embeds update information,
|
||
`gh-releases-zsync|storytold|filmcraft|latest|filmcraft-*-linux-<arch>.AppImage.zsync`, and
|
||
`appimagetool` writes the matching `.zsync` beside it (the job installs `zsync` for
|
||
`zsyncmake`). [AppImageUpdate](https://github.com/AppImageCommunity/AppImageUpdate) and
|
||
AppImageLauncher then fetch only the changed blocks from the newest published (non-pre-release)
|
||
release. The job checks both with `--appimage-updateinformation` and `test -s`.
|
||
|
||
**Flatpak.** The `flatpak` jobs (x86_64 on `ubuntu-24.04`, aarch64 on `ubuntu-24.04-arm`) run
|
||
`packaging/linux/flatpak-bundle.sh` on the Linux job's tarball: it installs the binaries with
|
||
`packaging/linux/flatpak/ai.storyteller.filmcraft.bundle.yml` into a single-file bundle,
|
||
`filmcraft-<v>-linux-<arch>.flatpak` (no Rust build, so it takes a minute), then installs it and
|
||
runs `filmcraft-cli --version` in the sandbox. Users install it with
|
||
`flatpak install --user <file>`; the freedesktop runtime comes from Flathub.
|
||
`packaging/linux/flatpak/ai.storyteller.filmcraft.yml` is the from-source manifest for a Flathub
|
||
submission (its header says how to build it). Both manifests must keep the same runtime and
|
||
`finish-args`, and install the same programs: the app and `filmcraft-cli`, which runs the
|
||
headless commands and the MCP server in the sandbox
|
||
(`flatpak run --command=filmcraft-cli ai.storyteller.filmcraft mcp`). packaging-lint checks this.
|
||
|
||
### FreeBSD
|
||
|
||
GitHub has no FreeBSD runners, so the `freebsd` job builds in a FreeBSD 14.3 VM
|
||
([vmactions/freebsd-vm](https://github.com/vmactions/freebsd-vm), pinned by commit) on
|
||
`ubuntu-latest`. `packaging/freebsd/package.sh` builds both programs and packs a
|
||
`/usr/local`-style tree as `filmcraft-<v>-freebsd-x86_64.tar.gz`; install it with
|
||
`tar -xzf filmcraft-<v>-freebsd-x86_64.tar.gz --strip-components 1 -C /usr/local`. The build needs
|
||
the packages listed in the job (winit/wgpu, GTK 3 for the file dialogs, fontconfig, and
|
||
`alsa-lib` for audio through cpal). `.github/workflows/freebsd.yml` builds and packages the same
|
||
way nightly, so a FreeBSD break shows up before a release. `package.sh --dry-run` checks the
|
||
tree's layout with stub binaries on any OS (packaging-lint runs it).
|
||
|
||
### Web
|
||
|
||
The web job builds `apps/filmcraft-web` with `cargo xtask web` (the `wasm-bindgen` CLI must match
|
||
the crate's version exactly, `WASM_BINDGEN` in `xtask/src/main.rs`) and zips the static site with
|
||
`packaging/web/package.sh`. [`packaging/web/README.md`](../packaging/web/README.md) covers hosting.
|
||
|
||
## The draft release
|
||
|
||
The last job waits for every build, downloads their artifacts, writes `SHA256SUMS.txt` and creates
|
||
the draft `FilmCraft 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 macOS, Windows and draft-release jobs run in the `release` environment, which only the
|
||
`release` branch can use and which holds the signing secrets. The other jobs sign nothing and 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 |
|
||
| `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 |
|