#!/usr/bin/env bash # ci-local.sh — Run augur CI quality gates locally. # Mirrors .github/workflows/ci-quality-gates.yml without GitHub-hosted runners. set -euo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$REPO_ROOT" run_contracts=false run_backend=false run_extensions=false explicit_stage=false skip_install=false declare -a suites=() declare -a default_suites=( "tests" "tests-copilot" "tests-deepseek" "tests-perplexity" "tests-grok" "tests-poe" ) usage() { cat <<'EOF' Usage: ./scripts/ci-local.sh [options] Runs local CI gates equivalent to .github/workflows/ci-quality-gates.yml: 1) contracts -> scripts/check-contract-drift.sh 2) backend -> go mod download, go vet ./..., go test ./... -count=1 3) extensions -> npm ci + npm test in each extension test suite If no stage options are provided, all stages run. Options: --contracts Run contracts drift check stage --backend Run backend Go stage --extensions Run extension Jest stage --suite NAME Extension suite name (repeatable), e.g. tests-deepseek --skip-install Skip dependency install steps (go mod download, npm ci) -h, --help Show this help Examples: ./scripts/ci-local.sh ./scripts/ci-local.sh --backend ./scripts/ci-local.sh --extensions --suite tests --suite tests-copilot ./scripts/ci-local.sh --contracts --backend --skip-install EOF } log() { printf '[ci-local] %s\n' "$*" } die() { printf '[ci-local] ERROR: %s\n' "$*" >&2 exit 1 } require_cmd() { local cmd="$1" command -v "$cmd" >/dev/null 2>&1 || die "required command not found: $cmd" } parse_args() { while [[ $# -gt 0 ]]; do case "$1" in --contracts) explicit_stage=true run_contracts=true shift ;; --backend) explicit_stage=true run_backend=true shift ;; --extensions) explicit_stage=true run_extensions=true shift ;; --suite) shift [[ $# -gt 0 ]] || die "--suite requires a value" suites+=("$1") shift ;; --skip-install) skip_install=true shift ;; -h|--help) usage exit 0 ;; *) die "unknown argument: $1" ;; esac done if [[ "$explicit_stage" == false ]]; then run_contracts=true run_backend=true run_extensions=true fi if [[ "${#suites[@]}" -eq 0 ]]; then suites=("${default_suites[@]}") fi } run_contracts_stage() { require_cmd git log "Stage: contracts" AUGUR_CONTRACT_DRIFT_USE_WORKTREE=1 scripts/check-contract-drift.sh } run_backend_stage() { require_cmd go log "Stage: backend" if [[ "$skip_install" == false ]]; then go mod download fi go vet ./... go test ./... -count=1 } run_extensions_stage() { require_cmd npm log "Stage: extensions" for suite in "${suites[@]}"; do local suite_dir="$REPO_ROOT/browser-extensions/history-extensions/$suite" [[ -d "$suite_dir" ]] || die "extension suite directory not found: $suite_dir" log "Suite: $suite" if [[ "$skip_install" == false ]]; then (cd "$suite_dir" && npm ci) fi (cd "$suite_dir" && npm test -- --runInBand) done } main() { parse_args "$@" local started_at started_at="$(date +%s)" log "Starting local CI pipeline in $REPO_ROOT" if [[ "$run_contracts" == true ]]; then run_contracts_stage fi if [[ "$run_backend" == true ]]; then run_backend_stage fi if [[ "$run_extensions" == true ]]; then run_extensions_stage fi local ended_at duration ended_at="$(date +%s)" duration="$((ended_at - started_at))" log "All selected stages passed (${duration}s)." } main "$@"