Skip to main content

revtrend

Track numeric metrics per git commit, such as image size, binary size, test duration or coverage, and watch them change over time in Grafana.

Measurements are keyed by commit SHA. Which commits belong to a branch is answered from git, not from timestamps: a branch shows its own commits plus the history it was forked from, so a commit on the default branch counts for every branch forked after it. CI can compare a pull request against its target to make changes visible in reviews. revtrend never fails a build; thresholds are up to you.

Quick start

Install the CLI and point it at your revtrend service:

pipx install revtrend
export REVTREND_URL=https://revtrend.example
revtrend login

login prints a URL to open in any browser, so it also works over ssh. The session is cached in ~/.cache/revtrend/sessions.json and refreshed automatically.

Inside a checkout of your project, push a measurement for a commit and compare it with a base branch:

revtrend push --label machine:amd64 --label image:core-image \
    image.size_bytes=81234432 image.files=5321
revtrend compare --base release/1.0
3f2a91c0 vs. release/1.0 (merge base 8d0e4b12, tip c71a5e09)

series                                            baseline      head     delta  delta %
image.files{image=core-image,machine=amd64}           5298      5321       +23  +0.4341
image.size_bytes{image=core-image,machine=amd64}  80123904  81234432  +1110528   +1.386

The project is found by matching the git remotes of the current repository against the projects on the service. Pass --project to choose one explicitly.

Pushing

revtrend push [--sha REV] [--label KEY:VALUE]... [--run-id ID] NAME=VALUE...
  • --sha defaults to HEAD and is resolved in the current repository. The commit must exist in the remote; local-only commits are rejected.
  • --label applies to all metrics of the push. For metrics with different labels, push once per label set.
  • All values of one push form a run. The run id defaults to $REVTREND_RUN_ID, or manual-<user>-<timestamp> if that isn't set. Pushing the same run id again is a no-op, so pass --run-id to make a retry safe.

To backfill history, check out an old commit, measure, and push.

Comparing

revtrend compare [--head REV] [--base REF] [--metric NAME]... [--label KEY:VALUE]... [--json]

