Releases: abdulwahed-sweden/rustio-admin
v0.28.0 — One declaration per model
All four crates ship 0.28.0 (rustio-admin, rustio-admin-macros, rustio-admin-cli, rio-theme).
rustio-admin = "0.28.0"Changed
#[derive(RustioAdmin)]now generatesimpl Modeltoo. The derive already walked every field to buildAdminModel; it now emits theorm::Modelimpl (TABLE,COLUMNS,INSERT_COLUMNS,id,from_row,insert_values) from the same walk. Models are declared once — the hand-written ORM glue that had to be kept in sync with the struct by hand is gone. Two struct-level escape hatches cover what the field walk can't infer:#[rustio(table = "…")]when the SQL table name differs from the auto slug (e.g.Address→addresses), and#[rustio(extra_columns = ["…"])]for generated/virtual columns that aren't struct fields (e.g. a Postgrestsvector).- Runtime dependencies are re-exported from
rustio_admin.chrono,rust_decimal,uuid, andsqlx(plus theDecimal/DateTime/Utc/NaiveDate/NaiveTime/Uuidtype aliases) are now re-exported. The derive's generated code routes through these paths, so a model crate can depend onrustio-adminalone; the scaffold'sCargo.tomldrops its directchrono/uuid/rust_decimal/sqlxdependencies. One resolved copy of each, no version skew.
Internal
- Two drift-guard tests with no machine check before: one asserts the
admin.css@importmanifest matches theADMIN_CSSconcat!(include_str!(…))bundle inroutes.rs; one assertsrustio_admin_macros::HUMANISE_ACRONYMSis byte-identical toadmin::render::HUMANISE_ACRONYMS.
⚠️ Upgrading from 0.27.x (breaking)
- Delete any hand-written
impl Modelblocks for types that deriveRustioAdmin— they now collide with the generated impl. If a model needs a non-default table name or virtual columns, use the new#[rustio(table = "…")]/#[rustio(extra_columns = ["…"])]attributes instead. - You can drop direct
chrono/uuid/rust_decimal/sqlxdependencies from a model crate'sCargo.tomland route throughrustio_admin's re-exports.
Full changelog: https://github.com/abdulwahed-sweden/rustio-admin/blob/v0.28.0/CHANGELOG.md
v0.27.6 — CSV-import guidance + admin redesign
All four crates ship 0.27.6 (rustio-admin, rustio-admin-macros, rustio-admin-cli, rio-theme).
rustio-admin = "0.27.6"Fixed
- CSV import no longer dead-ends on an unrecognised column. A header with columns the model doesn't declare used to return a bare
400 Bad request, even though the importer was already built to skip them. The handler now matches that design: unknown columns are skipped, reported on the result page, and turned into a ready-to-runALTER TABLEmigration plus the matchingstruct/from_row/insert_valuessnippet — with column types inferred from the column names. Removes the now-unusedParseError::UnknownColumnsvariant.
Changed
- Admin pages restyled. The framework-docs index, the markdown doc viewer, the API surface, the account-sessions page, the audit log, and the CSV-import result page were redesigned with the shared card / badge / glyph component language (page heads, colored status/method badges, avatar cells, typeset documentation prose). Token-only; secondary accents read
var(--rio-accent2, var(--rio-accent))so a project that defines an amber secondary picks it up while the framework default falls back to its accent.
Documentation
getting-started.mdrewritten example-first;modeladmin.mdgains a worked example;cli.mdnewdescription corrected.
Internal
examples/reorganised into two admin projects: the canonical multi-crate reference renamedclinic-admin→clinic, the unused axumclinicdemo dropped, and a new single-crateshop(e-commerce admin) added.
Built with this release
- shop
v0.1.0— a standalone e-commerce admin example released on this version: nine related models with inline relations, a navy + amber theme, redesigned admin pages, and seeded demo data. See its release notes.
Full changelog: https://github.com/abdulwahed-sweden/rustio-admin/blob/v0.27.6/CHANGELOG.md
v0.22.0 — CLI binary renamed `rustio` → `rustio-admin`
The rustio-admin-cli crate now produces a binary named rustio-admin instead of rustio. crates.io package names are unchanged; only the emitted executable changes.
Why
The bare rustio name collided at the shell level with an earlier unrelated upstream RustIO project that also publishes a rustio binary. Back-to-back cargo install runs on the same machine silently overwrote each other with no diagnostic. Renaming the bin to match the crate (rustio-admin-cli → rustio-admin) clears the ambiguity.
What changed
[[bin]] nameincrates/rustio-admin-cli/Cargo.toml, the clapname, theWELCOME_HELPbanner shown by--help.- Every user-facing
rustio <verb>example across README, ROADMAP, CLAUDE.md, active design docs, the bundled scaffold templates (newly-scaffolded projects' READMEs andmain.rscomments referencerustio-admin), library doc comments, admin-template empty-state CTAs (/admin,/admin/health,/admin/db-browser),examples/.env, the CI scaffold-pin-guard comment, and CLI error / hint strings.
What is deliberately NOT renamed
These are on-disk / runtime contracts with already-deployed projects. Renaming them would silently break running deployments or diverge newly-generated files from previously-generated ones for any project that scaffolds across the version bump.
- The
@generated by rustio <ver>codegen sentinel inbuilder/codegen.rs(the SchemaHash header in_generated/files). - The
.rustio/state directory (draft.toml,history.jsonl,builder.lock). - The
// rustio:insertion markers in scaffoldedsrc/main.rsfiles. - The
rustio_admin_actionsaudit table name. - The
RUSTIO_TEMPLATE_DIR/RUSTIO_SECRET_KEYenvironment variables.
Migration
After upgrading, cargo install rustio-admin-cli lands ~/.cargo/bin/rustio-admin. The previous ~/.cargo/bin/rustio from older installs is left as-is — cargo uninstall rustio-admin-cli from an earlier install removes it, or delete it by hand.
Shell scripts, CI steps, Dockerfiles, and project docs that invoke the binary directly need a one-token find-and-replace:
```sh
before
rustio startproject myapp
rustio migrate apply
after
rustio-admin startproject myapp
rustio-admin migrate apply
```
No library API change; the rustio-admin crate is byte-identical except for the version bump.
Full notes: CHANGELOG.md
v0.21.1 — Admin list-page redesign
[0.21.1] — 2026-05-26
Admin list-page redesign. Pure CSS + template + render-context
change; no Rust public-API impact, no schema, no auth or permission
behaviour shift.
Toolbar split
The pre-0.21 "View ▾" overflow dropdown collapsed Sort, Per-page,
CSV export, and CSV import into one mega-menu — operators had to
scan a 17-row list (8 fields × 2 directions + Default + 4 per-page
- Export + Import) to find any single operation. 0.21.1 splits the
toolbar into two zones with distinct intent:
- Row 1 "Find" — search input (flex:1, ≥360px), up to two
promoted filter chips, a "More filters" overflow for the rest, a
Reset link that surfaces only when there's state to reset
(search OR ≥1 active filter). - Row 2 "Arrange" — Sort field menu, direction toggle, Rows
per page, Saved view, then a flex spacer, then Export / Import
pinned to the trailing edge. Every control carries icon + label.
Find panel surface
.rlp-find sits inside an accent-tinted card (--rio-accent-soft
fill, --rio-accent-border, framework radius). No heading or icon
above the panel — the surface itself is the cue. Arrange stays
transparent so the visual weight stays on Find.
Dark table header
.rio-list-page .rio-table th re-skins to --rio-surface-chrome
(the same slate-900 the sidebar uses). The thead scope locally
redefines --rio-text-strong / --rio-text / --rio-text-muted
/ --rio-text-subtle to slate-50…500 and lifts --rio-accent to
teal-400 — same chrome-cascade trick layout/shell.css already
uses on the sidebar, so hover and active-sort copy stay legible
without per-selector overrides. The header bar reads as one
continuous frame with the sidebar; data rows now contrast hard
against the chrome instead of fading into a neutral.
Sort menu — field-once + direction toggle
SortFieldCtx (one entry per sortable field) replaces the
field×direction menu pattern. Clicking a field row activates
ascending sort; a sibling direction toggle flips to descending and
reads field-type-aware copy:
| Field type | Asc label | Desc label |
|---|---|---|
DateTime |
oldest first | newest first |
String / FilePath |
A → Z | Z → A |
I32 / I64 |
lowest first | highest first |
Bool |
off → on | on → off |
Numeric copy (lowest first / highest first) is new in 0.21.1;
0.21.0 fell back to the generic ascending / descending strip.
The unit test was updated to match the new copy.
Empty state
The pre-0.21 <p class="rio-empty">No <model> yet</p> floated as a
single sentence inside a large blank card. 0.21.1 ships a centered
empty-state block: a tinted-disc inbox icon, an h2 title
("No appointments yet"), a 16px body sentence ("Add the first
appointment to get started."), and the primary Add CTA. Copy
substitutes the model's plural / singular display name so the
same block reads correctly for every model with no project-side
override.
Bottom bar
Row count + pagination move out of the toolbar into a footer-style
bar below the table card. Showing N of M <model-plural> plus
pagination nav; hidden in the empty state because zero-of-zero is
noise.
Responsive
.rio-list-page page padding cascades: 32px desktop ≥1280px →
24px tablet 768–1279px → 16px mobile <768px, driven by a
.rio-main:has(> .rio-list-page) selector so non-list pages keep
their existing padding. The toolbar's flex-wrap lets Row 1 and
Row 2 reflow vertically below 768px without code; the table card
remains the only horizontal-scroll surface.
Render-context additions
ListCtx gains:
sort_fields: Vec<SortFieldCtx>— one entry per sortable field.current_sort_field_label: String— Sort chip caption.current_sort_dir_label: &'static str— direction toggle text.sort_dir_toggle_link: String— flips the active sort's direction.default_sort_link: String— Sort menu's reset row URL.
The legacy sort_options / current_sort_label fields are kept
for back-compat with project templates that haven't migrated to
the new layout.
Assets
- New stylesheet
assets/static/admin/pages/list.css, scoped
under.rio-list-page. Wired into both theadmin.css@import
manifest and theADMIN_CSSconcat block inroutes.rs;
tests/cascade_lockstep.rsverifies the two lists stay aligned. - New icons:
inbox(empty-state disc),rotate-ccw(Reset chip),
arrow-up-down(direction toggle decoration). All inline lucide
stroke paths, no external library.
Known issue (carried forward, not introduced by 0.21.1)
A foreign-key filter declared in ModelAdmin::list_filter() is
silently dropped when the RelationRegistry doesn't carry the
relation — infer_filters_with_registry falls through to
FilterKind::NumericExact, then the list handler hits a _ => {}
catch-all with no rendered widget. Pre-existing in 0.21.0; the
new toolbar makes the gap more visible (the "More filters"
dropdown is the natural place to surface secondary filters and
will obviously be empty when only the FK was configured). Fix
requires real widget design for NumericExact / RelationSelect
and belongs in a later phase, not a UI patch release.
Migration
Existing 0.21.0 projects update by bumping
rustio-admin = "0.21.0" to "0.21.1" in their Cargo.toml and
running cargo update -p rustio-admin. No schema migration. No
template override required — the new layout ships as the embedded
default. Projects that override templates/admin/list.html
locally will continue to use their override; remove it to pick up
the new layout.
v0.21.0 — Stage 2 (startapp --field, permission defaults, docs discoverability)
Stage 2 of the FTUX redesign. Four PRs bundle into one release: rustio startapp --field, structural permission defaults, a startapp pluralization + spatial-orientation fix, and rustio docs becoming a useful command.
No API breaks. Existing 0.20.x projects update by bumping rustio-admin = "0.20.0" to "0.21.0" in their Cargo.toml. Schema unchanged.
What ships
rustio startapp <name> --field <name>:<type> (PR 2.1)
Closed 8-token vocabulary — str / text / int / bigint / bool / timestamp / json / fk:<Model>. Generates a real model file with the declared fields, a matching CREATE TABLE migration, and a ModelAdmin impl whose list_display / search_fields defaults are derived from the declaration. Interactive prompt loop at TTY when no flags are passed. The "paste these lines into src/main.rs" handshake from prior releases is preserved — startapp deliberately does not edit your main.rs.
timestamp produces TIMESTAMPTZ NOT NULL (no implicit DEFAULT NOW()). json produces JSONB NOT NULL (no implicit DEFAULT '{}'). Either default would imply created_at / settings semantics that don't fit every use.
Structural permission defaults (PR 2.2)
Three groups seeded on fresh databases:
| Group | Permission scope | Audience |
|---|---|---|
administrator |
Full system access | Owner, operator |
editor |
add / change / view on content models. No delete. No user / group / permission management. |
Content team |
viewer |
view only |
Auditors, read-only stakeholders |
rustio user create --role accepts two new values: editor and viewer (both map to Role::User internally; authority comes from group membership). Group names lock to --role values via a CI-enforced lockstep test. The seed is guarded — projects with user-defined groups pick up zero changes on upgrade.
New doctrine doc: docs/design/DESIGN_PERMISSIONS.md.
Startapp pluralization + spatial orientation
The naive +s pluralizer that produced classs / categorys / buss was replaced with proper English rules (classes / categories / buses). Applied to both the migration filename and the admin URL.
Three new insertion markers in the scaffolded main.rs:
// rustio: modules
// rustio: imports
// rustio: modelsrustio startapp output redesigned to orient the developer spatially against the markers:
MODEL CREATED Task (2 fields)
✓ src/task.rs
✓ migrations/0001_create_tasks.sql
3 edits in src/main.rs
under // rustio: modules add mod task;
under // rustio: imports add use task::Task;
under // rustio: models add .model::<Task>()
$ rustio migrate apply
$ cargo run
admin http://127.0.0.1:8000/admin/tasks
Operational values (paths, commands, URLs) styled via console::style — paths cyan, commands green, URL cyan-underlined, markers dim. NO_COLOR / non-TTY environments strip ANSI cleanly. Missing-marker warning fires when any of the three is absent (older projects).
rustio docs becomes useful (PR 2.4)
Probes /admin/health with a 500ms timeout. The printed output reflects whether the local docs are actually reachable right now:
RustIO docs are running on this project.
local http://127.0.0.1:8000/admin/docs
online https://docs.rs/rustio-admin
(pass --open to open the local docs in your browser)
New --open flag launches the local docs in your default browser (open / xdg-open / cmd /c start). Exits 1 if the server is unreachable. Health probe uses raw TcpStream (no HTTP-client dep) and requires the framework's x-correlation-id response header so a foreign HTTP service bound to port 8000 doesn't fool it.
Homepage gains one muted line — sign in required — under the three nav pills to explain why clicking them redirects to login when not signed in.
What was deliberately cut
PR 2.3 (doctor onboarding mode + .rustio/onboarding.json state machine) was cut per the Stage 1 Reality Audit. The audit found that rustio doctor already reads clean, and a state file would introduce hidden framework state to solve a problem nobody has.
Upgrade
# Cargo.toml
[dependencies]
rustio-admin = "0.21.0"cargo update -p rustio-adminOptional but recommended: the three new insertion markers (// rustio: modules / imports / models) make rustio startapp output much more useful. Add them to your existing main.rs once and the spatial-orientation output gates against them automatically.
Full per-PR detail in CHANGELOG.md.
v0.20.0 — Stage 1 FTUX + Polish & Trust
Headline release of Stage 1. The first-time-developer experience overhaul, plus the Polish & Trust runtime fixes from the Stage 1 Reality Audit, shipped together so external users see a coherent new experience from cargo install rustio-admin-cli.
No API breaks. Existing 0.19.0 projects update by bumping rustio-admin = "0.19.0" to "0.20.0" in their Cargo.toml. Schema unchanged.
First-time developer experience
rustio new <name>— friendly alias forstartproject. Identical files, same exit codes. The Builder bootstrap that used to live in this slot moved torustio builder new.- Interactive TTY wizard — calm three-prompt flow: project name → project type → database name. Auto-generates
.envwith a working localDATABASE_URL. Bypassed under--no-interactive, non-TTY stdin/stdout, orCI=1. - Welcoming
rustio --help— beginner-surface welcome banner above the full command list; full surface preserved untouched below. - Four-part humanised onboarding errors —
Problem / Why / Fix / Retryshape with the verbatim sqlx text preserved in an indentedDetails:block. Covers the five onboarding-critical failures: DATABASE_URL missing, PostgreSQL unreachable, database missing, migration SQL failure, invalid--role. - Calm progress spinner — ASCII
| / - \on stderr during long ops (Db::connect,migrate apply,doctorconnect). One sober ✓ / ✗ marker on completion. Respects--quiet,--no-progress,CI,NO_COLOR, non-TTY. Feedback, not theatre. - First-boot homepage at
/— calm 130-line inline-CSS landing page baked into the binary viainclude_str!. Three CSS custom properties at the top (--brand,--text,--muted) make re-skinning a 3-line edit. - Project-type-aware subtitle — wizard's project_type choice (custom / clinic / school / inventory / blog) drives a single
{{type_phrase}}line on the homepage. - Project name in admin chrome — scaffolded
main.rsnow callsAdmin::new().app_name(...). Login tab title, login h1, and dashboard tab title carry the project name instead of generic "Admin".
Operational trust (Polish & Trust)
- Postgres NOTICE chatter silenced.
Db::connect_withsetsclient_min_messages = warningon every pooled connection. First-boot log drops from 65 lines to 2. WARNING / ERROR / LOG bands unaffected. - Duplicate
listeninglog removed.Server::runno longer emits its ownrustio listening on …. Scaffolded projects already log their canonical URL with conflicting paths was an audit finding. - Admin empty-state CLI handshake. Dashboard's "No models registered yet" now leads with
rustio startapp <name>before the Rust line. - Scaffold README restructured into five layered sections (
1. First run→2. First model→3. Admin usage→4. Common commands→5. Advanced topics) matching the new wizard reality. Drops thecp .env.example .envandcargo install rustio-admin-clisteps the wizard already handles.
Project identity
- Default scaffold is neutral.
src/post.rsandmigrations/0001_create_posts.sqlmoved into theblogpreset. A clinic / school / inventory / custom project ships zero domain models —rustio startapp <name>adds the first when the developer decides what they're actually building.
Terminal compatibility
- Spinner switched from Braille to ASCII. Braille (U+2800-U+28FF) triggered font fallback in some mixed RTL/LTR locales.
- Em-dashes in CLI strings reduced to
--. Two ASCII hyphens — conventional em-dash substitute. Doc comments and documentation untouched.
Doctrine
- New:
docs/design/DESIGN_ONBOARDING.md— Stage 0 constitution governing the FTUX surface. Same shape asDESIGN_AUDIT.md/DESIGN_SESSIONS.md. PRs against the onboarding stack are reviewed against this doctrine.
Upgrade
# Cargo.toml
[dependencies]
rustio-admin = "0.20.0"cargo update -p rustio-adminRecommended (one-line opt-in to project ownership): chain .app_name("Your Project") onto your Admin::new() call.
Full per-PR detail in CHANGELOG.md.
v0.19.0 — visual overhaul (Quiet Expert)
A six-turn redesign of every admin UI surface under the Quiet Expert design language. Calm, dense, professional — Stripe Dashboard / Linear / Vercel discipline applied to every page.
No backend change. Every Rust handler, route, model, SQLx query, and migration is identical to 0.18.x. The diff is CSS / templates / JS / one new icon.
New design primitives
| Primitive | Role |
|---|---|
.rio-section |
Page-level content rhythm — eyebrow label + counted title + body |
.rio-empty-state |
Icon + headline + lead + CTA — replaces one-paragraph .rio-empty |
.rio-confirm (+ --danger / --neutral) |
Block for every destructive / mutating action — left-edge stripe + icon badge + lead + items list |
.rio-env-chip |
Env / version metadata next to a page title |
.rio-page-actions__group |
Secondary-button cluster next to a primary CTA on the page header |
Refined .rio-card |
Lighter shadow ambient + --quiet / --elevated opt-ins |
Unified across every page
- Page header pattern: breadcrumb · title · primary action · lead — every list / form / detail / settings / admin-tool page now uses one shape.
- Stat tiles: drop the cycling pastel background fills (festive) for one consistent white surface + 3-px accent rail (semantic). Color is reserved for SEMANTIC meaning now, not per-tile decoration.
- Empty states: every
.rio-emptyparagraph upgraded to a proper.rio-empty-stateblock. - Confirmation flow: every delete / bulk-confirm / lock / disable-MFA / password-change success uses the same
.rio-confirmblock. Cancel button moved to the left of the action row so the destructive primary doesn't sit next to the cursor's natural arrival point. - Section wrapping: list pages all use
.rio-sectionwith eyebrow + counted title (e.g. "All groups · 3 groups").
Per-page work
| Turn | Templates |
|---|---|
| 1 — Foundation + Dashboard | index.html, cards.css, dashboard.css |
| 2 — List pages | list.html, users_list, groups_list, log_entries, notifications |
| 3 — Form + confirm | form.html, user_{new,edit,view}, group_{new,edit}, confirm_delete, user_confirm_delete, group_confirm_delete, lock_user, bulk_confirm_{delete,action}, confirm_admin_action |
| 4 — Settings + auth | account_sessions, password_change, mfa_{enroll,disable,regenerate,enroll_complete,regenerate_complete}. MFA settings pages migrated from the auth-style .rio-login centered shell to the in-chrome settings pattern — sidebar shows correctly now. |
| 5 — Admin tools | docs_index, apis_index, feature_flags, db_browser, object_history, csv_import_result |
| 6 — Polish | user_view final empty-state polish, version bump, CHANGELOG |
What rolls into this release
Two earlier in-progress passes (v0.18.6 polish + bug-fix bundle) ship together with the visual overhaul as one minor bump:
- 0.18.6 polish (committed under
a875ec4): topbar search trigger goes wide (flex:1), list-toolbar 2-row reflow, per-row kebab dropdown, dashboard greeting trimmed, smooth area-chart sparkline. - 0.18.6 bug fixes (committed under
2a43d59as pre-tested drop-in files):--rio-z-*ladder, kebab JS via<details>+ JS upgrade to escape table overflow, shared_row_actions.htmlpartial, focus-visible polish, post-seed demo rows.
Compatibility
- Library, macros, CLI public surface: unchanged.
- Projects that override
--rio-*tokens in their own stylesheet: no change, overrides still cascade. - Templates that depend on the dropped
.rio-dashboard-greeting*or.rio-dashboard-section-*selectors need to switch to the unified.rio-page-header/.rio-section. None of the framework's own templates still emit them. .rio-emptyis kept in the cascade for back-compat but the framework's own templates all use.rio-empty-statenow.- MSRV: 1.88 (unchanged).
See CHANGELOG.md for the full per-section record.
v0.18.4 — first publish since v0.13.0
All four workspace crates published to crates.io at 0.18.4. First publish since v0.13.0 — closes the 5-minor gap between the local workspace and the registry.
What was blocking publish
crates/rustio-admin/src/admin/docs.rs was reaching include_str!("../../../../docs/<file>.md") outside the crate root. Worked in-tree but failed cargo publish verification: the published tarball contains only the crate's own files, so the path didn't resolve. The bug was latent since v0.16.0 (introduced by 8817234); never surfaced because no version ≥ 0.14.0 had been published.
The fix
Copied architecture.md, modeladmin.md, and public-api.md into crates/rustio-admin/assets/docs/ and updated the three include_str! paths to ../../assets/docs/<file>.md (now inside the crate root, so they survive the publish tarball).
The workspace docs/ directory remains the canonical source for the human-facing site and IDE access; the in-crate copies are bundled at release time. Future doc edits should update both.
Side fix
A doc passage in architecture.md literally documents the Tier-2 symbol grep pattern — which then matched its own self-described regex when the doc moved inside crates/. The Tier-2 guard now excludes crates/*/assets/** (it scans code, not documentation).
Published crates
| Crate | crates.io | Notes |
|---|---|---|
rustio-admin |
0.18.4 | 17 total versions |
rustio-admin-macros |
0.18.4 | 17 total versions |
rustio-admin-cli |
0.18.4 | 14 total versions |
rio-theme |
0.18.4 | first release — name claimed |
What this unlocks
rustio startprojectnow works against the unmodified crates.io registry — no[patch.crates-io]workaround needed.- The full theme engine (
rio-theme) is available as a standalone build-time crate for any Rust project, not justrustio-adminconsumers. - All the work since v0.14.0 — Builder MVP, visual identity overhaul, dark-frame chrome, Operations & i18n surfaces, theme engine — is now accessible via
cargo add rustio-admin.
Compatibility
- No public API change vs 0.18.3.
- Drop-in upgrade for anyone tracking the workspace; the v0.13.0 → v0.18.4 jump is a five-minor bundle whose per-release detail is in
CHANGELOG.md. - MSRV: 1.88 (unchanged since v0.14.0).
v0.18.3 — green CI bundle
Patch release on top of v0.18.2.
Why this exists
v0.18.2 was tagged correctly but the tagged tree carried two pre-existing CI blockers that the prior releases never caught (CI had been red on the last five commits for unrelated reasons). Verifying the new template-pin guard fires on the runner surfaced them. v0.18.3 cuts a tag that ships with all 15 CI steps green.
Fixed
-
cargo fmt --checkclean on the whole workspace. Pre-existing rustfmt drift incrates/rio-theme/(introduced with the crate in 0.17.0 and never run through fmt locally) plus the CLI'stheme.rs. Mechanical reformat, no behavior change. -
clippy::uninlined-format-argsviolation incrates/rio-theme/src/color.rs. CI pinsrustc 1.88, whose clippy enforces this as a hard error under-D warnings; newer local toolchains are lenient.-format!(\"#{:02x}{:02x}{:02x}\", r, g, b) +format!(\"#{r:02x}{g:02x}{b:02x}\")
Same lint class that bit
bcdf9b6last quarter — flagging for the release checklist.
Verified
- All 15 CI steps green on run 26337327486 (the v0.18.3 release commit itself): fmt, clippy, build, test, Tier-2 symbol guard, four Builder Doctrine guards, and the new scaffold-template-pin guard.
- The template-pin guard's FAIL path independently verified on run 26337205216 via throwaway PR #2 (closed without merge): every other step green, only the guard step failed with the expected actionable error message:
::error::Workspace version (0.18.2) and scaffold template pin (0.16.0) have drifted. Update crates/rustio-admin-cli/templates/project/Cargo.toml.tmpl to read 'rustio-admin = \"0.18.2\"'. See v0.18.1 release notes for the convention.
Compatibility
- No library API change.
- No CLI surface change.
- Drop-in upgrade from 0.18.2.
- Release checklist note: run
cargo +1.88 clippyandcargo fmt --checklocally before tagging. CI catches it but earlier feedback is cheaper.
v0.18.2 — CI guard for template-pin drift
Patch release on top of v0.18.1.
What's new
A CI guard that prevents the kind of drift v0.18.1 patched. The scaffold template pin is now structurally locked to the workspace minor — silent stale pins can't ship again.
The guard
New step in .github/workflows/ci.yml:
- name: scaffold template pin tracks workspace minor
run: |
set -eu
workspace=$(awk '
/^\[workspace\.package\]/ { in_section = 1; next }
/^\[/ { in_section = 0 }
in_section && $1 == "version" { gsub(/"/, "", $3); print $3; exit }
' Cargo.toml)
template=$(grep -E '^rustio-admin\s*=\s*"' \
crates/rustio-admin-cli/templates/project/Cargo.toml.tmpl \
| head -1 | cut -d'"' -f2)
if [ "$workspace" != "$template" ]; then
echo "::error::Workspace version ($workspace) and scaffold template pin ($template) have drifted..."
exit 1
fi
echo "OK: workspace and scaffold template both pin $workspace"Why it matters
For three releases (0.17.0, 0.17.1, 0.18.0) the workspace bumped to a new minor while the bundled rustio startproject template kept pinning rustio-admin = "0.16.0" — a version that never existed on crates.io. Every fresh scaffold hit a registry resolve failure. The bug was silent because the template file isn't compiled — it's read at scaffold time, well after CI is done.
This guard catches the drift at the source: any PR that bumps the workspace version without bumping the template pin fails CI immediately.
Verification
Tested locally:
- In-sync (
0.18.2 ↔ 0.18.2):OK: workspace and scaffold template both pin 0.18.2. - Drift simulated (template reverted to
0.16.0):::error::Workspace version (0.18.1) and scaffold template pin (0.16.0) have drifted. Update crates/rustio-admin-cli/templates/project/Cargo.toml.tmpl to read 'rustio-admin = "0.18.1"'. → exit 1.
This release exercises the guard at its own bump — proof the system works end-to-end.
Compatibility
- No library API change.
- No CLI surface change.
- CI workflow only.
- Drop-in upgrade from 0.18.1.