Command-line interface for YouTrack
  • Rust 95.5%
  • Just 2.4%
  • Shell 1.1%
  • Dockerfile 0.7%
  • Nushell 0.3%
Find a file
David 4a16163d92
All checks were successful
Check / fmt + clippy + build + tests (push) Successful in 17s
Merge pull request 'feat(justfile): add dev-clean / dev-clean-all teardown recipes' (#71) from feat/dev-clean-recipes into main
Reviewed-on: #71
2026-06-13 19:47:24 +02:00
.devcontainer chore: adopt governance baseline (Docker / CI / dev container) 2026-05-12 18:23:28 -04:00
.forgejo/workflows fix(release): resolve previous tag from HEAD^ for changelog range 2026-05-31 13:33:15 -04:00
.idea feat: Add JetBrains .idea directory 2026-05-14 13:03:21 -04:00
crates fix(attachment): download file bytes, not the YouTrack SPA 2026-06-13 00:57:22 -04:00
oci-build fix(ci): copy all workspace member manifests in Docker builds 2026-05-31 13:23:18 -04:00
.dockerignore chore: adopt governance baseline (Docker / CI / dev container) 2026-05-12 18:23:28 -04:00
.gitignore chore: Sync .gitignore 2026-06-06 13:12:28 -04:00
Cargo.lock fix(attachment): download file bytes, not the YouTrack SPA 2026-06-13 00:57:22 -04:00
Cargo.toml refactor(workspace): move binary crate to crates/yt-cli 2026-05-27 04:34:36 -04:00
CLAUDE.md feat(project): add full read and CRUD for project VCS integrations 2026-06-06 10:37:28 -04:00
compose.dev.yml chore: rename shipped binary from youtrack-cli to yt 2026-05-13 18:36:39 -04:00
compose.yml chore: rename shipped binary from youtrack-cli to yt 2026-05-13 18:36:39 -04:00
justfile feat(justfile): add dev-clean / dev-clean-all teardown recipes 2026-06-13 12:52:27 -04:00
LICENSE.md chore: adopt governance baseline (Docker / CI / dev container) 2026-05-12 18:23:28 -04:00
README.md feat(project): add full read and CRUD for project VCS integrations 2026-06-06 10:37:28 -04:00
TODO.md feat(update): verify SHA-256 sidecar before self-replace 2026-05-14 13:37:58 -04:00

youtrack-cli

Command-line interface for YouTrack. Inspired by dukechill/youtrack-cli

Install

Latest pre-built binary (musl-static, Linux x86_64):

http get https://dev.a8n.run/api/packages/pandoras-box/generic/youtrack-cli/latest/yt-linux-x86_64 | save yt
^chmod +x yt
^./yt --version

Versioned downloads use the same path with latest replaced by the tag, e.g. .../youtrack-cli/v0.1.0/yt-linux-x86_64.

Build

Host build (requires a local Rust toolchain):

cargo build --release
# or
just build

Binary lands at target/release/yt.

Docker build (no host toolchain needed; uses ghcr.io/niceguyit/rust-builder-musl):

just build-docker          # publishable runtime image -> youtrack-cli:local
just build-docker-export   # ./dist/yt on the host
just check-docker          # builder stage only, fast Dockerfile feedback

Configuration

Per-instance configurations live under $XDG_CONFIG_HOME/youtrack-cli/. Each YouTrack instance is its own file, named after the first label of its hostname:

  • https://niceguyit.myjetbrains.com -> config-niceguyit.yml
  • https://jetbrains.myjetbrains.com -> config-jetbrains.yml

A config.yml in the same directory points at the default instance:

default: niceguyit

Directory is 0700, files are 0600. auth login to a new URL creates a new config-<instance>.yml; it does not overwrite an existing one. The default: pointer is set on the first login and never silently changed afterwards.

Pick which instance a command uses (highest priority first):

  • --instance <name> global flag (per-command override)
  • $YOUTRACK_CLI_INSTANCE environment variable
  • The default: field in config.yml
  • --config <path> bypasses all of the above and reads a single file directly (mostly for tests)
yt auth login --base-url "https://niceguyit.myjetbrains.com"   # creates config-niceguyit.yml and sets default: niceguyit

# Add a second instance (separate file; default stays as niceguyit)
yt auth login --base-url "https://prod.youtrack.cloud"

# Per-command override without changing the default
yt --instance prod list

# Switch the default: edit ~/.config/youtrack-cli/config.yml to change `default:`

yt config set board "My Agile Board"
yt config set sprint "Sprint 26"

yt config show      # masked token
yt config view      # raw YAML

Valid config set keys: url, token, sprint (writes default_sprint), board (writes board_name). For credentials prefer auth login, which verifies the token against the server before writing.

Articles (Knowledge Base)

YouTrack's web editor handles Markdown but does not reflow tables, which makes wide tables painful to edit. yt article provides a pull / edit / push loop so you can use any IDE for the editing step:

yt article list --project KB           # browse a project's articles
yt article inspect KB-A-12             # metadata + content preview
yt article pull KB-A-12                # writes ./KB-A-12.md (frontmatter + body)
# edit KB-A-12.md in your IDE: reflow tables, lint, spell-check, anything
yt article push KB-A-12.md             # pushes edits; refuses on stale remote
yt article push KB-A-12.md --ignore-stale   # override the stale-snapshot guard
yt article create new-topic.md --project KB # create from a local file
yt article delete KB-A-12 --yes        # delete an article

The pulled file is a YAML frontmatter block (id, summary, project, parent, tags, updated, content_sha256) followed by the article body byte-for-byte. The updated and content_sha256 fields back the stale-snapshot guard on push: if YouTrack has been edited since the pull, push refuses unless --ignore-stale is set. A push with no body changes is a no-op (the SHA matches), so pull then immediate push does not bump the article.

Project VCS integrations

yt project vcs reads and edits the Version Control project-settings screen. Mutations target a processor by its server-side id (shown by list / show):

yt project vcs list YT                                   # id, server, path, enabled, type (--enabled-only, --json)
yt project vcs show YT 99-1                               # every setting of one processor (--json)
yt project vcs add YT --server https://git.example.com --path acme/widgets   # add an enabled Gitea/Forgejo processor
yt project vcs add YT --server https://git.example.com --path acme/widgets --disabled   # add it disabled
yt project vcs update YT 99-1 --enabled false --path acme/gadgets --branch-spec "+:refs/heads/*"
yt project vcs delete YT 99-1                            # remove a processor

add references a VCS server that is already configured globally in YouTrack (Administration > Integrations > VCS); an unknown --server URL fails without attempting to create the server entry. Only Gitea/Forgejo processors can be created; GitHub/GitLab/etc. are follow-up work. The global --dry-run flag prints the intended endpoint and payload for add / update / delete without sending any request.

The VCS surface on the Project entity is undocumented; the mutation endpoint shapes are recorded as assumptions in code comments next to each client function and revised if a server rejects them.

Breaking change: the old read-only yt project vcs <SHORT_NAME> is now yt project vcs list <SHORT_NAME>.

Development

Host commands (require a local Rust toolchain):

just build       # cargo build --release
just test        # cargo test --all-targets
just lint        # cargo clippy --all-targets -- -D warnings
just fmt         # cargo fmt
just run         # cargo run --release

Container-based dev (no host toolchain needed):

just dev          # docker compose --file compose.dev.yml up --build
just shell        # interactive bash in the dev container
just dev-clean    # remove this user's cargo named volumes

The CI check workflow is mirrored in just pre-commit. Wire it into git commit:

just install-hooks

Tracing

$env.RUST_LOG = "yt=debug"; cargo run --release -- config show

Other implementations

License

MIT. See LICENSE.md.