gitlab-tracker-redmine 0.3.2

Optional Redmine integration plugin for gitlab-tracker
# gitlab-tracker-redmine

Optional Redmine integration plugin for [gitlab-tracker](../README.md).

Implements the `TrackerProvider` trait from `gitlab-tracker-core` to detect Redmine ticket references in MR titles and descriptions, and enrich the TUI with ticket details and time tracking.

---

## Features

* **Linked ticket display:** subject, status, author, and assignee shown in the Inspector's MR Info view.
* **Time Log view (`P`):** press `P` twice to reach the **Time Log** view:
  * A progress bar comparing time spent vs. the ticket's estimate.
  * The full list of time entries (date, user, activity, duration, comment) fetched live from Redmine.
  * Navigating between MRs with ``/`` while on this view automatically refreshes the entries for the newly selected ticket.
* **Log time (`L`):** open a popup to submit a new time entry directly to Redmine — select the activity category, enter a duration (e.g. `1h30`, `90m`, `1.5h`), optionally add a comment, and confirm with `Enter`.
* **Tracker column** in the main table (toggleable via `C`) — shows ticket ID, status, and spent/estimated time at a glance.

---

## Enabling at Build Time

The `redmine` feature flag must be explicitly passed when building or installing:

```bash
# Build from source
cargo build --release --features redmine

# Install from source
cargo install --path gitlab-tracker --features redmine

# Install from crates.io
cargo install gitlab-tracker --features redmine
```

> Without the flag, this crate is not compiled and there is zero runtime overhead.

---

## Configuration

On first launch with the feature enabled, the app interactively prompts for your Redmine URL:

```text
🌐 No Redmine URL found in config or environment.
   Leave empty to disable Redmine integration.
Redmine URL: https://redmine.my-company.com
```

Leaving the prompt **empty** silently disables the integration — no error, no impact on the rest of the dashboard.

The configuration is persisted as a YAML file:

* **Linux:** `~/.config/gitlab-tracker/gitlab-tracker/redmine.yaml`
* **macOS:** `~/Library/Application Support/gitlab-tracker/gitlab-tracker/redmine.yaml`
* **Windows:** `C:\Users\<User>\AppData\Roaming\gitlab-tracker\gitlab-tracker\redmine.yaml`

You can also pre-configure via environment variable to skip the prompt entirely:

```env
REDMINE_URL=https://redmine.my-company.com
```

The generated `redmine.yaml` (edit to customise ticket detection patterns):

```yaml
redmine_url: "https://redmine.my-company.com"
ticket_patterns:
  - "#(\\d+)"
  - "(?i)(?:refs|fixes|closes|resolves)\\s+#(\\d+)"
  - "/issues/(\\d+)"
```

| Field | Description |
| :--- | :--- |
| `redmine_url` | Base URL of your Redmine instance (no trailing slash) |
| `ticket_patterns` | Regex patterns used to detect ticket IDs in MR titles/descriptions — capture group 1 must match the numeric ID |

---

## API Token

The Redmine personal API token follows the same secure lookup chain as the GitLab token — it is **never stored in plain text**:

```text
1. REDMINE_TOKEN environment variable (if set)
2. Native OS Keyring (GNOME Keyring / macOS Keychain / Windows Credential Manager)
3. Interactive CLI prompt → saved to OS Keyring
```

---

## Adding a New Tracker Plugin

To add a different tracker (Jira, Linear, …), create a new crate that implements the `TrackerProvider` trait from `gitlab-tracker-core`:

```rust
#[async_trait]
impl TrackerProvider for MyProvider {
    fn name(&self) -> &'static str { "My Tracker" }
    fn detect_ticket_id(&self, title: &str, description: &str) -> Option<String> { ... }
    async fn fetch_ticket(&self, ticket_id: &str) -> Option<LinkedTicket> { ... }
    fn ticket_url(&self, ticket_id: &str) -> String { ... }
    // Optional — override for time tracking support:
    async fn fetch_activities(&self) -> Vec<Activity> { ... }
    async fn fetch_time_entries(&self, ticket_id: &str) -> Vec<TimeEntry> { ... }
    async fn log_time(&self, ticket_id: &str, entry: TimeEntryRequest) -> Result<(), String> { ... }
}
```

Then wire it in `gitlab-tracker/src/main.rs` under a new feature flag — no other file in the orchestrator needs to change.