Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

  1. All pull requests and their merge commits (via GitHub's GraphQL API).
  2. 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:

  1. pr-tracker-fetcher: obtains data, determines landings and persists them.
  2. pr-tracker-api: provides an HTTP endpoint for querying landings.

  1. 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.

NameDescription
PR_TRACKER_FETCHER_BRANCH_PATTERNSJSON 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_DIRCache directory (for repository clone).
PR_TRACKER_FETCHER_DATABASE_URLPostgreSQL connection URI.
PR_TRACKER_FETCHER_GITHUB_REPO_NAMEGitHub repository name.
PR_TRACKER_FETCHER_GITHUB_REPO_OWNERGitHub repository owner.
PR_TRACKER_FETCHER_GITHUB_TOKENGitHub 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.

NameDescription
PR_TRACKER_API_DATABASE_URLPostgreSQL connection URI.
PR_TRACKER_API_PORTPort to listen on.
PR_TRACKER_TRACING_FILTEROptional.
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

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.