#!/usr/bin/env bash # ============================================================================= # LOCAL ACT RUNNER (Bash) # ============================================================================= # Thin wrapper around nektos/act for local CI execution. # For full-featured local CI (smoke, pr, module modes) use: # ./devops/scripts/local-ci.sh # # Usage: # ./devops/ci-local/run-act.sh [options] # # Options: # -w, --workflow Workflow file (with or without .yml) # -j, --job Run only a specific job # -l, --list List available jobs # -n, --dry-run Dry-run mode # -e, --event Event type: pull_request (default) or push # -r, --rebuild Force rebuild CI image # -v, --verbose Verbose output # -h, --help Show this help # ============================================================================= set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" CI_IMAGE="stellaops-ci:local" CI_DOCKERFILE="$REPO_ROOT/devops/docker/Dockerfile.ci" # Defaults WORKFLOW="" JOB="" LIST=false DRY_RUN=false EVENT="pull_request" REBUILD=false VERBOSE=false usage() { head -25 "$0" | grep '^#' | sed 's/^# \?//' exit 0 } # ---- Parse args ---- while [[ $# -gt 0 ]]; do case "$1" in -w|--workflow) WORKFLOW="$2"; shift 2 ;; -j|--job) JOB="$2"; shift 2 ;; -l|--list) LIST=true; shift ;; -n|--dry-run) DRY_RUN=true; shift ;; -e|--event) EVENT="$2"; shift 2 ;; -r|--rebuild) REBUILD=true; shift ;; -v|--verbose) VERBOSE=true; shift ;; -h|--help) usage ;; *) echo "Unknown option: $1"; usage ;; esac done # ---- Prerequisite checks ---- if ! docker info &>/dev/null; then echo "ERROR: Docker is not running." >&2 exit 1 fi if ! command -v act &>/dev/null; then echo "ERROR: act is not installed." >&2 echo " macOS: brew install act" >&2 echo " Linux: curl -sSL https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash" >&2 echo " Windows: choco install act-cli" >&2 exit 1 fi # ---- Build CI image if needed ---- image_exists() { docker image inspect "$CI_IMAGE" &>/dev/null } if [[ "$REBUILD" == "true" ]] || ! image_exists; then echo "[ci-local] Building CI image $CI_IMAGE ..." docker build -t "$CI_IMAGE" -f "$CI_DOCKERFILE" "$REPO_ROOT" fi # ---- Ensure .env.local ---- ENV_LOCAL="$SCRIPT_DIR/.env.local" if [[ ! -f "$ENV_LOCAL" ]]; then echo "[ci-local] Creating .env.local from template ..." cp "$SCRIPT_DIR/.env.local.template" "$ENV_LOCAL" fi # ---- Assemble act arguments ---- act_args=() if [[ "$LIST" == "true" ]]; then cd "$REPO_ROOT" exec act -l fi if [[ -n "$WORKFLOW" ]]; then wf_file="$WORKFLOW" [[ "$wf_file" != *.yml ]] && wf_file="${wf_file}.yml" wf_path="$REPO_ROOT/.gitea/workflows/$wf_file" if [[ ! -f "$wf_path" ]]; then echo "ERROR: Workflow not found: $wf_path" >&2 exit 1 fi act_args+=(-W "$wf_path") fi if [[ -n "$JOB" ]]; then act_args+=(-j "$JOB") fi # Event file case "$EVENT" in pull_request) event_file="$SCRIPT_DIR/events/pull-request.json" ;; push) event_file="$SCRIPT_DIR/events/push.json" ;; *) echo "ERROR: Unknown event type: $EVENT" >&2; exit 1 ;; esac if [[ -f "$event_file" ]]; then act_args+=(--eventpath "$event_file") fi if [[ "$DRY_RUN" == "true" ]]; then act_args+=(-n) fi if [[ "$VERBOSE" == "true" ]]; then act_args+=(--verbose) fi # ---- Run ---- echo "[ci-local] act ${act_args[*]}" cd "$REPO_ROOT" exec act "${act_args[@]}"