Pierre’s CI is a simple, scriptable (TypeScript), event based system, that allows you to run jobs and manage rich integrations with Pierre’s UI.
$ npm i pierre
At it’s simplest this is Pierre CI:
// .pierre/ci/helloworld.ts
import { run } from 'pierre';
export const label = 'Hello world';
export default async () => {
await run(`echo "hello world"`);
};
Every top-level file in .pierre/ci
is automatically run as a job in our
default Docker container.
To create a new job, create a new file. All top-level jobs are run in parallel. Files included in
nested directories (e.g. .pierre/ci/util
) are not run as jobs.
Provide a label to give your job a name, otherwise the filename will be used.
Job handlers are executed every time a branch detects a new push. In the future we will likely add additional events for “comments”, “merges”, etc.
Job files in Pierre export a label (used to name your job) and a default function or array of functions.
If you return an array, each function will be evaluated serially:
export const label = 'Serial Tasks';
export default [firstTask, secondTask, thirdTask];
Each Job is called with an id and a branch:
export default async ({ id, branch }) => {
if (branch.name === '🍔') throw new Error('No burgers allowed');
console.log('Burger free zone');
};
Everything logged from the default job will be streamed to the jobs pages in Pierre (e.g,
https://pierre.co/[team]/[repo]/jobs/[…branch]
).
There are a few ways to fail a job. The first, and the most recommended, is to simply throw an error:
export default async () => {
throw new Error('This job failed… 😢');
};
This will result in the job failing with an exit code of 1. You can alternatively return a custom error code to indicate certain failure states, eg.
export default async () => {
return 123;
};
Note: An exit code of 0 indicates success, while anything else indicates failure.
Pierre exports a special function for executing commands in your container called run
. Run
automatically logs special information to your UI and makes your logs more readable.
import { run } from "pierre"
export default () => {
await run("tsc -p ./tsconfig.json --noEmit", {
label: "Run typescript typechecker"
})
}
The run command also takes the following options:
export interface RunOptions {
// Optional cwd to set
cwd?: string;
// When set to `true`, avoid passing through `process.env`
clearEnv?: boolean;
// Additional environment variables to set
env?: Record<string, string>;
// What exit code to assert for each command
expectedCode?: number;
// If set to true then no assertions are made on expected exit code
allowAnyCode?: boolean;
// Whether we should pipe stdout and stderr
pipe?: boolean;
// Optional label to print
label?: string;
// Timeout in milliseconds the command has to run
timeout?: number;
}
Run itself provides a couple of return objects. Useful for extracting specific results or processing output for strings.
export interface RunResult {
exitCode: number;
stdmerged: string;
stderr: string;
stdout: string;
}
Pierre runs all of your CI jobs in its default docker image. This container is modeled to be as similar to a regular dev machine as possible (with the latest stable version of node and chrome). By default it has your secrets and local branch cloned into it.
In the future we will allow you to provide custom containers for more advanced use cases and dependencies.
Here’s a partial list of installed tools:
node 20.15.0
npm 10.7.0
pnpm 9.4.0
yarn (follows packageManager in package.json)
bun 1.1.17
go 1.21.6
php 8.2.20
python 3.12.4 (miniconda)
ruby 3.1.2p20
chromium binary, plus dependencies for chromium, firefox, and webkit for test tools that install/vendor their own browsers
Pierre has it’s own Redis-powered KV store. This is useful for quickly storing information between CI runs such as performance metrics, bundle sizes, and other notes.
import { Store } from 'pierre';
export default async () => {
await Store.set('dog', '🐕');
await Store.get('dog');
};
Secrets are managed through repo settings in Pierre and are made available in your local container
as env variables accessible in process.env
and on the command line via $
.
import { run } from "pierre"
export default () => {
await run("echo $VERCEL_ACCESS_TOKEN");
}
You can use the Pierre CLI to debug your jobs locally. If you haven’t already, install Pierre’s CLI:
$ npm install pierre -g
Then:
# Ex: pierre run .pierre/ci/typecheck.ts
$ pierre run <path>
or
# Ex: pierre run typecheck
$ pierre run <name>
Code hosting, review, docs, and CI. One place for product engineers and their teams to focus on what they do best—building products.
Join the WaitlistSkip the line! Join our Discord for early access