Introduction
What
This is a system that provides answers to the following question:
Which branches did a certain GitHub pull request "land" in?
A pull request is considered landed in a branch when that branch contains that pull request's "merge commit"1.
As of 2024-02-02 GitHub does not provide an API for directly asking this question.
How
In order to provide an API that answers this question the following are obtained:
- All pull requests and their merge commits (via GitHub's GraphQL API).
- A clone of the repository.
From these, all landings are deduced and stored in a (PostgreSQL) database.
It is assumed that in the tracked branches, history is never rewritten.
Two programs are provided:
- pr-tracker-fetcher: obtains data, determines landings and persists them.
- pr-tracker-api: provides an HTTP endpoint for querying landings.
-
Note that in GitHub, a pull request has a "merge commit" even having been merged without an actual merge commit. ↩
Fetcher
Intended to be periodically executed. Takes no arguments.
Environment Variables
Reads the following environment variables.
Name | Description |
---|---|
PR_TRACKER_FETCHER_BRANCH_PATTERNS | JSON array of strings representing branch patterns to track. - ? matches a single occurrence of any character.- * matches zero or more occurrences of any character.No escape characters. |
PR_TRACKER_FETCHER_CACHE_DIR | Cache directory (for repository clone). |
PR_TRACKER_FETCHER_DATABASE_URL | PostgreSQL connection URI. |
PR_TRACKER_FETCHER_GITHUB_REPO_NAME | GitHub repository name. |
PR_TRACKER_FETCHER_GITHUB_REPO_OWNER | GitHub repository owner. |
PR_TRACKER_FETCHER_GITHUB_TOKEN | GitHub API token with read access to the repository's pull requests. |
API
Takes no arguments.
/openapi.json
/
redirects to API documentation
Environment Variables
Reads the following environment variables.
Name | Description |
---|---|
PR_TRACKER_API_DATABASE_URL | PostgreSQL connection URI. |
PR_TRACKER_API_PORT | Port to listen on. |
PR_TRACKER_TRACING_FILTER | Optional. Expected to deserialize into an EnvFilter . |
NixOS Module
services.pr-tracker.api.enable
Whether to enable pr-tracker-api.
Type: boolean
Default:
false
Example:
true
services.pr-tracker.api.package
The api package to use.
Type: package
Default:
pr-tracker.packages.api
services.pr-tracker.api.db.isLocal
Whether database is local.
Type: boolean
Default:
false
services.pr-tracker.api.db.passwordFile
Path to a file containing the database password. Contents will be appended to the database URL as a parameter.
Type: null or absolute path
Default:
null
Example:
"/run/secrets/db-password"
services.pr-tracker.api.db.urlParams
URL parameters from which to compose the PostgreSQL connection URI.
Required unless services.pr-tracker.db.createLocally
is true.
Type: null or (attribute set of string)
Default:
null
Example:
{
dbname = "pr-tracker";
host = "localhost";
port = "5432";
user = "pr-tracker";
}
services.pr-tracker.api.group
Group to run under.
Type: string
Default:
"pr-tracker-api"
services.pr-tracker.api.port
Port to listen on.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
services.pr-tracker.api.tracingFilter
Optional.
Expected to deserialize into an EnvFilter
.
Type: null or string
Default:
null
services.pr-tracker.api.user
User to run under.
Type: string
Default:
"pr-tracker-api"
services.pr-tracker.db.createLocally
Whether to create a local database automatically.
Type: boolean
Default:
false
services.pr-tracker.db.name
Automatically created local database name.
Type: string
Default:
"pr-tracker"
services.pr-tracker.fetcher.enable
Whether to enable pr-tracker-fetcher.
Type: boolean
Default:
false
Example:
true
services.pr-tracker.fetcher.package
The fetcher package to use.
Type: package
Default:
pr-tracker.packages.fetcher
services.pr-tracker.fetcher.branchPatterns
JSON array of strings representing branch patterns to track.
?
matches a single occurrence of any character.*
matches zero or more occurrences of any character.
No escape characters.
Type: list of string
Example:
[
"release-*"
]
services.pr-tracker.fetcher.db.isLocal
Whether database is local.
Type: boolean
Default:
false
services.pr-tracker.fetcher.db.passwordFile
Path to a file containing the database password. Contents will be appended to the database URL as a parameter.
Type: null or absolute path
Default:
null
Example:
"/run/secrets/db-password"
services.pr-tracker.fetcher.db.urlParams
URL parameters from which to compose the PostgreSQL connection URI.
Required unless services.pr-tracker.db.createLocally
is true.
Type: null or (attribute set of string)
Default:
null
Example:
{
dbname = "pr-tracker";
host = "localhost";
port = "5432";
user = "pr-tracker";
}
services.pr-tracker.fetcher.githubApiTokenFile
Path to a file containing a GitHub API token with read access to the repository’s pull requests.
Type: absolute path
Example:
"/run/secrets/github-api.token"
services.pr-tracker.fetcher.group
Group to run under.
Type: string
Default:
"pr-tracker-fetcher"
services.pr-tracker.fetcher.onCalendar
When to run the fetcher. This is a systemd timer OnCalendar
string, see
systemd.time(7)
for a full specification.";
Type: string
Example:
"daily"
services.pr-tracker.fetcher.repo.name
GitHub repository name.
Type: string
Example:
"nixpkgs"
services.pr-tracker.fetcher.repo.owner
GitHub repository owner.
Type: string
Example:
"NixOS"
services.pr-tracker.fetcher.user
User to run under.
Type: string
Default:
"pr-tracker-fetcher"
Versioning
- This project uses Conventional Commits v1 and Semantic Versioning v2.
- With regard to versioning, the documented executables and NixOS modules are public. The libraries are private.
- This project, with all its components, is versioned as one.
Prior art
- Alyssa Ross' pr-tracker (source) Server-side rendered web app. Computes landings for a given PR on the fly by invoking Git on the backend.
- ocfox's nixpkgs-tracker (source) Client-side rendered web app. Computes landings for a given PR on the fly using GitHub api.
- Maralorn's nixpkgs-bot (source) Matrix bot that provides notification of PR landings. Periodically computes new PR landings using Git and sends messages.
All of the above are Nixpkgs specific, whereas this project is not. None of the above internally maintain a dataset of landings. None of the above currently provide an HTTP API.
Vision
Push-driven updates
The current architecture of obtaining data via polling allows instantaneous and hopefully reliable responses. However, the data can be stale.
In the future, we intend to provide fresher data by subscribing to GitHub webhooks.
Since the public cannot subscribe to GitHub webhooks, this will require deployment by the repo owner.
Event record keeping
Building upon the implementation of push-driven updates, we intend to keep track of when PRs land in branches. This requires a dataset of landings.
Webhook service
We intend to allow users to subscribe to webhook notifications of PR landings. This provides a couple of benefits over subscribing to GitHub webhooks directly:
- GitHub webhooks can only notify when a PR lands in its target branch. They cannot notify when that PR lands in other branches.
- Only repo owners can subscribe to GitHub webhooks.
Backport PRs
A backport PR is a re-application of another PR, targeting a different branch.
We intend to adopt or invent a workflow whereby in backport PRs the original PR is declared. Using that metadata, when providing landings for an original PR, we intend to also include branches on which a backport PR had landed.