feat: add runner conversion scripts and strengthen cutover automation
This commit is contained in:
496
runners-conversion/augur/actions-local.sh
Executable file
496
runners-conversion/augur/actions-local.sh
Executable file
@@ -0,0 +1,496 @@
|
||||
#!/usr/bin/env bash
|
||||
# actions-local.sh — Setup/start/stop local GitHub Actions runtime on macOS.
|
||||
#
|
||||
# This script prepares and manages local execution of workflows with `act`.
|
||||
# Default runtime is Colima (free, local Docker daemon).
|
||||
#
|
||||
# Typical flow:
|
||||
# 1) ./scripts/actions-local.sh --mode setup
|
||||
# 2) ./scripts/actions-local.sh --mode start
|
||||
# 3) act -W .github/workflows/ci-quality-gates.yml
|
||||
# 4) ./scripts/actions-local.sh --mode stop
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
MODE=""
|
||||
RUNTIME="auto"
|
||||
RUNTIME_EXPLICIT=false
|
||||
REFRESH_BREW=false
|
||||
|
||||
COLIMA_PROFILE="${AUGUR_ACTIONS_COLIMA_PROFILE:-augur-actions}"
|
||||
COLIMA_CPU="${AUGUR_ACTIONS_COLIMA_CPU:-4}"
|
||||
COLIMA_MEMORY_GB="${AUGUR_ACTIONS_COLIMA_MEMORY_GB:-8}"
|
||||
COLIMA_DISK_GB="${AUGUR_ACTIONS_COLIMA_DISK_GB:-60}"
|
||||
WAIT_TIMEOUT_SEC="${AUGUR_ACTIONS_WAIT_TIMEOUT_SEC:-180}"
|
||||
|
||||
STATE_DIR="${TMPDIR:-/tmp}"
|
||||
STATE_FILE="${STATE_DIR%/}/augur-actions-local.state"
|
||||
|
||||
STATE_RUNTIME=""
|
||||
STATE_PROFILE=""
|
||||
STATE_STARTED_BY_SCRIPT="0"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
./scripts/actions-local.sh --mode <setup|start|stop> [options]
|
||||
|
||||
Required:
|
||||
--mode MODE One of: setup, start, stop
|
||||
|
||||
Options:
|
||||
--runtime RUNTIME Runtime choice: auto, colima, docker-desktop (default: auto)
|
||||
--refresh-brew In setup mode, force brew metadata refresh even if nothing is missing
|
||||
--colima-profile NAME Colima profile name (default: augur-actions)
|
||||
--cpu N Colima CPU count for start (default: 4)
|
||||
--memory-gb N Colima memory (GB) for start (default: 8)
|
||||
--disk-gb N Colima disk (GB) for start (default: 60)
|
||||
-h, --help Show this help
|
||||
|
||||
Examples:
|
||||
./scripts/actions-local.sh --mode setup
|
||||
./scripts/actions-local.sh --mode start
|
||||
./scripts/actions-local.sh --mode start --runtime colima --cpu 6 --memory-gb 12
|
||||
./scripts/actions-local.sh --mode stop
|
||||
./scripts/actions-local.sh --mode stop --runtime colima
|
||||
|
||||
Environment overrides:
|
||||
AUGUR_ACTIONS_COLIMA_PROFILE
|
||||
AUGUR_ACTIONS_COLIMA_CPU
|
||||
AUGUR_ACTIONS_COLIMA_MEMORY_GB
|
||||
AUGUR_ACTIONS_COLIMA_DISK_GB
|
||||
AUGUR_ACTIONS_WAIT_TIMEOUT_SEC
|
||||
EOF
|
||||
}
|
||||
|
||||
log() {
|
||||
printf '[actions-local] %s\n' "$*"
|
||||
}
|
||||
|
||||
warn() {
|
||||
printf '[actions-local] WARNING: %s\n' "$*" >&2
|
||||
}
|
||||
|
||||
die() {
|
||||
printf '[actions-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"
|
||||
}
|
||||
|
||||
ensure_macos() {
|
||||
local os
|
||||
os="$(uname -s)"
|
||||
[[ "$os" == "Darwin" ]] || die "This script currently supports macOS only."
|
||||
}
|
||||
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--mode)
|
||||
shift
|
||||
[[ $# -gt 0 ]] || die "--mode requires a value"
|
||||
MODE="$1"
|
||||
shift
|
||||
;;
|
||||
--runtime)
|
||||
shift
|
||||
[[ $# -gt 0 ]] || die "--runtime requires a value"
|
||||
RUNTIME="$1"
|
||||
RUNTIME_EXPLICIT=true
|
||||
shift
|
||||
;;
|
||||
--refresh-brew)
|
||||
REFRESH_BREW=true
|
||||
shift
|
||||
;;
|
||||
--colima-profile)
|
||||
shift
|
||||
[[ $# -gt 0 ]] || die "--colima-profile requires a value"
|
||||
COLIMA_PROFILE="$1"
|
||||
shift
|
||||
;;
|
||||
--cpu)
|
||||
shift
|
||||
[[ $# -gt 0 ]] || die "--cpu requires a value"
|
||||
COLIMA_CPU="$1"
|
||||
shift
|
||||
;;
|
||||
--memory-gb)
|
||||
shift
|
||||
[[ $# -gt 0 ]] || die "--memory-gb requires a value"
|
||||
COLIMA_MEMORY_GB="$1"
|
||||
shift
|
||||
;;
|
||||
--disk-gb)
|
||||
shift
|
||||
[[ $# -gt 0 ]] || die "--disk-gb requires a value"
|
||||
COLIMA_DISK_GB="$1"
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
die "unknown argument: $1"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ -n "$MODE" ]] || die "--mode is required (setup|start|stop)"
|
||||
case "$MODE" in
|
||||
setup|start|stop) ;;
|
||||
*) die "invalid --mode: $MODE (expected setup|start|stop)" ;;
|
||||
esac
|
||||
|
||||
case "$RUNTIME" in
|
||||
auto|colima|docker-desktop) ;;
|
||||
*) die "invalid --runtime: $RUNTIME (expected auto|colima|docker-desktop)" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
ensure_command_line_tools() {
|
||||
if xcode-select -p >/dev/null 2>&1; then
|
||||
log "Xcode Command Line Tools already installed."
|
||||
return
|
||||
fi
|
||||
|
||||
log "Xcode Command Line Tools missing; attempting automated install..."
|
||||
local marker="/tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress"
|
||||
local label=""
|
||||
|
||||
touch "$marker"
|
||||
label="$(softwareupdate -l 2>/dev/null | sed -n 's/^\* Label: //p' | grep 'Command Line Tools' | tail -n1 || true)"
|
||||
rm -f "$marker"
|
||||
|
||||
if [[ -n "$label" ]]; then
|
||||
sudo softwareupdate -i "$label" --verbose
|
||||
sudo xcode-select --switch /Library/Developer/CommandLineTools
|
||||
else
|
||||
warn "Could not auto-detect Command Line Tools package; launching GUI installer."
|
||||
xcode-select --install || true
|
||||
die "Finish installing Command Line Tools, then re-run setup."
|
||||
fi
|
||||
|
||||
xcode-select -p >/dev/null 2>&1 || die "Command Line Tools installation did not complete."
|
||||
log "Xcode Command Line Tools installed."
|
||||
}
|
||||
|
||||
ensure_homebrew() {
|
||||
if command -v brew >/dev/null 2>&1; then
|
||||
log "Homebrew already installed."
|
||||
else
|
||||
require_cmd curl
|
||||
log "Installing Homebrew..."
|
||||
NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
fi
|
||||
|
||||
if [[ -x /opt/homebrew/bin/brew ]]; then
|
||||
eval "$(/opt/homebrew/bin/brew shellenv)"
|
||||
elif [[ -x /usr/local/bin/brew ]]; then
|
||||
eval "$(/usr/local/bin/brew shellenv)"
|
||||
elif command -v brew >/dev/null 2>&1; then
|
||||
eval "$("$(command -v brew)" shellenv)"
|
||||
else
|
||||
die "Homebrew not found after installation."
|
||||
fi
|
||||
|
||||
log "Homebrew ready: $(brew --version | head -n1)"
|
||||
}
|
||||
|
||||
install_brew_formula_if_missing() {
|
||||
local formula="$1"
|
||||
if brew list --versions "$formula" >/dev/null 2>&1; then
|
||||
log "Already installed: $formula"
|
||||
else
|
||||
log "Installing: $formula"
|
||||
brew install "$formula"
|
||||
fi
|
||||
}
|
||||
|
||||
list_missing_formulas() {
|
||||
local formulas=("$@")
|
||||
local -a missing=()
|
||||
local formula
|
||||
for formula in "${formulas[@]}"; do
|
||||
if ! brew list --versions "$formula" >/dev/null 2>&1; then
|
||||
missing+=("$formula")
|
||||
fi
|
||||
done
|
||||
if [[ "${#missing[@]}" -gt 0 ]]; then
|
||||
printf '%s\n' "${missing[@]}"
|
||||
fi
|
||||
}
|
||||
|
||||
colima_context_name() {
|
||||
local profile="$1"
|
||||
if [[ "$profile" == "default" ]]; then
|
||||
printf 'colima'
|
||||
else
|
||||
printf 'colima-%s' "$profile"
|
||||
fi
|
||||
}
|
||||
|
||||
colima_is_running() {
|
||||
local out
|
||||
out="$(colima status --profile "$COLIMA_PROFILE" 2>&1 || true)"
|
||||
if printf '%s' "$out" | grep -qi "not running"; then
|
||||
return 1
|
||||
fi
|
||||
if printf '%s' "$out" | grep -qi "running"; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
docker_ready() {
|
||||
docker info >/dev/null 2>&1
|
||||
}
|
||||
|
||||
wait_for_docker() {
|
||||
local waited=0
|
||||
while ! docker_ready; do
|
||||
if (( waited >= WAIT_TIMEOUT_SEC )); then
|
||||
die "Docker daemon not ready after ${WAIT_TIMEOUT_SEC}s."
|
||||
fi
|
||||
sleep 2
|
||||
waited=$((waited + 2))
|
||||
done
|
||||
}
|
||||
|
||||
write_state() {
|
||||
local runtime="$1"
|
||||
local started="$2"
|
||||
cat > "$STATE_FILE" <<EOF
|
||||
runtime=$runtime
|
||||
profile=$COLIMA_PROFILE
|
||||
started_by_script=$started
|
||||
timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
EOF
|
||||
}
|
||||
|
||||
read_state() {
|
||||
STATE_RUNTIME=""
|
||||
STATE_PROFILE=""
|
||||
STATE_STARTED_BY_SCRIPT="0"
|
||||
|
||||
[[ -f "$STATE_FILE" ]] || return 0
|
||||
|
||||
while IFS='=' read -r key value; do
|
||||
case "$key" in
|
||||
runtime) STATE_RUNTIME="$value" ;;
|
||||
profile) STATE_PROFILE="$value" ;;
|
||||
started_by_script) STATE_STARTED_BY_SCRIPT="$value" ;;
|
||||
esac
|
||||
done < "$STATE_FILE"
|
||||
}
|
||||
|
||||
resolve_runtime_auto() {
|
||||
if command -v colima >/dev/null 2>&1; then
|
||||
printf 'colima'
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ -d "/Applications/Docker.app" ]] || command -v docker >/dev/null 2>&1; then
|
||||
printf 'docker-desktop'
|
||||
return
|
||||
fi
|
||||
|
||||
die "No supported runtime found. Run setup first."
|
||||
}
|
||||
|
||||
start_colima_runtime() {
|
||||
require_cmd colima
|
||||
require_cmd docker
|
||||
require_cmd act
|
||||
|
||||
local started="0"
|
||||
if colima_is_running; then
|
||||
log "Colima profile '${COLIMA_PROFILE}' is already running."
|
||||
else
|
||||
log "Starting Colima profile '${COLIMA_PROFILE}' (cpu=${COLIMA_CPU}, memory=${COLIMA_MEMORY_GB}GB, disk=${COLIMA_DISK_GB}GB)..."
|
||||
colima start --profile "$COLIMA_PROFILE" --cpu "$COLIMA_CPU" --memory "$COLIMA_MEMORY_GB" --disk "$COLIMA_DISK_GB"
|
||||
started="1"
|
||||
fi
|
||||
|
||||
local context
|
||||
context="$(colima_context_name "$COLIMA_PROFILE")"
|
||||
if docker context ls --format '{{.Name}}' | grep -Fxq "$context"; then
|
||||
docker context use "$context" >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
wait_for_docker
|
||||
write_state "colima" "$started"
|
||||
|
||||
log "Runtime ready (colima)."
|
||||
log "Try: act -W .github/workflows/ci-quality-gates.yml"
|
||||
}
|
||||
|
||||
start_docker_desktop_runtime() {
|
||||
require_cmd docker
|
||||
require_cmd act
|
||||
require_cmd open
|
||||
|
||||
local started="0"
|
||||
if docker_ready; then
|
||||
log "Docker daemon already running."
|
||||
else
|
||||
log "Starting Docker Desktop..."
|
||||
open -ga Docker
|
||||
started="1"
|
||||
fi
|
||||
|
||||
wait_for_docker
|
||||
write_state "docker-desktop" "$started"
|
||||
|
||||
log "Runtime ready (docker-desktop)."
|
||||
log "Try: act -W .github/workflows/ci-quality-gates.yml"
|
||||
}
|
||||
|
||||
stop_colima_runtime() {
|
||||
require_cmd colima
|
||||
|
||||
if colima_is_running; then
|
||||
log "Stopping Colima profile '${COLIMA_PROFILE}'..."
|
||||
colima stop --profile "$COLIMA_PROFILE"
|
||||
else
|
||||
log "Colima profile '${COLIMA_PROFILE}' is already stopped."
|
||||
fi
|
||||
}
|
||||
|
||||
stop_docker_desktop_runtime() {
|
||||
require_cmd osascript
|
||||
|
||||
log "Stopping Docker Desktop..."
|
||||
osascript -e 'quit app "Docker"' >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
do_setup() {
|
||||
ensure_macos
|
||||
ensure_command_line_tools
|
||||
ensure_homebrew
|
||||
local required_formulas=(git act colima docker)
|
||||
local missing_formulas=()
|
||||
local missing_formula
|
||||
while IFS= read -r missing_formula; do
|
||||
[[ -n "$missing_formula" ]] || continue
|
||||
missing_formulas+=("$missing_formula")
|
||||
done < <(list_missing_formulas "${required_formulas[@]}" || true)
|
||||
|
||||
if [[ "${#missing_formulas[@]}" -eq 0 ]]; then
|
||||
log "All required formulas already installed: ${required_formulas[*]}"
|
||||
if [[ "$REFRESH_BREW" == "true" ]]; then
|
||||
log "Refreshing Homebrew metadata (--refresh-brew)..."
|
||||
brew update
|
||||
else
|
||||
log "Skipping brew update; nothing to install."
|
||||
fi
|
||||
log "Setup complete (no changes required)."
|
||||
log "Next: ./scripts/actions-local.sh --mode start"
|
||||
return
|
||||
fi
|
||||
|
||||
log "Missing formulas detected: ${missing_formulas[*]}"
|
||||
log "Updating Homebrew metadata..."
|
||||
brew update
|
||||
|
||||
local formula
|
||||
for formula in "${required_formulas[@]}"; do
|
||||
install_brew_formula_if_missing "$formula"
|
||||
done
|
||||
|
||||
log "Setup complete."
|
||||
log "Next: ./scripts/actions-local.sh --mode start"
|
||||
}
|
||||
|
||||
do_start() {
|
||||
ensure_macos
|
||||
|
||||
local selected_runtime="$RUNTIME"
|
||||
if [[ "$selected_runtime" == "auto" ]]; then
|
||||
selected_runtime="$(resolve_runtime_auto)"
|
||||
fi
|
||||
|
||||
case "$selected_runtime" in
|
||||
colima)
|
||||
start_colima_runtime
|
||||
;;
|
||||
docker-desktop)
|
||||
start_docker_desktop_runtime
|
||||
;;
|
||||
*)
|
||||
die "unsupported runtime: $selected_runtime"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
do_stop() {
|
||||
ensure_macos
|
||||
read_state
|
||||
|
||||
local selected_runtime="$RUNTIME"
|
||||
local should_stop="1"
|
||||
|
||||
if [[ "$selected_runtime" == "auto" ]]; then
|
||||
if [[ -n "$STATE_RUNTIME" ]]; then
|
||||
selected_runtime="$STATE_RUNTIME"
|
||||
if [[ -n "$STATE_PROFILE" ]]; then
|
||||
COLIMA_PROFILE="$STATE_PROFILE"
|
||||
fi
|
||||
if [[ "$STATE_STARTED_BY_SCRIPT" != "1" ]]; then
|
||||
should_stop="0"
|
||||
fi
|
||||
else
|
||||
if command -v colima >/dev/null 2>&1; then
|
||||
selected_runtime="colima"
|
||||
elif [[ -d "/Applications/Docker.app" ]] || command -v docker >/dev/null 2>&1; then
|
||||
selected_runtime="docker-desktop"
|
||||
else
|
||||
log "No local Actions runtime is installed or tracked. Nothing to stop."
|
||||
return
|
||||
fi
|
||||
should_stop="0"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$should_stop" != "1" && "$RUNTIME_EXPLICIT" != "true" ]]; then
|
||||
log "No runtime started by this script is currently tracked. Nothing to stop."
|
||||
log "Pass --runtime colima or --runtime docker-desktop to force a stop."
|
||||
return
|
||||
fi
|
||||
|
||||
case "$selected_runtime" in
|
||||
colima)
|
||||
stop_colima_runtime
|
||||
;;
|
||||
docker-desktop)
|
||||
stop_docker_desktop_runtime
|
||||
;;
|
||||
*)
|
||||
die "unsupported runtime: $selected_runtime"
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ -f "$STATE_FILE" ]]; then
|
||||
rm -f "$STATE_FILE"
|
||||
fi
|
||||
|
||||
log "Stop complete."
|
||||
}
|
||||
|
||||
main() {
|
||||
parse_args "$@"
|
||||
|
||||
case "$MODE" in
|
||||
setup) do_setup ;;
|
||||
start) do_start ;;
|
||||
stop) do_stop ;;
|
||||
*) die "unexpected mode: $MODE" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user