gitlab-tracker 0.4.3

A fast terminal TUI dashboard for tracking GitLab Merge Requests across branches
gitlab-tracker-0.4.3 is not a library.

πŸš€ GitLab MR Tracker

CI Quality Gate Crates.io Version Crates.io Total Downloads License: MIT Built with Rust

GitLab MR Tracker is a fast, asynchronous Terminal User Interface (TUI) dashboard designed for engineering teams. It provides real-time verification of GitLab Merge Requests across target environment branches (main, preproduction, staging, etc.), handling strict SHA verification as well as cherry-picked commit identification.

gitlab-tracker demo

✨ Key Features

  • πŸ” OS Keyring Integration (Zero Plain-Text Secrets): Personal Access Tokens (PAT) can be securely stored directly in your OS secret manager (GNOME Keyring, KWallet, macOS Keychain, or Windows Credential Manager).

  • 🏷️ Dynamic Scoped Labels & Custom Chips:

    • Smart Filtering: Configure specific label prefixes (e.g., deploy::, review::) to display cleanly as colored chips in the main table grid, while keeping all attached tags visible in the side inspector panel.
    • Customizable Palette: Map label names or wildcard patterns (e.g., deploy::*) to custom terminal colors or standard HEX codes (#FF5733) via an XDG-compliant JSON config. Labels without a config override automatically fall back to their GitLab-side colour (fetched at startup), with foreground computed for legibility.
  • ⚑ High Performance & Asynchronous: Powered by tokio and reqwest, utilizing non-blocking event loops and bounded concurrent requests via semaphores to protect GitLab API rate limits.

  • πŸ›‘οΈ Pass-Through Pass Caching: Core MR metadata (author, milestone, assignee, description, labels) is fetched once and cached locally. Fully deployed MRs bypass network re-queries entirely ("Green Pass").

  • πŸ” Strict SHA Verification: Validates merge/squash commit SHAs against target branches via the GitLab Refs API (/commits/:sha/refs?type=branch). Zero false positives β€” if the SHA is not an ancestor of the branch, the MR is not considered present, regardless of title similarity or branch naming conventions.

  • πŸ–₯️ Responsive Flexbox TUI Grid: Features a dynamic layout engine (Constraint::Fill) that seamlessly scales table columns and side panels from 1080p laptop displays to ultra-wide 4K monitors without empty trailing spaces.

  • πŸ”ƒ Smart Auto-Sorting by Last Update: The dashboard defaults to sorting MRs by updated_at (most recently pushed to remote first), automatically re-applied after each refresh. Cycle through sort columns (S) and toggle direction (Shift+S). The active sort is always visible in the table title bar.

  • 🌐 Browser Integration: Open any selected MR directly in your default browser with a single keypress (O).

  • πŸ”” Smart Desktop Notifications: Receives native OS desktop notifications only when an MR's branch status has changed since the last run β€” no duplicate alerts on restart or redundant refreshes.

  • ✨ Refresh Highlight: After each background refresh, any MR whose updated_at timestamp has changed since the previous cycle is briefly highlighted in the table with a green tint. The highlight fades out automatically after ~10 seconds.

  • πŸ“ XDG-Compliant Persistence: Saves tracked dashboard state, UI configurations, and last-known branch statuses automatically to platform-standard configuration paths using directories.

  • Customizable Refresh Interval: Tailor the background polling rate to your needs (defaults to 15 minutes / 900s) via refresh_interval_secs in projects.toml.

  • πŸ“Š Activity Badge: Each MR in the Context Inspector displays a color-coded activity badge based on its updated_at timestamp β€” 🟒 Active, 🟑 Slowing, or πŸ”΄ Stale. Thresholds are fully configurable via activity_recent_days / activity_stale_days in projects.toml.

  • πŸ’¬ Notes Indicator: The total number of comments and discussion threads (user_notes_count) is fetched from the GitLab API at no extra cost and displayed both in the optional Notes table column and in the Context Inspector. A yellow πŸ’¬ N badge signals that comments are awaiting attention; a dimmed βœ” No comments confirms there is nothing to address.

  • 🎯 Review Effort Score: Each MR's diff is analysed at fetch time (files changed, lines added, lines deleted) and turned into a colour-coded effort indicator calibrated to your tech stack:

    • In the table, the optional Effort column shows a colour-coded chip badge β€” 🟒 Easy, 🟑 Medium, πŸ”΄ Complex β€” matching the style of the Inspector panel. In the side Inspector, the full breakdown is always visible: file/line counts, commit count, a 10-block progress bar, and the effort badge with the active profile name in parentheses.

    The score is computed with a weighted formula: (additions + deletions) Γ— 0.8 + files_changed Γ— 0.2, interpolated between two configurable thresholds (easy_threshold / hard_threshold). The profile is set per-project in projects.toml:

    [project.complexity_profile]
    name           = "Drupal"
    easy_threshold = 300
    hard_threshold = 2000
    

    Suggested presets:

    Tech stack easy_threshold hard_threshold Rationale
    Drupal 300 2000 Lots of YAML/config files that are verbose but lightweight to review
    Symfony / PHP 200 1200 Denser business logic, typically smaller PRs
    Java / Spring 100 600 Highly logic-dense lines; verbosity adds review cost
    TypeScript / React 150 900 JSX inflates line counts but remains readable
    Go 150 800 Concise but each line carries weight
    Generic (default) 200 1000 Conservative baseline for mixed stacks

    Diff data is cached behind the same updated_at guard as pipelines β€” no extra API call when the MR has not changed since the last refresh.

  • πŸ”€ Animated Status Badge: For open MRs, the Status column cycles through three phases every second with no extra column:

    Phase Badge Color Meaning
    1 OPEN 🟩 Green Base state
    2 Mergeability varies Live mergeability from GitLab API
    3 CI RUNNING / CI PENDING 🟧 Orange Latest pipeline is active β€” dimmed to (n/a) when no pipeline exists

    The CI badge only appears when the most recent pipeline is in Running or Pending state; otherwise phase 3 falls back to the mergeability badge. The animation keeps the layout compact while surfacing both merge-readiness and CI status at a glance.

  • πŸ—‚οΈ Toggleable Table Columns (C): Press C at any time to open an interactive column picker popup. Use ↑/↓ to navigate and Space to toggle each optional column on or off. Your selection is instantly saved to projects.toml and persisted across restarts β€” no manual file editing required. Available optional columns:

    Column Description
    Activity Color-coded activity badge β€” 🟒 Active, 🟑 Slowing, πŸ”΄ Stale (same thresholds as the Inspector)
    Target The branch the MR is intended to merge into
    Labels Filtered label chips (respects table_label_prefixes)
    Milestone The associated milestone title
    Notes Total number of comments and discussion threads β€” πŸ’¬ N in yellow when non-zero, dimmed βœ” 0 otherwise
    Effort Review effort chip badge β€” 🟒 Easy / 🟑 Medium / πŸ”΄ Complex, calibrated to your complexity_profile

    All columns are hidden by default to keep the layout compact. They can also be configured statically via [project.visible_columns] in projects.toml (see configuration section below).

  • ⭐ MR Flagging & Advanced Filters: Manually flag any MR with Space to mark it with a coloured star chevron (β˜…) in the title column. Press F to open the filter picker popup, which lets you narrow the table by:

    • Flagged β˜… β€” only your manually flagged MRs
    • GitLab state β€” Opened, Merged, or Closed
    • Mergeability β€” Mergeable, Conflict, Needs Rebase, Not Approved, Requested Changes, Draft, Discussions
    • Has comments β€” MRs with at least one note or discussion thread
    • Milestone β€” free-text search on the milestone title (case-insensitive)
    • Assignee β€” free-text search on the GitLab assignee and, when a tracker ticket is linked (e.g. Redmine), its assignee as well

    The active filter is shown in the table header. Flagged state is persisted across restarts via a tenant-scoped state file (tracker_<hash>.json, where the hash is derived from your GitLab URL and project ID).

  • 🏁 Milestone Bulk-Add (Release Manager Workflow): In Insert mode, type @ followed by any part of a milestone name to trigger a live autocomplete dropdown. Active and upcoming milestones are fetched from GitLab on startup and filtered in real time as you type. Selecting a milestone with Enter automatically adds all open MRs attached to that milestone in a single action β€” no need to enter IDs one by one. Ideal for release managers preparing a deployment checklist.

    i             β†’ Enter Insert mode
    @5.2          β†’ filters milestones containing "5.2"
    ↓ / Tab       β†’ navigate suggestions
    Enter         β†’ bulk-add all open MRs from the selected milestone
    Esc           β†’ close dropdown without selecting
    
  • πŸ”¬ Pipeline Inspector (P): Press P on any selected MR to toggle the side panel between MR metadata and its pipeline history. The last 5 pipeline runs are displayed with per-stage job breakdown, status icons, and execution durations:

    #9981  βœ” passed
      β–Έ test
        βœ” lint        (18s)
        βœ” unit-tests  (74s)
      β–Έ build
        βœ” build       (42s)
      β–Έ deploy
        βœ” deploy-staging (31s)
    

    Pipeline data is fetched alongside MR metadata in the same refresh cycle and persisted to disk β€” so it is immediately available on restart without an extra network call. Re-fetching only occurs when GitLab reports a new updated_at timestamp, keeping API usage minimal.

  • πŸ”Ž HEAD SHA & Pipeline Summary in Inspector: The MR metadata panel (default side panel) surfaces two additional at-a-glance fields without requiring a switch to the Pipeline view:

    • HEAD SHA β€” the abbreviated commit SHA (8 chars) of the MR's source branch tip, useful for cross-referencing with CI logs or git log.
    • Pipeline summary β€” the latest pipeline status (βœ” passed, ✘ failed, ⟳ running, …) with its total execution time and a [P] details hint to open the full pipeline inspector.

πŸ”‘ Authentication & Configuration

The application requires a GitLab project configuration and an API Personal Access Token.

Step 1: Set up environment variables (optional)

✨ Zero-config first run: If no .env file or projects.toml is present, gitlab-tracker will interactively prompt you for the required values on first launch and persist them automatically to projects.toml. No manual file setup is needed.

 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚              FIRST-RUN INTERACTIVE ONBOARDING            β”‚
 β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
 β”‚ 🌐 GitLab URL [https://gitlab.com]: _                    β”‚
 β”‚ πŸ”’ GitLab Project ID: _                                  β”‚
 β”‚ 🏷️  Project name (optional): _                           β”‚
 β”‚ πŸ”‘ GitLab Personal Access Token: _                       β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

For teams and CI pipelines, you can still pre-configure everything via a .env file to skip the prompts entirely:

  1. Copy the provided template to create your local .env file:

    cp .env.example .env
    
  2. Open .env and specify your project details:

    # Required: Your target GitLab Project ID
    GITLAB_PROJECT_ID=12345678
    
    # Optional: Custom self-hosted GitLab instance (defaults to https://gitlab.com if omitted)
    GITLAB_URL=https://gitlab.my-company.com
    
    # Optional: Override token via environment variable (not recommended for disk storage)
    # GITLAB_TOKEN=glpat-xxxxxxxxxxxxxxxxxxxx
    

πŸ”„ Settings Resolution Order

Settings are resolved in the following order (highest to lowest priority):

  1. System Environment Variables & Local .env (current directory)
  2. Global .env (~/.config/gitlab-tracker/.env)
  3. projects.toml (~/.config/gitlab-tracker/projects.toml) β€” canonical config file
  4. Built-in Fallback Defaults (https://gitlab.com, ["main"] for default branch)

Upgrading from an older version? If you have a config.json from a previous release, the app performs a silent one-time migration on first startup: all settings are read from config.json, written into projects.toml, and the old file is no longer used. Nothing breaks β€” you will simply see a βœ… Project settings migrated message once.


Step 2: First-Run Interactive Onboarding & Keyring PAT Security Layer

Your GitLab personal access token is never stored in plain text.

On first launch, gitlab-tracker resolves each required value using the following priority order β€” prompting interactively only as a last resort:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    SETTINGS LOOKUP ORDER                     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ GITLAB_PROJECT_ID & GITLAB_URL                               β”‚
β”‚   1. Environment variable / .env file                        β”‚
β”‚   2. ~/.config/gitlab-tracker/projects.toml                  β”‚
β”‚   3. Interactive CLI prompt β†’ saved to projects.toml         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ GITLAB_TOKEN                                                 β”‚
β”‚   1. GITLAB_TOKEN environment variable (if set)              β”‚
β”‚   2. Native OS Keyring β€” keyed per GitLab instance URL       β”‚
β”‚      (multi-tenant: each instance has its own slot)          β”‚
β”‚   3. Interactive CLI prompt β†’ saved to OS Keyring            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  1. First-Run Onboarding: If no project is configured yet, the application will prompt you interactively on first launch:

    βš™οΈ  No project configured yet. Let's set one up.
    
    GitLab URL [https://gitlab.com]: https://gitlab.my-company.com
    GitLab Project ID: 12345678
    Project name (optional label): My Project
    βœ… Project saved to projects.toml!
    
    πŸ”‘ No GITLAB_TOKEN found in environment or system Keyring.
    Please enter your GitLab Personal Access Token: glpat-xxxxxxxxxxxx
    βœ… Token securely saved to OS Keyring!
    
  2. Secure Token Persistence: The token is encrypted and handed off directly to your operating system's native secret manager:

    • Linux: GNOME Keyring / KWallet via Secret Service API
    • macOS: Apple Keychain Service
    • Windows: Windows Credential Manager
  3. Subsequent Launches: You can delete the GITLAB_TOKEN entry from your .env completely. On subsequent runs, gitlab-tracker retrieves the token silently from the OS Keyring without requiring plain-text files or manual re-entry.


Step 3: Project Configuration (projects.toml)

All settings β€” connection details, display preferences, branch lists, and label colours β€” live in a single TOML file per project:

  • Linux: ~/.config/gitlab-tracker/projects.toml
  • macOS: ~/Library/Application Support/gitlab-tracker/projects.toml
  • Windows: C:\Users\<User>\AppData\Roaming\gitlab-tracker\projects.toml

The file supports multiple projects in a [[project]] array. The active project is the first entry with active = true (or the first entry overall when none is marked).

Full annotated example

[[project]]
name = "My Company β€” Backend"
gitlab_url = "https://gitlab.my-company.com"
project_id = "12345678"
active = true

# Branches whose pipeline status appears as columns in the MR table.
default_branches = ["main", "staging"]

# Branches currently tracked in the TUI (managed automatically via Insert mode).
tracked_branches = ["main", "develop", "staging"]

# Label prefixes shown as chips in the "Labels" table column.
table_label_prefixes = ["deploy::", "review::"]

# How often the MR list is refreshed from GitLab (in seconds).
refresh_interval_secs = 900

# Activity badge thresholds (in days) shown in the Context Inspector.
activity_recent_days = 2   # 🟒 Active if updated within N days
activity_stale_days  = 7   # πŸ”΄ Stale if not updated for N days

# Tech-stack calibration for the review-difficulty score.
# Weighted formula: (additions + deletions) Γ— 0.8 + files_changed Γ— 0.2
[project.complexity_profile]
name            = "Drupal"
easy_threshold  = 300    # score below this β†’ 🟒 Easy
hard_threshold  = 2000   # score above this β†’ πŸ”΄ Complex

# Which optional columns are visible in the MR table.
[project.visible_columns]
activity      = false
target_branch = false
labels        = false
milestone     = true
notes         = false
tracker_ticket = true
diff_stats    = true   # "Effort" column (🟒/🟑/πŸ”΄ chip badge based on diff size)

# Label colour overrides β€” exact names or wildcard patterns (e.g. "deploy::*").
# Accepted colour values: named colours ("red", "cyan", "dark_gray", …) or hex codes ("#D32F2F").
[project.label_colors]
"bug"              = { bg = "red",      fg = "white" }
"fix"              = { bg = "red",      fg = "white" }
"deploy::*"        = { bg = "green",    fg = "black" }
"review::*"        = { bg = "cyan",     fg = "black" }
"review::approved" = { bg = "magenta",  fg = "white" }
"size::*"          = { bg = "dark_gray", fg = "white" }


# Add more projects below β€” only the one with `active = true` is loaded at startup.
# [[project]]
# name       = "My Company β€” Frontend"
# gitlab_url = "https://gitlab.my-company.com"
# project_id = "87654321"
# active     = false

Complexity profile presets:

Tech stack easy_threshold hard_threshold Rationale
Drupal 300 2000 Lots of YAML/config files β€” verbose but lightweight to review
Symfony / PHP 200 1200 Denser business logic, typically smaller PRs
Java / Spring 100 600 Highly logic-dense lines; verbosity adds review cost
TypeScript / React 150 900 JSX inflates line counts but remains readable
Go 150 800 Concise but each line carries weight
Generic (default) 200 1000 Conservative baseline for mixed stacks

Optional table columns β€” all hidden by default. Enable them per project under [project.visible_columns]:

Key Column shown
activity Activity β€” 🟒 Active / 🟑 Slowing / πŸ”΄ Stale badge
target_branch Target β€” the branch the MR merges into
labels Labels β€” filtered label chips (respects table_label_prefixes)
milestone Milestone β€” the associated milestone title
notes Notes β€” total comment count (πŸ’¬ N in yellow when non-zero)
diff_stats Effort β€” 🟒 / 🟑 / πŸ”΄ chip badge calibrated to complexity_profile
tracker_ticket Ticket β€” linked tracker ticket ID + status (requires a tracker plugin)

Activity badge thresholds control the colour-coded indicator next to the Updated field in the Context Inspector:

Badge Meaning Condition
🟒 Active Updated recently elapsed days < activity_recent_days
🟑 Slowing Activity slowing down between the two thresholds
πŸ”΄ Stale No recent activity elapsed days β‰₯ activity_stale_days
⬛ Unknown Timestamp unavailable β€”

πŸ”” How Desktop Notifications Work

Notifications fire on four events (new branch, MR updated, mergeability changed, milestone changed) and include a clickable "Open MR" button that opens the MR in your default browser. Change notifications are suppressed during the initial sync to avoid spurious alerts on restart.

See gitlab-tracker-notify/README.md for the full event reference, platform support details, and feature flags.


🌿 How Branch Resolution Works

Tracked branches (the columns shown in the MR table) are resolved in this priority order at startup:

  1. tracked_branches in projects.toml β€” the canonical source, written automatically by the TUI whenever you add or remove a branch in Insert mode.
  2. branches in tracker_<hash>.json β€” legacy field from older versions, migrated silently to projects.toml on first startup and never written again.
  3. default_branches in projects.toml β€” used on the very first run before any branch has been tracked interactively.

πŸ“¦ Installation

Prerequisites

Before installing, ensure the following system dependencies are present:

Platform Requirement Notes
Linux libdbus-1-dev, pkg-config Required for OS Keyring (Secret Service API) and desktop notifications
macOS β€” Uses native Apple Keychain β€” no extra dependencies
Windows β€” Uses native Windows Credential Manager β€” no extra dependencies

Linux (Debian / Ubuntu):

sudo apt install libdbus-1-dev pkg-config

Linux (Fedora / RHEL):

sudo dnf install dbus-devel pkgconf

Linux (Arch):

sudo pacman -S dbus pkgconf

Recommended β€” Install from crates.io

The simplest way to install gitlab-tracker if you have Rust (1.80+) available:

cargo install gitlab-tracker

This downloads, compiles, and installs the latest published release directly from crates.io into ~/.cargo/bin/. No cloning required.

Pre-built Binaries

If you prefer not to compile, download the latest pre-compiled binary for your architecture from the Releases Page and place it somewhere on your $PATH.

Building from Source

For development or to test unreleased changes, clone the repository and build manually:

git clone git@github.com:julien-langlois/gitlab-tracker.git
cd gitlab-tracker

# Build optimized release executable (builds all workspace members)
cargo build --release

# Optional: install binary globally to ~/.cargo/bin/
cargo install --path gitlab-tracker

# Build without desktop notifications (headless / CI environments)
cargo install --path gitlab-tracker --no-default-features

Once installed via any of the methods above, launch the dashboard from any terminal folder:

gitlab-tracker

⌨️ Dashboard Navigation & Shortcuts

The dashboard operates in two keyboard modes, inspired by vim:

🟦 Normal Mode (default)

Shortcut keys are active. The input field is passive.

Shortcut Action
? Open help popup β€” lists all registered shortcuts by section (any key to close)
i or / Enter Insert mode β€” focus the input field
β–² / β–Ό or k / j Navigate rows in the table
Tab Cycle focus between panes: Dashboard β†’ Inspector β†’ Tracker β†’ Dashboard (Tracker pane only when a ticket is linked)
T When focus is on Dashboard or Inspector: jump to Tracker pane. When already on Tracker: open ticket URL in browser
P Inspector pane focused: cycle MR Info ↔ Pipelines. Tracker pane focused: toggle Ticket Info ↔ Time Log
L Log time on the linked tracker ticket (only when a tracker plugin is configured)
C Open column picker β€” toggle optional columns on/off
O Open selected MR in your default web browser
R Force immediate network refresh for all MRs
s Cycle sort column (Updated β†’ ID β†’ Milestone β†’ Title β†’ …)
S Toggle sort direction (ascending / descending)
Space Toggle flag β˜… on the selected MR β€” persisted across restarts
F Open filter picker (state, mergeability, notes, milestone, assignee…)
Del Delete selected MR row
Esc Quit dashboard

🟩 Column Picker Mode

Opened with C. The table border turns cyan as a visual indicator.

Shortcut Action
β–² / β–Ό or k / j Navigate the column list
Space Toggle the highlighted column on/off
Enter or Esc Close the picker β€” changes are saved immediately to projects.toml

🟨 Insert Mode

The input field has exclusive focus. All printable keys feed the field β€” shortcuts are suspended. The input bar turns yellow as a visual indicator.

Shortcut Action
142 + Enter Add MR ID !142 to tracking
staging + Enter Add branch staging to target columns
-142 + Enter Remove MR ID !142 from tracking
-staging + Enter Remove branch column staging
@name Filter milestones matching name β€” opens autocomplete dropdown
Enter Submit input, or confirm highlighted milestone suggestion
Esc Close autocomplete dropdown, or cancel and return to Normal mode

🏁 Milestone Autocomplete (Insert Mode)

When the input starts with @, a dropdown appears above the input bar listing all active/upcoming milestones fetched from GitLab. The list is filtered in real time as you type.

Shortcut Action
↑ / ↓ or Shift+Tab / Tab Navigate suggestions
Enter Confirm selection β€” bulk-adds all open MRs from the milestone
Esc Close dropdown without selecting

Why two modes? Branch names starting with s, S, p, P, o, O, r or R would otherwise collide with shortcut keys. Insert mode guarantees the full branch name is captured without interference.


πŸ—οΈ Project Architecture

This project is structured as a Cargo workspace with four crates:

gitlab-tracker/                  # Binary crate β€” TUI orchestrator
└── src/
    β”œβ”€β”€ main.rs          # Entry point: wires providers, calls build_tracker_colors(), event loop
    β”œβ”€β”€ app.rs           # State machine, InputMode, ActiveFilter, row navigation & sort logic
    β”œβ”€β”€ config.rs        # Label filtering, wildcard matching, parse_color(), VisibleColumns & activity badge
    β”œβ”€β”€ models.rs        # Strongly-typed API DTOs & runtime event types
    β”œβ”€β”€ gitlab.rs        # Async network handling & rate-limit semaphores
    β”œβ”€β”€ events.rs        # Keyboard & mouse event dispatch (Normal / Insert mode routing)
    β”œβ”€β”€ storage.rs       # OS Keyring interface & XDG state/config persistence
    β”œβ”€β”€ utils.rs         # Fuzzy matching algorithmic utilities
    β”œβ”€β”€ demo.rs          # Demo mode with pre-populated mock data (screenshots & CI)
    β”œβ”€β”€ shortcuts_core.rs# inventory::submit! β€” built-in keyboard shortcut block (Core section)
    β”œβ”€β”€ filters_core.rs  # inventory::submit! β€” built-in filter definitions (state, mergeability, …)
    β”œβ”€β”€ columns_core.rs  # inventory::submit! β€” built-in column definitions (activity, labels, …)
    └── ui/
        β”œβ”€β”€ mod.rs       # Root layout renderer & input bar (mode-aware)
        β”œβ”€β”€ table.rs     # Main MR table widget
        β”œβ”€β”€ inspector.rs # Upper-right pane: MR metadata & pipeline history
        └── tracker.rs   # Lower-right pane: linked ticket details & time log (TrackerLabelColors)

gitlab-tracker-core/             # Library crate β€” shared trait contracts, zero UI dependency
└── src/
    β”œβ”€β”€ lib.rs           # Re-exports: TrackerProvider, LinkedTicket, FilterDef, ColumnDef, …
    β”œβ”€β”€ provider.rs      # TrackerProvider trait + all shared domain types
    β”‚                    #   LinkedTicket: flat ticket data (type, priority, version, progress, …)
    β”‚                    #   LabelColorMaps: raw (String, String) badge colour maps β€” no ratatui
    β”œβ”€β”€ filters.rs       # FilterDef contract + MrSnapshot + inventory::collect! registry
    β”œβ”€β”€ columns.rs       # ColumnDef contract + inventory::collect! registry
    └── shortcuts.rs     # ShortcutBlock / ShortcutFactory + inventory::collect! registry

gitlab-tracker-notify/           # Library crate β€” desktop notification plugin
└── src/
    └── lib.rs           # notify-rust integration (no-op stubs when feature `desktop` is disabled)

gitlab-tracker-redmine/          # Library crate β€” optional Redmine integration plugin
└── src/
    β”œβ”€β”€ lib.rs           # RedmineProvider: implements TrackerProvider + label_colors()
    β”œβ”€β”€ client.rs        # Async Redmine REST API client (issue, time entries, activities)
    β”œβ”€β”€ config.rs        # RedmineConfig: YAML load/save, LabelColorConfig, onboarding prompt
    β”œβ”€β”€ detector.rs      # Regex-based ticket ID detector (title & description)
    β”œβ”€β”€ keyring.rs       # Secure token retrieval via OS Keyring
    β”œβ”€β”€ shortcuts.rs     # inventory::submit! β€” Redmine keyboard shortcut block
    β”œβ”€β”€ filters.rs       # inventory::submit! β€” "Has linked ticket" filter definition
    └── columns.rs       # inventory::submit! β€” "Tracker" column definition

Optional Feature Flags

Feature flag Default Effect
notifications βœ… enabled Desktop notifications via notify-rust
redmine ❌ disabled Redmine ticket & time-tracking integration (see gitlab-tracker-redmine)

Tracker Plugins

gitlab-tracker supports optional external tracker integrations (Redmine, and future providers such as Jira or Linear) through a plugin architecture based on the TrackerProvider trait defined in gitlab-tracker-core.

When a tracker plugin is configured, the dashboard is enriched with:

  • Linked ticket display in the Inspector β€” subject, type, priority, status, assignee, target version, start date, progress bar, and time tracking (estimate / spent / remaining)
  • Coloured badges for Type and Priority β€” colours are fully configurable per-label in the plugin's config file (no hardcoded values β€” works with any language or custom workflow)
  • Time Log view (P Γ— 2) β€” chronological list of time entries for the linked ticket, auto-refreshed on MR navigation
  • Log time (L) β€” submit a new time entry directly from the TUI
  • Tracker column in the table (toggleable via C)

Each plugin lives in its own crate and is activated via a Cargo feature flag. See the plugin's own README for setup instructions:

Plugin Feature flag Documentation
Redmine --features redmine gitlab-tracker-redmine/README.md

πŸ“„ License

Distributed under the MIT License. See LICENSE for details.