You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
mtui reaches users through exactly two channels today:
an OBS/IBS RPM, built and published from the Build Service by a maintainer
walking the seven-step recipe in docs/src/installation.md § Cutting a
release;
a GitHub release tarball, built by .github/workflows/release.yml when an
unprefixed version tag is pushed (x86_64-unknown-linux-musl + aarch64-apple-darwin).
Neither serves a Rust developer who wants cargo install, nor someone who wants
to run mtui-mcp --transport http next to an LLM client without building it,
nor the maintainer — who still hand-bumps the workspace version, hand-writes the
CHANGELOG section, tags, and then drives OBS by hand.
Three separable pieces of work, filed as one issue because their inputs
interlock: the crates.io name, the tag shape, and the version scheme are shared
decisions.
Current state (verified, not assumed)
release.yml fires on [0-9]+.[0-9]+.[0-9]+* — unprefixed tags. Every
real tag matches (18.2.1 … 26.1.2); workspace version is 26.2.0.
Docs drift: § Cutting a release says git tag v1.2.0. No such tag has
ever existed. The _serviceversionrewrite tolerates both, release.yml's
filter effectively does too, but this must be settled before automating —
a release tool needs one answer.
Releases are cut on per-minor branches (26.0.x, 26.1.x) that fork from main and are never merged back. release.yml's release-notes-base walk
exists precisely because consecutive tags are siblings, not ancestors. Any
release automation has to survive that topology.
8 publishable crates; xtask is already publish = false; fuzz/ is its own
detached workspace, so neither is in the way.
cargo publish -p mtui-types --dry-run → packages fine, but warning: manifest has no description. crates.io rejects an upload with
no description, so this is a hard blocker wearing a warning's clothes.
cargo publish -p mtui-core --dry-run → hard error: all dependencies must have a version requirement specified when publishing; dependency 'mtui-config' does not specify a version. Every internal path dep
in [workspace.dependencies] is path-only.
The crates.io name mtui is taken. It is an unrelated Modbus TUI
(inowattio/MTUI), at 0.9.6, last published 2026-07-24, owner CosminPerRam. Actively maintained — not squatted, so there is no name-policy
claim to make. Every other name we need (mtui-types, mtui-config, mtui-hosts, mtui-datasources, mtui-testreport, mtui-core, mtui-cli, mtui-mcp) is free right now.
Plan
Phase 1 — make the workspace publishable (publishes nothing)
Add the crates.io-required metadata: description (per crate — it is the one
field that cannot be meaningfully inherited), plus readme, keywords, categories, homepage/documentation inherited from [workspace.package] where they are genuinely common.
Give every internal dep a version requirement alongside its path
(mtui-types = { path = "crates/mtui-types", version = "=26.2.0" } — exact
vs caret is Q4).
Audit each package payload with cargo package --list. The tests/
fixtures are the golden authority for the wire contracts (lock format,
history format, testreport export, MCP schemas) — decide per crate whether
they ship (reproducible cargo test for a downstream) or are excluded
(smaller tarball). mtui-datasources/mtui-testreport fixtures are the
large ones (~288K/200K).
Add a CI job running cargo package --workspace --locked so metadata rot
fails a PR instead of a release.
Done when:cargo package --workspace is clean and the new CI job is green.
No behaviour change, no publish.
Phase 2 — crates.io
Publish order (a dependency walk, not a preference): mtui-types → mtui-config → mtui-hosts → mtui-datasources → mtui-testreport → mtui-core → {mtui-cli, mtui-mcp}.
Reserve the names with a first publish early — all 8 are free today and
the one we actually wanted is already gone.
Auth via crates.io Trusted Publishing (OIDC from the release workflow)
rather than a long-lived CARGO_REGISTRY_TOKEN; the repo runs Scorecard and
pins every action by SHA, a standing publish token is the odd one out.
Documentation must be honest about what this channel is not: cargo install mtui-cli gives you the mtui binary and nothing else — no
completions, no man pages, no dist/terms/*.sh launchers (so terms/switch
degrade), and mtui-mcp needs --features mcp to be a server at all (Q5).
The RPM stays the complete install.
Phase 3 — container image (docker + podman)
What it is for: primarily mtui-mcp --transport http running beside an
LLM client; secondarily a throwaway mtui REPL.
Base image is the load-bearing choice (Q6): scratch/distroless with
the static musl binary is smallest but has no svn, so SVN testreport
checkout is simply absent; registry.opensuse.org/opensuse/bci/bci-base can zypper in subversion openssh-clients, is idiomatic for an openSUSE project,
and costs ~50 MB.
Credentials are mounted, never baked: ~/.ssh (pubkey-only by design), oscrc, openQA client.conf, mtui.toml. Document the bind mounts with :ro (and ,Z for SELinux). Run as a non-root user, read-only rootfs where
possible.
terms/switch are inert in a container (no terminal emulator). The
graceful-degradation contract already covers the behaviour; the docs must say
it out loud so it is not read as a bug.
Multi-arch amd64 + arm64. The current release matrix has no aarch64-unknown-linux-musl leg — adding one also yields an arm64 Linux
tarball, which is useful on its own.
Containerfile at the repo root (docker builds it unchanged), plus a
documented podman run invocation. Tags X.Y.Z, X.Y, latest; cosign
signature + build-provenance attestation.
Registry choice is Q7.
Phase 4 — release-plz
What it buys: a Release PR that bumps the version and writes the CHANGELOG
section from conventional commits; on merge it tags, publishes to crates.io, and
can create the GitHub release.
Friction that must be resolved before adopting it — none of it fatal, all of it
a decision:
Version scheme. release-plz infers semver bumps from conventional commits.
mtui's majors (18 → 19 → 26) are not commit-derived. Either accept inferred
bumps, or run it with the maintainer setting the version and release-plz doing
only changelog + tag + publish (Q8).
Branch topology. release-plz assumes one release branch; mtui cuts on 26.N.x branches that never merge back. Release PR on main + cherry-picks
is the usual shape, but it needs deciding (Q8).
CHANGELOG.CHANGELOG.md is hand-curated Keep-a-Changelog prose with real
explanatory paragraphs; release-plz emits git-cliff output. Either let it
generate a draft and hand-edit it inside the Release PR (realistic), or keep
the changelog fully manual and disable that feature. Note CHANGELOG has no sections for released 26.0.2 and 26.0.3; blocks curated release notes #401 — two released
versions have no section at all — is a practical prerequisite either way.
Tag name.git_tag_name = "{{ version }}" to preserve unprefixed tags; _service's @PARENT_TAG@ + versionrewrite and release.yml's tag filter
both depend on the current shape.
Division of labour with release.yml (Q9): keeping release.yml as the
tag-triggered artifact builder and letting release-plz only bump/tag/publish
preserves the working matrix + notes-base logic.
Questions
Q1 — crates.io name.mtui is taken by an active unrelated project.
(a) publish the binary crate as mtui-cli; the installed binary is still mtui, no coordination needed — recommended;
(b) ask the owner to transfer — unlikely, and slow;
(c) rename the published umbrella (mtui-rs, suse-mtui).
This also decides the container image name and the docs wording.
Q2 — publish surface. crates.io requires every dependency to be published,
so shipping the binaries means shipping all 8 crates. Do we accept publishing
the internal library crates with an explicit "no API stability promise; the
supported interface is the mtui CLI and the MCP tool surface" disclaimer?
(Recommended: yes.)
Q3 — lockstep versions. Every crate bumps to 26.x even when unchanged.
Keep the single workspace version (consistent with the RPM's one-version story),
or let release-plz version crates independently? (Recommended: lockstep.)
Q4 — internal dep requirement:version = "=26.2.0" (exact, matches
lockstep and makes a mixed-version install impossible) vs "26.2" caret.
(Recommended: exact.)
Q5 — mtui-mcp's mcp feature. Off by default so the mtui build never
pulls rmcp/axum. For a published crate that means cargo install mtui-mcp
yields a binary that is not a server. Make the feature default-on for the
published mtui-mcp package, or document --features mcp at every install
site?
Q6 — container base. Which workflows must work inside the container? If
SVN testreport checkout must work, it is BCI + subversion; if the container is
MCP-only and the Gitea checkout path suffices, scratch + static musl is on the
table.
Q7 — registry.ghcr.io/openSUSE/mtui (CI-native, immediate, free) vs registry.opensuse.org via an OBS container build (consistent with the RPM
channel and its signing, more setup). Both?
Q8 — release-plz control model. Maintainer-set version or
conventional-commit inference? And: run it on main only with cherry-picks to 26.N.x, or per release branch?
Q9 — who creates the GitHub release, release-plz or the existing release.yml on the tag? (Recommended: release.yml keeps it — the matrix and
notes-base logic already work and are non-obvious.)
Q10 — crates.io auth. Trusted Publishing (OIDC) needs a crates.io-side
config per crate (8 of them) and a named workflow/environment. Acceptable, or
start with a repository secret and migrate?
Q11 — ownership. Which crates.io account(s) own the crates? More than one
owner, so the names are not stranded behind a bus factor of 1.
Out of scope
Homebrew / AUR / nixpkgs packaging.
Changing the OBS/IBS RPM pipeline — it remains the primary channel for SUSE
users, and everything here is additive to it.
Phase 1: cargo package --workspace --locked clean; CI job guarding it green.
Phase 2: all 8 crates on crates.io at the current version, published by CI
from a tag, with cargo install mtui-cli documented and verified end to end.
Phase 3: signed multi-arch image pullable by both docker pull and podman pull, with a documented run invocation for mtui-mcp --transport http
that mounts credentials read-only and runs as non-root; docs/src/installation.md
gains a container section stating what does not work in it.
Phase 4: a version bump produces a Release PR; merging it tags, publishes, and
triggers the existing artifact build — with the maintainer's answers to Q8/Q9
encoded in release-plz.toml, and the docs' tag-prefix drift fixed.
Why
mtui reaches users through exactly two channels today:
walking the seven-step recipe in
docs/src/installation.md§ Cutting arelease;
.github/workflows/release.ymlwhen anunprefixed version tag is pushed (
x86_64-unknown-linux-musl+aarch64-apple-darwin).Neither serves a Rust developer who wants
cargo install, nor someone who wantsto run
mtui-mcp --transport httpnext to an LLM client without building it,nor the maintainer — who still hand-bumps the workspace version, hand-writes the
CHANGELOG section, tags, and then drives OBS by hand.
Three separable pieces of work, filed as one issue because their inputs
interlock: the crates.io name, the tag shape, and the version scheme are shared
decisions.
Current state (verified, not assumed)
release.ymlfires on[0-9]+.[0-9]+.[0-9]+*— unprefixed tags. Everyreal tag matches (
18.2.1…26.1.2); workspace version is26.2.0.git tag v1.2.0. No such tag hasever existed. The
_serviceversionrewritetolerates both,release.yml'sfilter effectively does too, but this must be settled before automating —
a release tool needs one answer.
26.0.x,26.1.x) that fork frommainand are never merged back.release.yml's release-notes-base walkexists precisely because consecutive tags are siblings, not ancestors. Any
release automation has to survive that topology.
xtaskis alreadypublish = false;fuzz/is its owndetached workspace, so neither is in the way.
cargo publish -p mtui-types --dry-run→ packages fine, butwarning: manifest has no description. crates.io rejects an upload withno
description, so this is a hard blocker wearing a warning's clothes.cargo publish -p mtui-core --dry-run→ hard error:all dependencies must have a version requirement specified when publishing; dependency 'mtui-config' does not specify a version. Every internal path depin
[workspace.dependencies]is path-only.mtuiis taken. It isan unrelated Modbus TUI
(
inowattio/MTUI), at0.9.6, last published 2026-07-24, ownerCosminPerRam. Actively maintained — not squatted, so there is no name-policyclaim to make. Every other name we need (
mtui-types,mtui-config,mtui-hosts,mtui-datasources,mtui-testreport,mtui-core,mtui-cli,mtui-mcp) is free right now.Plan
Phase 1 — make the workspace publishable (publishes nothing)
description(per crate — it is the onefield that cannot be meaningfully inherited), plus
readme,keywords,categories,homepage/documentationinherited from[workspace.package]where they are genuinely common.(
mtui-types = { path = "crates/mtui-types", version = "=26.2.0" }— exactvs caret is Q4).
cargo package --list. Thetests/fixtures are the golden authority for the wire contracts (lock format,
history format, testreport export, MCP schemas) — decide per crate whether
they ship (reproducible
cargo testfor a downstream) or are excluded(smaller tarball).
mtui-datasources/mtui-testreportfixtures are thelarge ones (~288K/200K).
cargo package --workspace --lockedso metadata rotfails a PR instead of a release.
Done when:
cargo package --workspaceis clean and the new CI job is green.No behaviour change, no publish.
Phase 2 — crates.io
mtui-types→mtui-config→mtui-hosts→mtui-datasources→mtui-testreport→mtui-core→ {mtui-cli,mtui-mcp}.the one we actually wanted is already gone.
rather than a long-lived
CARGO_REGISTRY_TOKEN; the repo runs Scorecard andpins every action by SHA, a standing publish token is the odd one out.
cargo install mtui-cligives you themtuibinary and nothing else — nocompletions, no man pages, no
dist/terms/*.shlaunchers (soterms/switchdegrade), and
mtui-mcpneeds--features mcpto be a server at all (Q5).The RPM stays the complete install.
Phase 3 — container image (docker + podman)
mtui-mcp --transport httprunning beside anLLM client; secondarily a throwaway
mtuiREPL.scratch/distroless withthe static musl binary is smallest but has no
svn, so SVN testreportcheckout is simply absent;
registry.opensuse.org/opensuse/bci/bci-basecanzypper in subversion openssh-clients, is idiomatic for an openSUSE project,and costs ~50 MB.
~/.ssh(pubkey-only by design),oscrc, openQAclient.conf,mtui.toml. Document the bind mounts with:ro(and,Zfor SELinux). Run as a non-root user, read-only rootfs wherepossible.
terms/switchare inert in a container (no terminal emulator). Thegraceful-degradation contract already covers the behaviour; the docs must say
it out loud so it is not read as a bug.
aarch64-unknown-linux-muslleg — adding one also yields an arm64 Linuxtarball, which is useful on its own.
Containerfileat the repo root (docker builds it unchanged), plus adocumented
podman runinvocation. TagsX.Y.Z,X.Y,latest; cosignsignature + build-provenance attestation.
Phase 4 — release-plz
What it buys: a Release PR that bumps the version and writes the CHANGELOG
section from conventional commits; on merge it tags, publishes to crates.io, and
can create the GitHub release.
Friction that must be resolved before adopting it — none of it fatal, all of it
a decision:
mtui's majors (18 → 19 → 26) are not commit-derived. Either accept inferred
bumps, or run it with the maintainer setting the version and release-plz doing
only changelog + tag + publish (Q8).
26.N.xbranches that never merge back. Release PR onmain+ cherry-picksis the usual shape, but it needs deciding (Q8).
CHANGELOG.mdis hand-curated Keep-a-Changelog prose with realexplanatory paragraphs; release-plz emits git-cliff output. Either let it
generate a draft and hand-edit it inside the Release PR (realistic), or keep
the changelog fully manual and disable that feature. Note CHANGELOG has no sections for released 26.0.2 and 26.0.3; blocks curated release notes #401 — two released
versions have no section at all — is a practical prerequisite either way.
git_tag_name = "{{ version }}"to preserve unprefixed tags;_service's@PARENT_TAG@+versionrewriteandrelease.yml's tag filterboth depend on the current shape.
release.yml(Q9): keeping release.yml as thetag-triggered artifact builder and letting release-plz only bump/tag/publish
preserves the working matrix + notes-base logic.
Questions
Q1 — crates.io name.
mtuiis taken by an active unrelated project.(a) publish the binary crate as
mtui-cli; the installed binary is stillmtui, no coordination needed — recommended;(b) ask the owner to transfer — unlikely, and slow;
(c) rename the published umbrella (
mtui-rs,suse-mtui).This also decides the container image name and the docs wording.
Q2 — publish surface. crates.io requires every dependency to be published,
so shipping the binaries means shipping all 8 crates. Do we accept publishing
the internal library crates with an explicit "no API stability promise; the
supported interface is the
mtuiCLI and the MCP tool surface" disclaimer?(Recommended: yes.)
Q3 — lockstep versions. Every crate bumps to
26.xeven when unchanged.Keep the single workspace version (consistent with the RPM's one-version story),
or let release-plz version crates independently? (Recommended: lockstep.)
Q4 — internal dep requirement:
version = "=26.2.0"(exact, matcheslockstep and makes a mixed-version install impossible) vs
"26.2"caret.(Recommended: exact.)
Q5 —
mtui-mcp'smcpfeature. Off by default so themtuibuild neverpulls
rmcp/axum. For a published crate that meanscargo install mtui-mcpyields a binary that is not a server. Make the feature default-on for the
published
mtui-mcppackage, or document--features mcpat every installsite?
Q6 — container base. Which workflows must work inside the container? If
SVN testreport checkout must work, it is BCI +
subversion; if the container isMCP-only and the Gitea checkout path suffices, scratch + static musl is on the
table.
Q7 — registry.
ghcr.io/openSUSE/mtui(CI-native, immediate, free) vsregistry.opensuse.orgvia an OBS container build (consistent with the RPMchannel and its signing, more setup). Both?
Q8 — release-plz control model. Maintainer-set version or
conventional-commit inference? And: run it on
mainonly with cherry-picks to26.N.x, or per release branch?Q9 — who creates the GitHub release, release-plz or the existing
release.ymlon the tag? (Recommended: release.yml keeps it — the matrix andnotes-base logic already work and are non-obvious.)
Q10 — crates.io auth. Trusted Publishing (OIDC) needs a crates.io-side
config per crate (8 of them) and a named workflow/environment. Acceptable, or
start with a repository secret and migrate?
Q11 — ownership. Which crates.io account(s) own the crates? More than one
owner, so the names are not stranded behind a bus factor of 1.
Out of scope
users, and everything here is additive to it.
prerequisite for any generated changelog.
Definition of done
cargo package --workspace --lockedclean; CI job guarding it green.from a tag, with
cargo install mtui-clidocumented and verified end to end.docker pullandpodman pull, with a documented run invocation formtui-mcp --transport httpthat mounts credentials read-only and runs as non-root;
docs/src/installation.mdgains a container section stating what does not work in it.
triggers the existing artifact build — with the maintainer's answers to Q8/Q9
encoded in
release-plz.toml, and the docs' tag-prefix drift fixed.