Lean Flow Metrics
Extract flow metrics from Jira and Azure DevOps. A fast, self-contained Rust CLI.
Lean Flow Metrics is the Rust rewrite of Actionable Metrics. It connects to your Jira or Azure DevOps instance and produces ActionableAgile-compatible exports — cycle time, throughput, work item age, an interactive HTML dashboard, and more — from a single dataset configuration file. It ships as one small container image with zero cloud dependencies.
Why Lean Flow Metrics?
Privacy First - All processing happens locally on your machine. Your data never leaves your infrastructure. No cloud services, no third-party tracking.
One Fast Binary - A single statically-built Rust CLI. No Python runtime, no virtualenv, no interactive shell to manage — point it at a config file and it runs.
Two Sources, One Config - The same dataset YAML drives both Jira (JQL) and Azure DevOps (WIQL) extracts.
Flexible Export - CSV for ActionableAgile Analytics, throughput CSV, a self-contained HTML dashboard, blocked-history and Shipcast exports — all toggled from the dataset config.
Quick Start
Prerequisites
Before continuing, ensure you have completed the following from the main installation guide:
- Git installed and configured
- Docker or Podman installed and running
- Repository cloned via
git clone
If you haven’t completed these steps, return to the main page and follow the Requirements and Getting Started sections first.
1. Navigate to Lean Flow Metrics Directory
cd lean-flow-metrics2. Configure Environment
cp .env.example .envEdit .env and configure the required settings:
# Container image link (required)
IMAGE_NAME=registry.gitlab.com/tomasz-feliksik/flow-metrics-base/lean-flow-metrics
# Container image version (pinned; `git pull` brings a new one)
RELEASE=9.0.0
# Authentication token (required for accessing the registry)
ACCESS_TOKEN=glpat-xxxxxxxxxxxxxxxxxxxxRELEASE is pinned to a specific
version, not latest, so you get the release this
checkout was tested against and upgrading is a deliberate
git pull — never a silent change under a moving tag. Set
RELEASE=latest if you would rather track the newest
release, but be aware a major version can change the config schema
underneath you (9.0.0 did — see Upgrading).
Optional settings:
CONTAINER_RUNTIME- Specifydockerorpodman(auto-detected if not set)HOST_DIR- Directory to store configuration & output data (defaults to current directory)
How the container sees your files: the runner mounts the current directory into the container at
/data, which is the CLI’s working directory. Every path you pass (config files, output) is relative to this directory.
3. Generate Example Configurations
Linux/macOS:
./run_lean_flow_metrics.sh --generate-config-examplesWindows:
.\run_lean_flow_metrics.cmd --generate-config-examplesWhy
.cmdand not.ps1on Windows? Stock Windows refuses to run.ps1files (running scripts is disabled on this system) and opens them in Notepad when double-clicked.run_lean_flow_metrics.cmdis a one-line launcher that hands the same PowerShell script to PowerShell with-ExecutionPolicy Bypassfor that launch only — it changes no setting on your machine, and it forwards every argument you give it. So you never have to touch execution policy, and the command line is identical to the Linux and macOS one. Prefer the.ps1directly? It works unchanged in a PowerShell window where scripts are allowed; if yours blocks them, runpowershell -ExecutionPolicy Bypass -File .\run_lean_flow_metrics.ps1 [args], or ask your IT administrator to relax the policy.
This writes annotated examples to
configurations/examples/:
dataset_config_example_jira.yamldataset_config_example_azure_devops.yaml
Running the script with no arguments prints the CLI’s help.
4. Set Up Your Source Credentials
Create a source YAML (referenced by
source_config_file in your dataset config). It declares the
source type under the source: envelope — exactly one of
jira: or azure_devops:. Copy the annotated
template and fill in your values:
# Jira:
cp credentials.jira.yaml.example credentials.yaml # match your configs' source_config_file
# or Azure DevOps:
cp credentials.azure-devops.yaml.example credentials.yamlFor Jira, the minimum is:
source:
jira:
url: https://your-company.atlassian.net # Cloud; or https://jira.your-company.com (Server/DC)
user: your.email@company.com # email (Cloud) / username (basic); empty for a Server PAT
auth_method: api_token # api_token (Cloud, default) | basic | pat (Server/DC)
token: your-jira-api-token # the secret: API token, password, or PATGenerate your Jira API token at your Atlassian
account. The field names are validated strictly (unknown keys are
rejected), so use token — not pass. Any value
may reference an environment variable as ${NAME}
(e.g. token: ${JIRA_TOKEN}), so the secret can live in
.env instead of the YAML. See
credentials.jira.yaml.example (or
credentials.azure-devops.yaml.example) for the full
annotated schema: connection tuning, the fields: list, and
the optional cache: block.
Two field vocabularies, and they are not interchangeable. A source YAML’s
fields:list takes your tracker’s own ids (issuetype,System.Title) because that list is what goes on the wire. A dataset YAML’sfields:list takes canonical names (issue_type,story_points,links). Getting them the wrong way round is caught before any fetch, with an error naming what the source can actually produce.
Comments and links are opt-in on Jira. They are the expensive part of an issue, so neither is fetched unless you set
include_comments: true/include_links: truein the source YAML. A dataset that needs one and does not get it is a hard error naming the knob — the blocked-history report needs comments, and the Shipcast export needs links. Azure DevOps always supplies both, so nothing is needed there.
Upgrading an existing configuration? See Upgrading.
5. Configure Your Dataset
Copy an example to configurations/your_dataset.yaml and
edit it. The key fields:
name: My Team
source_config_file: source_jira.yaml # the credentials file from step 4
query: <JQL (Jira) or WIQL (Azure DevOps)>
arrival_column: In Progress
departure_column: Done
workflow:
columns: # ordered map — YAML key order = column order
Backlog: [Inbox, Prioritized]
In Progress: [In Progress, In Review]
Done: [Done]
calculation_rolling_window: 28 # days, used by every rolling-stat chart
throughput_report_window: 0 # 0 disables the throughput CSV export
generate_flow_dashboard: true # write the HTML dashboardRun --generate-config-examples (step 3) for the
fully-commented schema, including caching, blocked-signal configuration,
theming, and the Shipcast export block.
6. Generate the Exports
Linux/macOS:
./run_lean_flow_metrics.sh -c configurations/your_dataset.yamlWindows:
.\run_lean_flow_metrics.cmd -c configurations/your_dataset.yamlOutput files are written to ./output/ by default.
CLI Reference
Arguments are forwarded directly to the
lean-flow-metrics binary inside the container. Every
example below is written with the Linux/macOS runner; on Windows use
.\run_lean_flow_metrics.cmd with exactly the same
arguments.
| Flag | Description |
|---|---|
-c, --configuration <FILE> |
Dataset configuration YAML (required for a run) |
-o, --output-path <DIR> |
Output directory or full filename (default:
output) |
--datetime-suffix |
Append __YYYY-MM-DD_HH-MM to every output filename |
--generate-config-examples |
Write example dataset configs to
configurations/examples/ and exit |
--no-cache |
Bypass the dataset’s cache for this run |
--invalidate-cache |
Drop the dataset’s cache before fetching |
-l, --log-level <LEVEL> |
debug, info, warn,
error (default: info) |
--help / --version |
Print the CLI’s help / version |
Run ./run_lean_flow_metrics.sh --help (Windows:
.\run_lean_flow_metrics.cmd --help) for the full,
authoritative list.
Upgrading
The image version is pinned in .env
(RELEASE=), so git pull is what brings
a new release — it updates the pinned default and the templates
that go with it. Bump RELEASE to match, then run as
usual.
To 9.0.0
9.0.0 retires four source-YAML keys. A config still carrying one is rejected when it loads, with the replacement spelled out — nothing is silently ignored, and nothing runs against a half-understood config.
| Old key (rejected) | Replacement |
|---|---|
epic_link_field: customfield_10014 |
domain_fields: {epic_link: customfield_10014} |
story_points_field: customfield_100 |
domain_fields: {story_points: customfield_100} |
parent_field: System.Parent (ADO) |
domain_fields: {parent: System.Parent} — or delete |
field_mappings: (Jira) |
delete it; select the field by its native id |
domain_fields: is one map naming which native field
backs a domain concept on your instance. Jira accepts
epic_link and story_points; Azure DevOps
accepts parent, which already defaults to
System.Parent — so most ADO users can simply delete the old
key rather than translate it.
Two more changes to know about, neither of which needs an edit:
max_results:becamebatch_size:. The old spelling still parses, so nothing breaks. It now sizes only the by-key item requests, not the key-listing pass.- The work-item cache refills once. Cache invalidation now accounts for which field backs which concept, so the first run after upgrading refetches. Expected, and nothing to do.
Also new in 9.0.0, and worth a look: links is a
selectable dataset column, creator joins the canonical
vocabulary, and connection failures now say what actually went wrong
(and which timeout to reach for) instead of one fixed message.
From v4.0.1 or older
The source YAML schema changed shape: the old top-level
jira: block with
server:/auth_secret: (ADO:
organization_url:/access_token:) became the
source: envelope shown above with
url:/token:. Every release after v4.0.1
rejects the old shape.
Troubleshooting
| Issue | Solution |
|---|---|
running scripts is disabled (Windows) |
Run .\run_lean_flow_metrics.cmd instead of the
.ps1 — it bypasses the policy for that launch only, and
takes the same arguments |
| Double-clicking the runner opens Notepad (Windows) | You opened the .ps1; use
run_lean_flow_metrics.cmd. It needs arguments, so run it
from a terminal rather than double-clicking |
| Docker permission errors on Linux | Add your user to the docker group |
| Cannot pull container image | Verify IMAGE_NAME and ACCESS_TOKEN in
.env |
IMAGE_NAME is not set error |
Set IMAGE_NAME in your .env file |
missing --configuration error |
Pass -c configurations/your_dataset.yaml |
moved to 'domain_fields' error |
A retired source-YAML key after upgrading to 9.0.0 — see Upgrading; the error names the fix |
| A field exports an empty column | 9.0.0 rejects unproducible fields up front instead; if you still see
one, check the source’s fields: list |
| Output files owned by root (Linux) | The runner already maps your host user via --user;
ensure you ran the .sh script (not a raw
docker run) |
| Connection failures | Check internet connectivity and firewall settings |
| Container won’t start | Ensure Docker or Podman is running |