@build-tracker/server

Adding the @build-tracker/server package will install a binary available as bt-server. It can be run with yarn bt-server or npx bt-server

Important note: It is recommended to do set up your Build Tracker server & application either in a new repository or in an isolated workspace away from the code that you wish to track. This will help you avoid configuration conflicts as well as build-time issues between your app and Build Tracker itself.

Install

yarn add @build-tracker/server@latest
# or
npm install --save @build-tracker/server@latest

To list all commands and help, run yarn bt-server --help

Configuration

The Build Tracker application is easily configured using a cosmiconfig compatible file.

Starting from the current working directory, it will look for the following possible sources, in this order:

  1. a build-tracker property in your package.json
  2. a .build-tracerrc file
  3. a build-tracker.config.js file exporting a JS object

The ..build-trackerrc file (without extension) can be in JSON or YAML format.

Alternately, you can add a filename extension to designate JSON, YAML, or JS format: .build-trackerrc.json, .build-trackerrc.yaml, .build-trackerrc.yml, .build-trackerrc.js. You may want to use an extension so that your text editor can better interpret the file, and help with syntax checking and highlighting.

Once one of these is found and parsed, the search will stop and that object will be used.

The configuration search can also be short-circuited by passing a --config argument with a path to your configuration file.

Basic configuration

Please note that options not denoted with a ? are required and must be provided.

module.exports = {
url: 'https://my-url', // required
};

artifacts?: {}

Allows setting budgets for single or groups of artifacts, as well as filtering out and hiding artifacts from viewers.

artifacts.budgets?: ArtifactBudgets

An object map of artifact names to arrays of budgets for each artifacts.

module.exports = {
artifacts: {
budgets: {
main: [{ level: 'error', sizeKey: 'gzip', type: 'size', maximum: 150000 }],
},
},
};

Artifact budgets can also be given a special key, '*', that will apply to all artifacts in every build and be merged with any specific budgets:

module.exports = {
artifacts: {
budgets: {
main: [{ level: 'error', sizeKey: 'gzip', type: 'size', maximum: 150000 }],
// Warn on any artifact that grows more than 50%
'*': [{ level: 'warn', sizeKey: 'gzip', type: 'percent_delta', maximum: 0.5 }],
},
},
};
artifacts.filters?: Array<RegExp>

An array of regular expressions used to hide artifacts from display. This is typically not needed, unless you have artifacts generated by systems that really don't need to be checked.

For example, filter out all translation bundles except for the default:

module.exports = {
artifacts: {
// Hide all packages matching 'i18n/.*', except for 'i18n/en'
filters: [/^i18n\/(?!en$).*$/],
},
};
artifacts.groups?: Array<Group>

Have Build Tracker group specific artifacts together so you can track and budget on the sum of the artifacts. This is a great option when you have synchronous and asynchronous artifacts, allowing you to determine the byte-size required for initial page loads.

interface Group {
artifactNames: Array<string>;
artifactMatch?: RegExp;
budgets?: Array<Budget>;
name: string;
}
module.exports = {
artifacts: {
groups: [
{
// An array of strings that match your artifacts
artifactNames: ['main', 'shared', 'vendor'],
// Optional, a regular expression to match artifact names
artifactMatch: /^i18n/,
// An array of budgets
budgets: [{ level: 'error', sizeKey: 'gzip', type: 'size', maximum: 150000 }],
// A name for the group
name: 'Initial',
},
],
},
};

budgets?: Array<Budget>

Separate from individual artifacts, budgets can be set across the sum of all artifacts.

enum BudgetLevel {
WARN = 'warn',
ERROR = 'error',
}
enum BudgetType {
DELTA = 'delta',
PERCENT_DELTA = 'percentDelta',
SIZE = 'size',
}
interface Budget {
level: BudgetLevel;
sizeKey: string;
type: BudgetType;
maximum: number;
}
module.exports = {
budgets: [{ level: 'error', sizeKey: 'brotli', type: 'size', maximum: 1000000 }],
};

hideAttribution?: boolean = false

Set to true to hide the attribution and link in the Drawer.

name?: string = 'Build Tracker'

If provided, replaces the title Build Tracker in the top application bar.

url: string

The URL that your server is running at. Please set this to ensure the application is able to fetch from your database correctly.

module.exports = {
url: 'https://build-tracker-demo.herokuapp.com',
};

Server & application options

defaultBranch?: string = 'main'

Your application will graph this branch by default and ignore builds from other branches.

This is a useful setting if you use and track a branch that is not main in your git repository.

dev?: boolean = false

Set the server into development mode. This is only recommended if you are contributing to Build Tracker.

handlers?: Handlers

Run any custom code after a build has been inserted into the database. The comparator object you receive will compare the new build against the build associated with its own parentRevision.

interface Handlers {
onBuildInsert?: (comparator: Comparator) => Promise<void>;
}

port?: number

defaultSizeKey?: string

The default size type used to represent files. For example, gzip or brotli.

Database integration

The following options should generally be handled by a Build Tracker plugin. You can always override them or write your own custom queries and DB setup if you like.

setup?: () => Promise<boolean>

If provided, this function is run before the application starts running. Most plugins will do database creation or upgrades during this phase.

queries: Queries

The Build Tracker server API uses these methods to fetch and insert builds in the database.

interface Queries {
build: {
byRevision: (revision: string) => Promise<Build>;
insert: (build: Build) => Promise<string>;
};
builds: {
byRevisions: (revisions: Array<string>) => Promise<Array<Build>>;
byRevisionRange: (startRevision: string, endRevision: string) => Promise<Array<Build>>;
byTimeRange: (startTimestamp: number, endTimestamp: number, branch: string) => Promise<Array<Build>>;
recent: (limit: number, branch: string) => Promise<Array<Build>>;
};
}

Securing your API

It's easy to see that the API POST endpoint /api/builds is left completely open. To secure this and ensure that bad actors don't have an easy way to write random data to your database, you can do the following:

When running your server, ensure you have an environment variable set, BT_API_AUTH_TOKEN=my-super-secret-token.

BT_API_AUTH_TOKEN=my-secret-token bt-server run

When your CI system uploads new builds using the bt-cli upload-build command, ensure the token is set in that environment as well.

BT_API_AUTH_TOKEN=my-secret-token bt-cli upload-build

Commands

The bt-server command has a few commands available. For the most part, you should only ever need run.

run

Run the server application. Remember to ensure that an API token environment variable is set to add a layer of security to the API.

BT_API_AUTH_TOKEN=my-secret-token bt-server run

setup

Run initial database setup. This method runs only the setup function provided by the Database Integration section of this document.

This is typically unnecessary to run individually, as run will also execute this command before starting the application.

seed dev-only

For development purposes only! This function will seed your database with fake data so that it's easier to build and test new plugins. This command also requires the package @build-tracker/fixtures, which is not installed by default. You will need to install it manually.

version

Output the version number of the bt-server.