# Packaging and release
This document describes how the tix issue tracker is packaged and published as
an official Rust crate, and how it is installed by users:
- **crates.io** — `cargo install tix-tracker` (builds from source).
- **cargo-binstall** — `cargo binstall tix-tracker` (prebuilt binaries).
- **Source** — `cargo install --path .` (development), and the linked GitLab
repository: <https://gitlab.com/thyseus/tix>.
## Naming
The crate published on crates.io is **`tix-tracker`**, not `tix`:
- The crates.io name **`tix` is already taken** by an unrelated, active crate
(a CLI alarm clock/timer). `cargo install tix` installs that other crate, so
this project publishes under `tix-tracker` instead.
- The **installed binary is still `tix`** — only the crate name differs.
| crates.io package | `tix-tracker` |
| Binary | `tix` |
| Library | `tix` (kept so existing `tix::` code paths are unchanged) |
| Repository / homepage | https://gitlab.com/thyseus/tix |
| Documentation | https://docs.rs/tix-tracker |
The relevant `[package]` metadata lives in `Cargo.toml`:
```toml
[package]
name = "tix-tracker"
repository = "https://gitlab.com/thyseus/tix"
homepage = "https://gitlab.com/thyseus/tix"
documentation = "https://docs.rs/tix-tracker"
keywords = ["issues", "kanban", "tracker", "cli", "tui"]
categories = ["command-line-utilities"]
rust-version = "1.85" # since the crate uses edition 2024
[[bin]]
name = "tix"
path = "src/main.rs"
```
`repository` is what links the crate to the GitLab repo on the crates.io page
and is also what `cargo-binstall` uses to locate release binaries.
---
## Prerequisites
- **Rust toolchain** (edition 2024 / MSRV 1.85+), i.e. `cargo`.
- **crates.io account + API token** (next section) for publishing.
- (Optional, for binary installs) `cargo-binstall` — see
[Binary installation](#binary-installation).
## 1. crates.io account & authentication
1. Go to <https://crates.io> and click **Sign in**.
2. Create your account by logging in with your **GitHub** or **GitLab**
identity. crates.io has no username/password signup — OAuth only.
3. After the first login crates.io asks for a **username** (this is the name
shown as the crate owner) and an **email address**; confirm the email.
4. Generate an API token:
- Go to <https://crates.io/settings/tokens> → **New token**.
- Give it a name (e.g. `tix-cli`) and a **scope**:
- `publish-new` + `publish-update`: for publishing. Enough for either a
new crate or updates (this is what a maintainer token should use).
- `publish-update`: only for updating an existing crate you own.
- Copy the token now — crates.io shows it only once.
5. Log in locally with cargo (writes the token to
`~/.cargo/credentials.toml`, mode 0600):
```bash
cargo login
cargo login <token>
```
Verify with `cargo login --help` and check that `$CARGO_HOME/credentials.toml`
exists and is owned by you (cargo refuses insecure permissions).
6. **CI automation** (GitLab CI / a release pipeline): store the token as a
masked CI/CD variable, e.g. `CARGO_REGISTRY_TOKEN`, and publish with
`cargo publish --token "$CARGO_REGISTRY_TOKEN"`. Never commit the token.
## 2. Pre-publish checks
Before the first (or any) publish, from the repo root:
```bash
cargo fmt --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test
```
Then verify exactly what would land in the crate:
```bash
cargo package --list # list every file that will be uploaded
cargo package # build the .crate archive (verifies it compiles + checks metadata)
cargo publish --dry-run # run the full publish pipeline without uploading
```
Notes:
- The `[package] exclude` list keeps governance artifacts (`.kittify/`,
`kitty-specs/`, `kitty-ops/`, `.agents/`, `.worktrees/`, `openspec/`,
`.specify/`, `specs/`) out of the crate.
Review `cargo package --list` output to confirm the crate contains only
source, `README.md`, and the `LICENSE-*` files.
- `cargo publish` verifies the spam/immutability checks locally; the first
publish also performs a package-size sanity check.
## 3. Publish to crates.io
**First publish** (creates the `tix-tracker` namespace):
```bash
cargo publish
```
Owners can be added afterwards from the crates.io dashboard
(`tix-tracker` → Owner settings → invite).
**Subsequent releases** — bump the `version` in `Cargo.toml` (SemVer, see
[Release checklist](#release-checklist)), update `CHANGELOG.md`, then republish:
```bash
cargo publish
```
After a successful publish the crate and its README appear on
<https://crates.io/crates/tix-tracker> (allow a couple of minutes for the index
to update).
### docs.rs
docs.rs builds the `tix` library automatically on every publish (the crate's
`documentation` metadata points at it):
<https://docs.rs/tix-tracker>. If a docs build ever fails, trigger a rebuild
from the docs.rs dashboard — do **not** republish just to fix docs.
## 4. `cargo install tix-tracker` (users, from source)
Any user with a Rust toolchain installs the latest version:
```bash
cargo install tix-tracker
```
The `tix` binary lands in `~/.cargo/bin` (or `$CARGO_BIN_DIR`).
- Pin a version: `cargo install tix-tracker --version 0.1.0`
- Reproducible builds from the lockfile: `cargo install tix-tracker --locked`
- The crate ships a `tix` library **and** the `tix` binary; `cargo install`
builds the binary and ignores the library's public API.
## 5. Binary installation: `cargo binstall tix-tracker`
`cargo-binstall` downloads prebuilt binaries instead of compiling, so installs
are seconds, not minutes, and don't require a Rust toolchain. It is a drop-in
replacement for `cargo install` for most cases.
### Users
```bash
cargo install cargo-binstall --locked # one-time install of binstall
cargo binstall tix-tracker # -> downloads the prebuilt binary
```
If no prebuilt binary exists for your platform/version, binstall falls back to
a source build via `cargo install` (it asks first).
### Maintainers: publishing prebuilt binaries
`cargo-binstall` reads the `[package.metadata.binstall]` section in
`Cargo.toml`:
```toml
[package.metadata.binstall]
pkg-url = "{ repo }/-/releases/v{ version }/downloads/{ name }-{ target }-v{ version }{ archive-suffix }"
bin-dir = "{ name }-{ target }-v{ version }/{ bin }{ binary-ext }"
pkg-fmt = "tgz"
```
It expands to, for example, version `0.1.0` on `x86_64 Linux`:
- URL: `https://gitlab.com/thyseus/tix/-/releases/v0.1.0/downloads/tix-tracker-x86_64-unknown-linux-gnu-v0.1.0.tgz`
- Path inside the archive: `tix-tracker-x86_64-unknown-linux-gnu-v0.1.0/tix`
So for every release you must attach one **`.tgz` archive per target**, named
exactly like the URL above, each containing a `tix` binary (plus optional
manpages, shell completions, etc.) unpacked into the `name-target-vversion/`
directory.
Manual build + upload example:
```bash
# for each target triple you support:
rustup target add x86_64-unknown-linux-gnu
cargo build --release --target x86_64-unknown-linux-gnu
mkdir -p "tix-tracker-x86_64-unknown-linux-gnu-v0.2.0"
cp target/x86_64-unknown-linux-gnu/release/tix "tix-tracker-x86_64-unknown-linux-gnu-v0.2.0/"
tar -czf tix-tracker-x86_64-unknown-linux-gnu-v0.2.0.tgz tix-tracker-x86_64-unknown-linux-gnu-v0.2.0
# attach to the GitLab release for tag v0.1.0 (needs `glab auth login`):
glab release create v0.1.0 \
tix-tracker-x86_64-unknown-linux-gnu-v0.1.0.tgz \
tix-tracker-aarch64-unknown-linux-gnu-v0.1.0.tgz \
tix-tracker-x86_64-apple-darwin-v0.1.0.tgz \
tix-tracker-aarch64-apple-darwin-v0.1.0.tgz
```
That manual flow is enough to make `cargo binstall tix-tracker` work. Prefer to
automate it (GitLab CI job that builds on a tag and creates the release), so
source and binaries ship together — see the
[Release checklist](#release-checklist).
#### GitLab release asset notes
- GitLab's **permanent download links**
(`.../-/releases/<tag>/downloads/<file>`) are what binstall hits. For files
uploaded directly as release attachments, the permanent link keeps the
original filename, so the names must match the template above.
- If you create the assets as GitLab _links_ instead (API only), set the
link's `filepath` to the same name.
- gitlab.com has this permanently-linked-assets behavior out of the box.
- `binstall` also ships sensible _defaults_ for GitLab
(`.../-/releases/{version}/downloads/binaries/...`); we pin an explicit
`pkg-url`/`bin-dir` instead so the file naming is under our control.
#### Verifying the binary install
Before publishing a release, test the URL resolution locally against the
working tree:
```bash
cargo binstall tix-tracker --manifest-path Cargo.toml --verbose --dry-run
```
After publishing, verify end-to-end on a clean machine (or after removing your
local copy):
```bash
cargo binstall tix-tracker --verbose # or just: cargo binstall tix-tracker
tix --version
```
## Release checklist
For each new version:
1. Bump `version` in `Cargo.toml` (SemVer; `breaking → major`,
`feature → minor`, otherwise patch). Keep `Cargo.lock` in sync —
`cargo metadata --no-deps` reflects the new version.
2. Add a `[x.y.z] — date` entry in `CHANGELOG.md` and move unreleased notes up.
3. Run `cargo fmt --check`, `cargo clippy -- -D warnings`, `cargo test`,
`cargo package`.
4. `cargo publish` (steps 1–4 of this document).
5. Tag and release on GitLab with the same version:
```bash
git tag -a v0.1.0 -m "tix-tracker 0.1.0"
git push origin v0.1.0
glab release create v0.1.0 <prebuilt-archives...>
```
The tag must be `v<version>` — both the binstall `pkg-url` template and the
GitLab default scheme expect it.
6. Attach the prebuilt archives (each target), then verify
`cargo binstall tix-tracker` on at least one platform.
7. Confirm docs.rs built: <https://docs.rs/tix-tracker>.
## Yanking a broken release
If a published version is broken and cannot be fixed by a patch quickly, yank
it (removes it from new resolution; existing installs stay):
```bash
cargo yank tix-tracker --version 0.1.0
```
Always yank _after_ publishing a fixed version, so `cargo install tix-tracker`
resolves to the good one. To undo: `cargo yank --undo`.