#!/usr/bin/env bash
# Every check the commit hooks make, plus the test suite, in one command.
#
# A commit made with `--no-verify` is checked by nobody, and so is a branch
# whose author never ran `prek install`. This script is what a person or a
# runner invokes to check a branch anyway.
#
# It does not restate the checks. prek.toml is the list, and this runs prek
# over it; a second list here would be a second source of truth and would
# disagree with the hooks within a month. What this adds to the hooks is the
# two things they cannot do:
#
# 1. The commit-msg stage over a range. The hook sees one message as it is
# written; here every commit on the branch is checked, including the ones
# that went in with the hook turned off.
# 2. The whole test suite. The hooks run the lexicon tests only, because a
# full `cargo test` on every commit is a wait nobody would accept. A
# change that breaks a test in another crate passes every hook.
#
# Usage: scripts/ci.sh []
#
# The ref the branch is measured against for the commit-msg stage.
# Defaults to $CI_BASE, then to origin/main.
set -euo pipefail
cd "$(dirname "$0")/.."
base="${1:-${CI_BASE:-origin/main}}"
status=0
step() {
printf '\n=== %s ===\n' "$1" >&2
}
step "prek: the pre-commit hooks, over every file"
# --all-files rather than a diff: a runner has no staged set, and a hook whose
# `files` pattern never matched the branch's diff is exactly the one that has
# quietly stopped working.
prek run --all-files || status=1
step "prek: the commit-msg hook, over $base..HEAD"
# A spindle clones one commit deep, so it has neither the history nor the
# base, and committed's prebuilt binary cannot load on its Nix image anyway.
if [ "$(git rev-parse --is-shallow-repository)" = true ]; then
echo "skipping the commit-msg stage: a shallow clone has no history to check" >&2
elif ! git rev-parse --verify --quiet "$base" >/dev/null; then
echo "skipping the commit-msg stage: $base does not resolve here" >&2
else
message="$(mktemp)"
trap 'rm -f "$message"' EXIT
commits="$(git rev-list "$base..HEAD")"
if [ -z "$commits" ]; then
echo "no commits on top of $base" >&2
fi
for commit in $commits; do
git log -1 --format=%B "$commit" >"$message"
echo "--- $(git log -1 --format='%h %s' "$commit")" >&2
prek run --stage commit-msg --commit-msg-filename "$message" || status=1
done
fi
step "the brand images are the ones the generator produces"
# Not a commit hook: a full anneal of every mark is tens of seconds. Here,
# once, is where a stale favicon gets caught.
scripts/build-brand.sh --check || status=1
step "npm ci: the Node client crates/didbot-serve/tests/oauth_interop.rs runs"
# Before the workspace run, which signs in with @atproto/oauth-client-node only
# when this tree is installed and otherwise says it skipped. Not a commit hook:
# it fetches from the npm registry.
if command -v npm >/dev/null 2>&1; then
npm ci --prefix tools/oauth-interop || status=1
else
echo "npm is not on PATH; tests/oauth_interop.rs will skip" >&2
fi
step "the Merkle search tree corpus crates/didbot/tests/conformance reads"
# Not vendored, and the conformance suite fails rather than skips without it.
# A runner starts with an empty cache, so this clones it every run; a machine
# that already has it is left alone.
scripts/fetch-mst-vectors.sh || status=1
step "cargo test: the whole workspace"
# --no-fail-fast: a failing test binary still leaves every later crate's
# results on the page.
cargo test --workspace --all-features --no-fail-fast || status=1
step "the policy dashboard against real servers"
# Opt-in: it compiles didbot-pds, builds the site, starts two servers and wants
# a Chrome, which is minutes and a browser no other step needs. DIDBOT_E2E=1
# asks for it; the script itself skips whatever it cannot find.
if [ "${DIDBOT_E2E:-0}" = 1 ]; then
scripts/test-policy-e2e.sh || status=1
else
echo "skipping; set DIDBOT_E2E=1 to run scripts/test-policy-e2e.sh" >&2
fi
step "the analytics page in a browser"
# Opt-in for the same reason: a minute of seeding and a Chrome.
if [ "${DIDBOT_E2E:-0}" = 1 ]; then
scripts/test-analytics-browser.sh || status=1
else
echo "skipping; set DIDBOT_E2E=1 to run scripts/test-analytics-browser.sh" >&2
fi
if [ "$status" -eq 0 ]; then
printf '\nall checks passed\n' >&2
else
printf '\nsomething failed; see above\n' >&2
fi
exit "$status"