diff --git a/CLAUDE.md b/CLAUDE.md index 9b7129b5..27191c6a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,6 +1,6 @@ # FilmCraft — instructions for agents -FilmCraft is a clean-room, open-source, pure-Rust non-linear video editor targeting Adobe Premiere Pro parity (and beyond). Native on macOS, Windows, Linux; web via WASM. Sibling of `../photocraft` (Photoshop), `../printcraft` (Acrobat) and `../drawcraft` (Illustrator), with the same conventions. +FilmCraft is a clean-room, open-source, pure-Rust non-linear video editor targeting Adobe Premiere Pro parity (and beyond). Native on macOS, Windows, Linux; web via WASM. Sibling of `../photocraft` (Photoshop), `../printcraft` (Acrobat) and `../vectorcraft` (Illustrator; formerly DrawCraft), with the same conventions. ## Start every session here `plan/` is maintainer-local (gitignored, not in the repo). Each step names the public equivalent to use if you don't have it. diff --git a/README.md b/README.md index cba7e686..e6bf8a3c 100644 --- a/README.md +++ b/README.md @@ -220,7 +220,7 @@ Import merges the document's bins, media and sequences into your project as one ## Built for agents -Every menu item, button, slider and drag in FilmCraft is a **command** with an id, typed parameters and an enabled state. There are about 135 engine commands so far, with the rest of Premiere's catalogue on the way. The UI, the CLI, a JSON control channel and an **MCP server** all dispatch the same commands, so Claude or any agent can cut, trim, grade, mix and export exactly the way a person does. The UI can also be driven at the level of mouse and keyboard: every widget has an automation id, and agents can click, drag, type and take screenshots. +Every menu item, button, slider and drag in FilmCraft is a **command** with an id, typed parameters and an enabled state. There are more than 650 engine commands (`filmcraft-cli commands` lists them), with the rest of Premiere's catalogue on the way. The UI, the CLI, a JSON control channel and an **MCP server** all dispatch the same commands, so Claude or any agent can cut, trim, grade, mix and export exactly the way a person does. The UI can also be driven at the level of mouse and keyboard: every widget has an automation id, and agents can click, drag, type and take screenshots. ```jsonc // over the control channel (JSON lines on TCP) or as MCP tool calls @@ -238,7 +238,7 @@ The trailer and the grades in these screenshots were built exactly this way, by ## Everywhere - **Native** on macOS, Windows and Linux, with a native macOS menu bar. -- **The web:** every crate up to the engine compiles to `wasm32`; the browser front end is next. +- **The web:** the same engine and UI run in the browser via WebAssembly (`apps/filmcraft-web`, see [docs/web.md](docs/web.md)); every release ships it as `filmcraft-web-.zip`. - **Swappable UI.** The interface is one crate (`ui-egui`) over the engine, so a different front end can replace it without touching editing logic.
diff --git a/apps/filmcraft-cli/src/main.rs b/apps/filmcraft-cli/src/main.rs index 239c8907..4753551a 100644 --- a/apps/filmcraft-cli/src/main.rs +++ b/apps/filmcraft-cli/src/main.rs @@ -27,12 +27,16 @@ SUBCOMMANDS inspect [project|sequence] project tree or active sequence as JSON (default: both) import ... import media into the project export [--preset name] [--format f] [--range r] [--start s --end s] [--settings json] + [--scale f] [--quality 0-100] [--no-audio] [--queue] export the active sequence and wait for it to finish: with an export preset (`export --list-presets`; built-in or the user's), or - a format (h264|prores|dnxhr|mjpeg|mxf-op1a|mxf-opatom|png|tiff|bmp|gif|wav|aiff, guessed + a format (h264|prores|dnxhr|apv|mjpeg|mxf-op1a|mxf-opatom|png|tiff|bmp|gif|wav|aiff, guessed from the extension); --range entire|inOut|workArea, or a custom range in seconds; --settings is ExportSettings JSON merged over the - preset; --queue adds to the export queue and runs it instead + preset; --scale renders at a fraction of the frame size (0.5 = + half); --quality 0-100 for the formats that take one; --no-audio + leaves the sound out; --queue adds to the export queue and runs it + instead export --list-presets [query] list export presets (name, category, format) as JSON render --seconds S --out f.png [--scale 0.5] render one Program frame to PNG probe [--image-sequence] diff --git a/docs/releasing.md b/docs/releasing.md new file mode 100644 index 00000000..d449638b --- /dev/null +++ b/docs/releasing.md @@ -0,0 +1,134 @@ +# Releasing FilmCraft + +Every push to the `release` branch runs `.github/workflows/release.yml`. The workflow builds +installers for macOS, Windows and Linux, plus the web build, and creates or updates a **draft** +GitHub Release named `FilmCraft v`. Nobody sees a draft until a maintainer publishes it. + +User-facing names say **FilmCraft**. Files, binaries and ids stay lowercase +(`filmcraft---.`, `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 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) | `filmcraft--macos-universal.dmg`, `filmcraft-cli--macos-universal.zip` | `macos-15` | +| Windows x64 | `filmcraft--windows-x64.msi`, `filmcraft--windows-x64-portable.zip` | `windows-latest` | +| Windows x86 (32-bit) | `filmcraft--windows-x86.msi`, `filmcraft--windows-x86-portable.zip` | `windows-latest` | +| Windows on ARM64 | `filmcraft--windows-arm64.msi`, `filmcraft--windows-arm64-portable.zip` | `windows-latest` (cross-compiled) | +| Linux x86_64 | `filmcraft--linux-x86_64.{AppImage,deb,rpm,tar.gz}` | `ubuntu-22.04` | +| Linux aarch64 | `filmcraft--linux-aarch64.{AppImage,deb,rpm,tar.gz}` | `ubuntu-22.04-arm` | +| Web | `filmcraft-web-.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. + +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 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`. + +`packaging/linux/flatpak/ai.storyteller.filmcraft.yml` is a Flatpak manifest (its header says how +to build it). The release doesn't build a Flatpak; the workflows only check the manifest's id. + +### 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` 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 | +| `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 |