compare finds where --head (default HEAD) forked from --base (default: the remote's default branch) and compares head against the nearest measured commit at or before that fork point. A (N back) note in the baseline column means the fork point itself wasn't measured. --json prints the raw response for scripts.

In CI

The CLI reads its settings from the environment, so a CI job only needs three variables:

Variable
REVTREND_URL service URL
REVTREND_TOKEN an OIDC token the service accepts, used instead of login
REVTREND_RUN_ID a unique id for the build, e.g. the build URL

Example for Jenkins with the OpenID Connect Provider plugin:

environment {
    REVTREND_URL = 'https://revtrend.example'
    REVTREND_RUN_ID = "${env.BUILD_URL}"
}
steps {
    withCredentials([string(credentialsId: 'revtrend-oidc', variable: 'REVTREND_TOKEN')]) {
        sh '''
            pipx install revtrend
            revtrend push --label machine:amd64 --label image:core-image \
                image.size_bytes=$(stat -c %s core-image.wic)
            if [ -n "$CHANGE_TARGET" ]; then
                revtrend compare --base "$CHANGE_TARGET" --json > revtrend-compare.json
            fi
        '''
    }
}

What to do with the comparison, such as posting a PR comment or applying thresholds, is up to the pipeline.

Measure the target branches too, after every merge, not only pull requests. Otherwise compare has no baseline to compare against.

Runs from CI and from developers are kept apart: the service marks each run ci or manual based on the token's issuer. If a commit has CI runs, manual runs of the same series are ignored, so manual pushes can fill gaps but never distort CI data.

Concepts

Metrics and labels. Metric names use [a-z0-9_.] (e.g. image.size_bytes), label keys use [a-z0-9_], and label values are free text. Use labels for dimensions such as machine or test suite instead of encoding them into the metric name. A metric and its labels form a series. There is nothing to declare up front: a series exists as soon as its first value is pushed.

Runs and aggregation. A commit can have several runs, from CI retries or rebuilds. Queries reduce them to one value per commit, using median by default (latest, min, max and mean are also available).

History. A branch's history is its first-parent history, so with merge commits the default branch shows one point per merged pull request. The x-axis is the committer time.

Grafana

Use the Infinity datasource with OAuth2 client credentials for a token with the reader role. A time series panel can query:

/api/v1/projects/$project/series?metric=$metric&ref=$ref&base=$base&labels=machine:$machine&from=${__from}&to=${__to}

Each row has time, sha, subject, series (use it for grouping), value and on_branch. on_branch is true for commits on ref but not on base, so you can highlight a branch's own commits. Dashboard variables can be filled from /api/v1/projects, /api/v1/projects/$project/refs?active_since=90d and /api/v1/projects/$project/metrics.

Running the service

pipx install 'revtrend[server]'
revtrend-server --config /etc/revtrend/config.toml

See contrib/config.example.toml and contrib/revtrend.service. The service keeps a SQLite database and a bare mirror of each project's repository, which it fetches periodically and whenever it sees an unknown commit. It only reads from the remote.

Every API request needs a bearer JWT from one of the configured issuers. Rules map token claims to a role per project:

Role Allows
reader series, compare, discovery
writer reader + push
admin writer + delete

Each issuer has a fixed source (ci or manual). Runs record the submitter as <issuer name>:<sub>; set display_claims on an issuer (e.g. ["preferred_username", "name"]) to also store a readable name.

/metrics (Prometheus) is unauthenticated, so don't expose it through the reverse proxy.

HTTP API

Endpoint Role
POST /api/v1/projects/{project}/measurements writer push one run for a commit
GET /api/v1/projects/{project}/series reader flat rows for Grafana (metric, ref, base, from, to, labels, agg)
GET /api/v1/projects/{project}/compare reader head vs. nearest measured commit from the merge base with base
GET /api/v1/projects any projects the caller can read
GET /api/v1/projects/{project}/refs reader branches (active_since, match)
GET /api/v1/projects/{project}/metrics reader metric names and label values
DELETE /api/v1/projects/{project}/measurements admin delete by run_id or metric
GET /auth/cli/{config,callback,poll} none PKCE code relay for revtrend login
GET /healthz, GET /metrics none health and Prometheus metrics

Pushing without the CLI:

curl -fsS -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
    --data '{"sha": "<40 hex>", "run_id": "'"$BUILD_URL"'", "metrics": [
              {"name": "image.size_bytes", "value": 81234432,
               "labels": {"machine": "amd64", "image": "core-image"}}]}' \
    https://revtrend.example/api/v1/projects/myproject/measurements

Development

uv sync
uv run pytest
uv run ruff check
uv run ruff format --check

Metadata

Release files for revtrend 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for revtrend 0.2.0
File Size Uploaded Provenance Actions
revtrend-0.2.0.tar.gz 28.7 kB Without provenance Details for revtrend-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for revtrend 0.2.0
File Interpreter ABI Platform Actions
revtrend-0.2.0-py3-none-any.whl Python 3 none any Details for revtrend-0.2.0-py3-none-any.whl

Total release size: 60.6 kB

Release files / revtrend-0.2.0.tar.gz

Download URL revtrend-0.2.0.tar.gz
Size 28.7 kB
Tags Source
SHA-256 checksum
How to use checksums
8b341ad9292866f8d19012a3e0f9c85829dd793327b4f9d53d3f42d5cee62796
BLAKE2b-256 checksum
How to use checksums
7ace3e26924e1814d75edc9e71edad41117b4827769a65aa92d4ce3bf7524e9b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.8 {"installer":{"name":"uv","version":"0.13.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / revtrend-0.2.0-py3-none-any.whl

Download URL revtrend-0.2.0-py3-none-any.whl
Size 31.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
06ceb797f9ce20a9a0e24c25bf87a37dd907d07c4fcbb87f89052f48e087f9c4
BLAKE2b-256 checksum
How to use checksums
a62a68faa8edeb4be43936a5e80b1cad45b745630a729dc820794f7db7137ab3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.8 {"installer":{"name":"uv","version":"0.13.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for revtrend 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page