release: publish the library crates to crates.io with versions decoupled from the app #160
Labels
No labels
Area/ci
Area/cli
Area/client
Area/client-core
Area/daemon
Area/deps
Area/docs
Area/grid
Area/manual
Area/nix
Area/protocol
Area/pty
Area/render
Area/shaping
Area/skills
Area/transport
Area/vt
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Performance
Kind/Security
Kind/Testing
perf-regression-accepted
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Blocked
Status
Need More Info
Status
Upstream
No milestone
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
felis-terminal/felis#160
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why now
The library crates have outside consumers: spoor uses felis-vt / felis-grid through a git tag, and adroit, a desktop app, wants to embed felis-vt / felis-grid in-process in place of alacritty_terminal (GitHub mirror: https://github.com/felis-terminal/felis/issues/1, https://github.com/felis-terminal/felis/issues/2, https://github.com/felis-terminal/felis/issues/3, https://github.com/felis-terminal/felis/issues/4). crates.io rejects git dependencies, so a consumer that publishes itself cannot depend on felis until felis publishes. The Rust APIs are still unstable, and the release has to say so rather than wait for them to settle.
Policy
[workspace.package] version. Lockstep would force an app minor release for every library API break, and bump libraries that did not change.cargo semver-checksagainst the last published version decides patch vs minor. One release cadence, independent numbers.PROTOCOL_MINORand the minor ledger, never the crate version. felis-protocol needs this most: its wire is stable while its Rust API is not.pub(crate)or#[doc(hidden)]; what embedders use gets rustdoc. spoor's and adroit's usage is the floor.Work
versionandpublishin the published crates'Cargo.toml; workspacepublish = falsestays for the rest. Intra-workspace dependencies carryversionalongsidepath.cargo publish --dry-runandcargo semver-checksfor the published set.release-mirror.yml(it supports GitHub Actions; Forgejo Actions is assumed unsupported, to verify), else a scoped token in the Forgejo release workflow.docs/reference/workspace.md("none is published", crate map, a reuse path for in-process embedders),docs/explanation/implementation.md"Why the crate version carries no wire information" plus a section on the decoupled versioning and its rejected lockstep alternative,docs/reference/testing.md"CI shape",CONTRIBUTING.mdrelease steps,CHANGELOG.md.Timing
First publish with the release that lands the embedder splits (expected v0.3.0). Until then consumers pin the GitHub tag.
Two updates from the renderer split (#170–#173):
felis-render-core(palette, box-drawing rasterizer, glyph index, cell painter and its display list; depends on felis-protocol, felis-grid, felis-shaping). By this issue's policy it belongs with felis-shaping and felis-render-wgpu: unpublished until an outside consumer needs it. Publishing it later pulls felis-shaping into the published set. The crates.io name is free as of today.crates/felis-protocol/tests/crate_purity.rsandcrates/felis-grid/tests/no_render_deps.rsshell out tocargo treeover the workspace; once those crates are published, the tests ship in the package and fail outside the workspace. They move intodeny.toml[bans](wrappersentries, as #171 did forwgpu/winit) in a separate PR before the first publish.Policy amendment (2026-10-19)
This overrides "Published set" in the body and the
felis-render-corenote in the first comment.crates/, the binaries included (felis-cli, felis-client, felis-daemon); thetests/member (felis-workspace-tests) stays unpublished.cargo install felis-cli felis-client felis-daemonbecomes a way to get felis, with no support claim: no CI gate runs on it, and it lacks what the archives and Nix ship besides the binaries (terminfo, desktop entry, man pages, completions). felis-cli looks for felis-client next to its own executable first, so acargo installof all three finds it.felisis held by an unrelated crate, so the front door isfelis-cli.[workspace.package] version), the one the release tag andfelis versionname; felis-daemon's library API, which embedders use, breaks with an app minor.tools/release/verify.py(and its self-test) requires only the three binary crates to inherit the workspace version; today it refuses any crate that does not.cargo publish --workspace --dry-run, which packages and builds the members in that order against each other rather than against crates.io, so it passes before the first publish.cargo semver-checkscovers the library crates and felis-daemon once each has a published baseline.felisand its ownCARGO_PKG_VERSION, and DA3 with the hardcoded unit IDfelis-1(crates/felis-grid/src/sink.rs). The identity is the host's, not the grid's: under independent versions a 0.3.0 app would replyfelis 0.1.0, and an embedder would answer as felis. The grid keeps answering both, in order with its other replies, from an identity the host supplies (the daemon:felis <app version>andfelis-1, the replies it sends today). With none set, XTVERSION stays silent and DA3 answers xterm's all-zero unit ID. The daemon sets it on grids restored by an in-place upgrade too, since a dump from 0.2.0 carries none; test harnesses that pin the replies (the protocol coverage matrix, the vttest smoke) set it explicitly.version, socargo packagedrops them. The body's "intra-workspace dependencies carryversion" applies to normal dependencies only. felis-client-core and felis-daemon dev-depend on each other; withversionon those entries neither can be packaged before the other is on crates.io, andcargo publish --workspace --dry-runfails on the cycle. The tests that need such a dev-dependency do not build from the extracted package, like the next item's.docs/reference/ipc.md) fails in an extracted package; it moves to thetests/member or out of the package.cargo installpath todocs/reference/workspace.mdas unsupported.Shipped in v0.3.0: every crate is on crates.io, with trusted publishing from the GitHub mirror.