Prepare the first coordinated craft-libs release

This commit is contained in:
skylartaylor
2026-10-13 01:24:04 +02:00
parent f03ec27ab2
commit 244367fbaf
4 changed files with 252 additions and 2 deletions
+75
View File
@@ -0,0 +1,75 @@
name: Workspace
on:
push:
branches: [main]
tags: ["v*"]
pull_request:
workflow_dispatch:
permissions:
contents: read
jobs:
native:
name: Workspace (${{ matrix.os }})
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Install software Vulkan renderer
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y mesa-vulkan-drivers libvulkan1
echo 'WGPU_BACKEND=vulkan' >> "$GITHUB_ENV"
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
with:
key: workspace
- name: Prepare the Windows test driver
if: runner.os == 'Windows'
shell: powershell
run: |
$ErrorActionPreference = 'Stop'
Start-Service Spooler
Add-PrinterDriver -Name 'Generic / Text Only'
- run: cargo fmt --check
- run: cargo clippy --workspace --all-targets --locked -- -D warnings
- run: cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
- run: cargo test --workspace --locked
env:
CRAFT_PRINT_REQUIRE_QUEUE: ${{ runner.os == 'Windows' && '1' || '0' }}
- run: cargo test --workspace --all-features --locked
env:
CRAFT_PRINT_REQUIRE_QUEUE: ${{ runner.os == 'Windows' && '1' || '0' }}
msrv:
name: Rust 1.95 (Linux libraries)
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@1.95.0
- uses: Swatinem/rust-cache@v2
with:
key: workspace-msrv
- run: cargo check --workspace --lib --all-features --locked
wasm:
name: UI and settings WASM
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v2
with:
key: workspace-wasm
- run: cargo check -p craft-ui -p craft-settings --all-features --locked --target wasm32-unknown-unknown
+65
View File
@@ -0,0 +1,65 @@
# Changelog
All crates share the workspace version. See [the versioning policy](docs/versioning.md).
## v0.1.0 — 2026-10-12
First coordinated Git release of craft-libs. Applications can pin a named baseline instead of
following development commits. This is an initial 0.x release: compatibility is maintained
within `0.1.x`; changes requiring consumer migration use a new minor version.
### Included crates
- **craft-atomic:** same-directory staged file replacement, exclusive creation, streaming
writes, transient rename retries and fault-injection support, extracted from PhotoCraft.
- **craft-settings:** app-owned TOML settings with platform/portable paths, atomic saves,
backups, damaged-file recovery, schema migrations and preservation of unknown fields.
- **craft-quit:** native macOS termination, optional egui close handling and an optional
application menu. Save decisions and prompts stay with the app.
- **craft-print:** CUPS and native Windows PDF submission, printer discovery, copies, duplex,
colour and scaling options. Includes bounded PDF sheet handling and the pinned Windows
completion fix. PDF generation stays with the app.
- **craft-ui:** egui icon buttons, tabs, splits, resize handles, toolbar overflow, transactional
docking, theme helpers and print-settings controls. Commands, content, localization and
application state stay with the consumer. Docking serialization is opt-in through `serde`.
### Requirements and adoption
- Rust **1.95+**, edition 2024. Public UI integrations use **egui 0.36**.
- Use `git = "https://github.com/storytold/craft-libs", tag = "v0.1.0"` for each required crate;
use the same tag everywhere. The [README](README.md#use-a-released-version) has a complete
manifest example and a commit-pin alternative. Commit application lockfiles.
- No automatic migration from an arbitrary earlier Git revision is implied. Compare the API
you used with this release and test affected workflows before changing pins.
- For `craft-quit`, run the close hook last in `eframe::App::logic`, after the app's unsaved-work
decision. Finish asynchronous saves before requesting close; termination does not execute
code after `run_native`. See the crate's API documentation before adoption.
- This release is distributed through Git only. No crates.io packages are published by it.
### Platform coverage and known limits
The release gate checks the workspace on Linux, macOS and Windows with default and all features,
plus Linux minimum-compiler and UI/settings WASM checks. UI tests include offscreen visual
regressions. Windows print tests exercise a temporary paused spooler queue; those checks do
not establish physical printer fidelity or every application's native GUI behavior.
- `craft-ui` supports native and WASM use. Font coverage, bidirectional text and screen-reader
behavior still depend on the application and upstream stack; see [#17](https://github.com/storytold/craft-libs/issues/17).
- `craft-settings` persistence is native-only. WASM exposes settings traits, document helpers
and error types. Concurrent writers are best-effort, last-writer-wins; strict coordination
needs an application-owned lock.
- `craft-atomic` does not preserve all filesystem metadata during replacement. Directory sync
is best effort; tests do not prove physical power-loss durability. See its README's contract.
- `craft-quit`'s native behavior is macOS-specific; other platforms keep their normal close
path. Earlier hardware testing of the close hook is recorded in
[#3](https://github.com/storytold/craft-libs/issues/3); it does not qualify every consuming app.
- `craft-print` requires CUPS command-line tools on Linux/macOS or the native Windows spooler.
Browser submission is unsupported. Windows accepts a bounded subset of print-ready PDFs;
unsupported inputs and driver paper/scaling mismatches return errors. Printing is synchronous,
so call it from a worker. Physical output needs hardware testing.
- Windows printing retains a pinned Git dependency on the maintained `winprint` fork. Its
build dependencies require the license/advisory policy exceptions documented in
[craft-print's README](crates/print/README.md). Registry publication is deferred.
Planned crates and open feature PRs are not part of this release. Existing commit pins remain
valid, and consumers do not need to update simply because `main` changes.
+31 -2
View File
@@ -15,12 +15,41 @@ A Cargo workspace (edition 2024, resolver 3). Each library is a crate under `cra
(`license.workspace = true`, and so on) and `[lints] workspace = true`. Register each crate in
`[workspace.dependencies]` in the root `Cargo.toml`.
Apps depend on a crate by git:
## Use a released version
The first coordinated release is **[v0.1.0](https://github.com/storytold/craft-libs/releases/tag/v0.1.0)**.
It gives consumers a fixed baseline while development continues on `main`. All five crates
share this version. They require Rust 1.95 or later; the UI integrations use egui 0.36.
Add only the crates you need, using the same tag for every craft-libs dependency:
```toml
craft-<name> = { git = "https://github.com/storytold/craft-libs", rev = "<commit>" }
[dependencies]
craft-ui = { git = "https://github.com/storytold/craft-libs", tag = "v0.1.0" }
craft-settings = { git = "https://github.com/storytold/craft-libs", tag = "v0.1.0" }
# Optional, for egui close handling and the macOS application menu:
craft-quit = { git = "https://github.com/storytold/craft-libs", tag = "v0.1.0", features = ["menu"] }
```
`craft-atomic` and `craft-print` use the same dependency syntax. Enable `craft-ui`'s
`serde` feature if you need to serialize its docking layout. These are Git releases;
crates.io publication is deferred.
Release tags are never moved or reused. For a commit pin, resolve the tag with
`git ls-remote https://github.com/storytold/craft-libs 'refs/tags/v0.1.0^{}'` and replace
`tag = "v0.1.0"` with `rev = "<full commit SHA>"` on every craft-libs dependency.
Use either `tag` or `rev`, not both. An existing commit pin keeps working; switching to this
release may require adapting to changes since that commit.
Commit your application's `Cargo.lock` and build with `cargo build --locked` for reproducible
Rust dependency resolution. A Git pin fixes craft-libs source, not every transitive dependency.
A library's own lockfile does not constrain its downstream consumers. Upgrade deliberately:
read the release notes, update all craft-libs pins together, refresh the lockfile with
`cargo check`, and run your application's tests and affected workflows before committing.
See the [release notes](CHANGELOG.md) for included functionality and known limits, and the
[versioning policy](docs/versioning.md) for compatibility guarantees and release checks.
## Libraries
- [`craft-atomic`](crates/atomic/README.md): Crash-safe atomic file writing and replacement with same-directory staging, directory fsync, Apple `F_FULLFSYNC` fallback, transient lock backoff, and fault injection for the Craft suite.
+81
View File
@@ -0,0 +1,81 @@
# Versioning and releases
craft-libs releases give applications a fixed dependency baseline. Consumers decide when to
upgrade; development on `main` is not a reason to refresh a working application's pins.
## One workspace, one release
All crates inherit `workspace.package.version` and ship together under an annotated `vX.Y.Z`
Git tag. A release contains the crates listed in that tag's README, not proposed crates in
open pull requests. A crate can keep the same implementation across several workspace releases.
Use the same tag or commit for every craft-libs dependency in a consumer's dependency graph;
mixing Git sources can create duplicate crate instances and incompatible Rust types.
Tags identify reviewed commits and are never moved, deleted or reused to fix a release.
Corrections get a new version. The release notes and policy are stored with the source so a
consumer can read the contract that applied to its version. GitHub Releases carry the same notes.
Publishing to crates.io will be a separate step; these tags do not imply registry availability.
## Compatibility while versions are 0.x
The initial release is `0.1.0`. APIs are still developing, but compatibility within a minor
release line is intentional:
- **Patch (`0.1.0` → `0.1.1`):** compatible fixes, documentation and additive APIs that preserve
existing callers. Keep public signatures, feature defaults, supported targets, minimum Rust
version and documented behavior compatible. Check that additions do not break exhaustive
matches, struct literals, trait implementations or other existing source patterns.
- **Minor (`0.1.x` → `0.2.0`):** changes requiring consumer action. This includes removing or
changing public APIs, incompatible persisted representations, feature/default changes,
dropping a supported target, raising the minimum Rust version, or upgrading a dependency
such as egui when its exposed types become incompatible. Explain the before/after and migration.
- **`1.0.0`:** a separate decision that the public contracts are ready for long-term stability.
After that, breaking changes require a major release; compatible additions use minor releases.
Documented invalid input can be rejected more strictly in a patch to fix a defect or security
issue. Call out any observable change. Do not use “0.x” or “bug fix” to silently break a valid,
documented workflow. App-owned settings schemas and migration policy remain the app's
responsibility; library releases do not automatically migrate consumer data.
For public egui APIs in `0.1.x`, keep the egui 0.36 compatibility line. The minimum Rust version
is 1.95. Compatible transitive updates still need validation and release-note coverage when
observable. Cargo's version-range rules do not select newer Git tags: changing a release pin
is always an explicit manifest edit.
## Support and scope
The newest minor line receives routine fixes. Important fixes to older lines are considered
case by case; there is no promise of maintaining every 0.x line or an LTS schedule. Existing
tags remain available so consumers can upgrade on their own schedule. Release notes identify
known limitations; a tag is not a claim that every driver, OS version or consuming app was tested.
Platform boundaries are crate-specific. See each crate's API/README and the release notes.
In particular, native printing needs the OS spooler, settings persistence is native-only, and
browser UI compatibility does not imply native services work in a browser. Consumers must test
their own save/close flows, accessibility, rendering and printing where they use those services.
## Preparing a release
1. Choose a reviewed commit on `main`. Inspect open defects, changes since the previous tag,
crate metadata and any changed platform/dependency contracts. Update the shared version
and lockfile when needed; the first release already uses `0.1.0`.
2. Add a dated `CHANGELOG.md` entry with the included crates, changes, compatibility requirements,
migration steps and known limits. Update the README's recommended pin. Keep fixes and
unrelated pending features out of the release preparation unless they block the baseline.
3. Run formatting, workspace Clippy with warnings denied, and workspace tests, both default
and all features. The `Workspace` workflow runs these on Linux, macOS and Windows, requires
Windows spooler regression tests, checks the minimum compiler on Linux, and checks the
documented UI/settings WASM surface. All release jobs must pass for the candidate source.
Repository-local build commands must also follow the host's storage/allocation rules.
4. Review consumer impact and existing platform evidence. Exercise affected native workflows
when behavior changes; unit tests and cross-compilation cannot establish GUI or physical
printer acceptance. Record limitations instead of extending the support claim beyond evidence.
5. Merge the preparation PR and verify the checks for the actual release commit. Create an
annotated tag on that exact commit (`git tag -a vX.Y.Z FULL_SHA -m "craft-libs vX.Y.Z"`),
push only that tag, and create a GitHub Release using that version's changelog entry.
Tag-triggered CI should also pass. Do not publish crates as part of this Git-only release.
6. Verify that the remote tag resolves to the intended commit, the release notes are visible,
and a small external consumer resolves and checks with the documented pin. Preserve the
source identity, check results and release URL. Never repair a published tag by moving it.
The workflow validates source; it does not automatically tag or publish on every push.