From 244367fbafa13c08c69deacd729fec669f6487d1 Mon Sep 17 00:00:00 2001 From: skylartaylor Date: Tue, 13 Oct 2026 01:24:04 +0200 Subject: [PATCH] Prepare the first coordinated craft-libs release --- .github/workflows/workspace.yml | 75 ++++++++++++++++++++++++++++++ CHANGELOG.md | 65 ++++++++++++++++++++++++++ README.md | 33 +++++++++++++- docs/versioning.md | 81 +++++++++++++++++++++++++++++++++ 4 files changed, 252 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/workspace.yml create mode 100644 CHANGELOG.md create mode 100644 docs/versioning.md diff --git a/.github/workflows/workspace.yml b/.github/workflows/workspace.yml new file mode 100644 index 000000000..4002900d9 --- /dev/null +++ b/.github/workflows/workspace.yml @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..31364ecf9 --- /dev/null +++ b/CHANGELOG.md @@ -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. diff --git a/README.md b/README.md index 1be832cbe..9387364fb 100644 --- a/README.md +++ b/README.md @@ -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- = { git = "https://github.com/storytold/craft-libs", rev = "" } +[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 = ""` 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. diff --git a/docs/versioning.md b/docs/versioning.md new file mode 100644 index 000000000..58c22cbf3 --- /dev/null +++ b/docs/versioning.md @@ -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.