Task Trackers & Work Items¶
This repo normalizes Jira issues, GitHub issues/Projects items, GitLab issues, and Linear issues into a unified WorkItem model (models/work_items.py) and computes daily aggregates + cycle times.
Jira is used to track associated project work (planning/throughput/WIP). Pull request metrics are computed from PR/MR data synced via the CLI (python cli.py sync ...) and are independent of Jira.
Provider Credentials (env vars)¶
Jira (Cloud)¶
JIRA_BASE_URL(e.g.your-org.atlassian.netorhttps://your-org.atlassian.net; normalized tohttps://)JIRA_EMAILJIRA_API_TOKENJIRA_PROJECT_KEYS(optional, comma-separated, e.g.ABC,XYZ)JIRA_JQL(optional override; if set, used as-is instead of the built-in windowed query)JIRA_FETCH_ALL(optional; set to1to fetch all issues in the project(s) regardless of date window — can be very slow)JIRA_FETCH_COMMENTS(optional; set to0to disable comment metadata ingestion; default:1)
Optional Jira field mappings (instance-specific):
- JIRA_STORY_POINTS_FIELD (e.g. customfield_10016)
- JIRA_SPRINT_FIELD (default: customfield_10020)
- JIRA_EPIC_LINK_FIELD (e.g. customfield_10014)
GitHub¶
GITHUB_TOKEN- Optional CLI override:
python cli.py sync work-items --provider github --auth "$GITHUB_TOKEN" ...
Optional Projects v2 ingestion:
- GITHUB_PROJECTS_V2 as comma-separated org_login:project_number entries, e.g.:
- GITHUB_PROJECTS_V2="myorg:3,anotherorg:12"
GitLab¶
GITLAB_TOKENGITLAB_URL(optional, default:https://gitlab.com)- Optional CLI override:
python cli.py sync work-items --provider gitlab --auth "$GITLAB_TOKEN" ...
Linear¶
LINEAR_API_KEY(required)
Optional configuration:
- LINEAR_FETCH_COMMENTS (default: true) - fetch issue comments
- LINEAR_FETCH_HISTORY (default: true) - fetch status change history for transitions
- LINEAR_FETCH_CYCLES (default: true) - fetch cycles as sprints
- LINEAR_COMMENTS_LIMIT (default: 100) - max comments per issue
Usage:
# Sync all teams
python cli.py sync work-items --provider linear --db "$DATABASE_URI"
# Sync specific team by key (e.g., ENG, PROD)
python cli.py sync work-items --provider linear --repo ENG --db "$DATABASE_URI"
Status & Type Normalization¶
Status normalization is config-driven via config/status_mapping.yaml.
Status categories¶
Normalized categories are:
- backlog
- todo
- in_progress
- in_review
- blocked
- done
- canceled
Provider-specific rules¶
The mapping file supports:
- Jira: providers.jira.statuses (maps Jira status names)
- GitHub/GitLab: providers.<provider>.status_labels (maps label names to categories)
- Linear: providers.linear.state_types (maps Linear state.type to categories)
If no label/status match exists:
- GitHub: open → todo, closed → done
- GitLab: opened → todo, closed → done
- Linear: Maps state.type directly (backlog, unstarted → todo, started → in_progress, completed → done, canceled)
Identity Mapping (optional)¶
To keep user metrics consistent across providers, populate config/identity_mapping.yaml.
Schema:
- canonical: stable identity (prefer email)
- aliases: provider-qualified logins (e.g. github:octocat) or Jira account IDs (e.g. jira:accountid:abcd123)
Team Mapping (optional)¶
To enable team filtering in Grafana, you can sync teams from various sources.
Config-based Mapping¶
Populate config/team_mapping.yaml (schema: team_id, team_name, members).
Then run:
python cli.py sync teams --path config/team_mapping.yaml
Jira Project Mapping¶
Automatically import Jira projects as teams:
python cli.py sync teams --provider jira
Running Jira work metrics¶
Jira work items are fetched via the work item sync job:
python cli.py sync work-items --provider jira --date 2025-02-01 --backfill 30 --db "clickhouse://localhost:8123/default"
Use -s/--search to filter repos by name (glob pattern), e.g.:
python cli.py sync work-items --provider github -s "org/*" --date 2025-02-01 --backfill 30 --db "clickhouse://localhost:8123/default"
Quick Jira API smoke test (curl)¶
Jira Cloud has removed GET /rest/api/3/search; use GET /rest/api/3/search/jql:
curl -sS -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
--get "https://$JIRA_BASE_URL/rest/api/3/search/jql" \
--data-urlencode "jql=(updated >= '2025-09-10' OR (statusCategory != Done AND created <= '2025-12-18')) ORDER BY updated DESC" \
--data-urlencode "maxResults=5" \
--data-urlencode "fields=key,summary,updated,status"