Quickstart

Setup

  1. Install the binary: brew install gabrielkoerich/tap/shipcheck, or curl -fsSL https://shipcheck.run/install.sh | sh (install).
  2. Run shipcheck init in a clone of the repository, on main.
  3. Commit the shipcheck.toml it writes and merge it to main.
  4. Install the shipcheck App on the repository. init prints this link.

Each other clone then needs shipcheck install once. The sections below say what each step does.

For the repository owner, once

In a clone of the repository, on main:

shipcheck init

It reads your workflows and project files and changes nothing until you apply. Its steps are tabs: Checks, Install, Workflows, GitHub and Review. Move between them with the arrow keys, Tab or 1 to 5, and go back to any of them. Each starts on the recommended choice. On Checks, Space or Enter moves a job to another place it can run: a host check, act on Docker, act on a colima VM where colima could run it, or GitHub. A job becomes a host check only when it ran as one in init's trial. Apply on the Review tab. Quitting, or Ctrl-C at any point before you apply, leaves the repository and the machine as they were.

An agent or a script runs shipcheck init --yes, which asks nothing and applies the recommended choices, except the browser and the ruleset. Without a terminal and without --yes, init only previews: it tries each check with a temporary SHIPCHECK_HOME, prints the plan and the review --yes would apply, and writes nothing.

Checks

init turns each job of a workflow that GitHub runs for feature branches into a check named like the job.

Only when no job gives a check, a package.json test or lint script, a Cargo.toml, a pyproject.toml or a go.mod at the root gives the checks they imply. A repository with no .github/workflows gets its checks this way, in host mode, the only mode it can use.

It shows each check with the file it came from, and what it left out and why. The checks run natively in a sandbox, and before the review init runs each one there once, as shipcheck run would. A check that fails runs again without forced color, then with color off. A job whose check still fails stays on GitHub, and init prints the end of its log and the fix. init does not try the act jobs, so a failing one shows on its first run. Edit shipcheck.toml afterwards to drop one:

runner = "host"

[host]
versions = ["cargo --version", "rustc --version"]

[host.checks]
test = "cargo fmt --check && cargo clippy --all-targets -- -D warnings && cargo test"
service = "cd service && bun run test"

shipcheck has two modes. init writes host mode when it finds checks, with act entries for the jobs a host check cannot run as GitHub does. It writes act mode for the whole repository when a job runs a Linux matrix, every job runs on Linux and Docker is running, since host mode tests only this machine's toolchain:

GitHub: the App

It shows the install link of the shipcheck App, which posts the checks, and opens it in the browser when you apply. Install it on the repository.

Pick your user or organization and the repositories there (install the App). At the end, init signs this machine in if it cannot post yet (sign in each machine).

Workflows: hosted Actions only on main

For each workflow that GitHub runs for feature branches, it shows one line, such as ci.yml: on PRs shipcheck runs shipcheck/clippy, shipcheck/fmt, shipcheck/test, GitHub keeps running ci.yml on pushes to main, and on fork PRs, and a diff. The workflow keeps its triggers. Each job a shipcheck check covers gets an if: that runs it on GitHub only on your default branch, on the other branches the workflow lists for pushes, on PRs into other branches, for Dependabot, and for a fork's PR, which shipcheck never runs. A job no check covers stays on GitHub with no if: added (which jobs run where).

GitHub: branch protection

It shows one check name per job, such as shipcheck/test, and GitHub's own check names of the jobs that stay on GitHub. The recommended choice protects the default branch with a ruleset, created with your own gh login: no direct pushes, only PRs, and a PR merges once its branch is up to date and each of those checks passes, from the App or from GitHub Actions.

For a job on GitHub whose check names init cannot tell, such as a job whose name or matrix comes from an expression, the ruleset keeps the checks the branch requires now. When gh cannot read the branch's rules, init makes no ruleset and leaves them as they are. Install the App before the first PR, since a required check that nobody posts blocks it. When your gh login has no admin rights on the repository, init says so and gives the link to set the ruleset up by hand, and the rest still applies. Update the ruleset when a job is added, renamed or removed. Require the checks has the manual steps.

Install

It writes the agent instructions (agents). Then, as shipcheck install does below, it removes the agent hooks an older version wrote, installs the git hooks for this clone, signs the machine in if it cannot post, and starts the daemon.

Other cases

Add on = ["push", "commit"] to start runs on each commit too.

Commit it and merge it to main. shipcheck reads this file and the workflows only from main, so a PR cannot change what it must pass. Until the file is on main there is nothing to run: shipcheck run exits 2, and the hooks and the daemon start nothing. Every setting.

Your workflows keep running on GitHub on pushes to main, as a backstop after each merge.

Add the badge

To show that the repository's checks come from shipcheck, add this line to its README:

[![checks: shipcheck](https://img.shields.io/badge/checks-shipcheck-1f9d55)](https://shipcheck.run)

checks: shipcheck

For each teammate, once per clone

shipcheck install

It installs two hooks, pre-push and post-commit. The post-commit hook starts a run only when on has commit. It also starts the daemon as a background service, a launchd agent on macOS or a systemd user unit on Linux, and adds this repository to it. Set SHIPCHECK_NO_DAEMON=1 to skip that on one machine. Git never installs hooks on clone, so each clone needs this one step. To make it automatic, run it from the repository's usual setup: the prepare script in package.json, a just setup recipe, or a mise task.

If the repository already uses husky, lefthook or another hook manager, through core.hooksPath or a pre-push hook of its own, shipcheck install leaves its hooks alone and stops with an error that gives the line to add. It then does not sign the machine in or start the daemon, so run it again after you add the line. A post-commit hook of its own only gets a note with the line.

When the machine cannot post yet, install signs it in: in a terminal it prints a code to enter at github.com/login/device. Without a terminal it prints one line and goes on.

For an agent machine

  1. Install the binary.
  2. Run shipcheck install in each clone. In a terminal, it signs the machine in if it cannot post yet.

A machine with nobody at its terminal, such as a VM, gets a token instead: run shipcheck token --name <machine> where you can open a browser and set the output as SHIPCHECK_TOKEN there (unattended machines).

Every day

Commit and push as usual. Nothing waits on shipcheck:

To run the checks in the foreground and wait for the verdict, for example as an agent's test step before it pushes:

shipcheck run

When a run fails, give the agent the logs:

shipcheck logs

Previous: Install Next: Require the check