dev/run

377 lines
13 KiB
Bash
Executable file

#!/usr/bin/env bash
set -euo pipefail
# ═══════════════════════════════════════════════════════════
# 🔧 SETUP
# ═══════════════════════════════════════════════════════════
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="$SCRIPT_DIR/.env"
RUNS_DIR="$SCRIPT_DIR/runs"
LOGS_DIR="$SCRIPT_DIR/logs"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[0;33m'
NC='\033[0m'
# ═══════════════════════════════════════════════════════════
# 🛠️ HELPERS
# ═══════════════════════════════════════════════════════════
log() { echo -e "${GREEN}[RUN]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
# ═══════════════════════════════════════════════════════════
# 🔍 DEPENDENCY VALIDATION
# ═══════════════════════════════════════════════════════════
check_dependencies() {
local missing=()
# Required tools
local required_tools=("git" "find" "grep")
# Optional but recommended tools
local optional_tools=("gitleaks")
log "Checking dependencies..."
# Check required tools
for tool in "${required_tools[@]}"; do
if ! command -v "$tool" &>/dev/null; then
missing+=("$tool")
fi
done
# Check optional tools
for tool in "${optional_tools[@]}"; do
if ! command -v "$tool" &>/dev/null; then
warn "Optional tool missing: $tool (recommended for security scanning)"
else
log "✓ Found: $tool"
fi
done
# Report missing required tools
if [[ ${#missing[@]} -gt 0 ]]; then
error "Missing required tools: ${missing[*]}"
error "Please install missing dependencies"
exit 1
fi
log "✓ All required dependencies found"
}
# ═══════════════════════════════════════════════════════════
# 🔒 SECURITY SCANNING
# ═══════════════════════════════════════════════════════════
run_security_scan() {
log "Running security scan..."
if command -v gitleaks &>/dev/null; then
log "Using GitLeaks for secret detection..."
if gitleaks detect --verbose --exit-code 1; then
log "✅ No secrets detected"
return 0
else
error "❌ Secrets detected! Review before pushing."
return 1
fi
else
warn "GitLeaks not installed - skipping security scan"
warn "Install with: paru -S gitleaks"
echo
read -p "Continue without security scan? (y/N): " -r answer
if [[ ! "$answer" =~ ^[Yy]$ ]]; then
error "Push cancelled for security"
exit 1
fi
return 0
fi
}
# ═══════════════════════════════════════════════════════════
# 📤 PUSH COMMAND
# ═══════════════════════════════════════════════════════════
handle_push() {
log "Preparing to push repository..."
# Check if we're in a git repository
if ! git rev-parse --git-dir &>/dev/null; then
error "Not in a git repository"
exit 1
fi
# Check for uncommitted changes
if ! git diff-index --quiet HEAD --; then
warn "You have uncommitted changes"
echo
git status --short
echo
# Generate default commit message
local default_msg="dev: automated commit - $(date '+%Y-%m-%d %H:%M:%S')"
read -p "Commit all changes? (Y/n): " -r answer
if [[ ! "$answer" =~ ^[Nn]$ ]]; then
echo
echo "Default: $default_msg"
read -p "Custom commit message (or press Enter for default): " -r commit_msg
# Use default if empty
if [[ -z "$commit_msg" ]]; then
commit_msg="$default_msg"
fi
git add .
git commit -m "$commit_msg"
log "✓ Changes committed: $commit_msg"
fi
fi
# Run security scan
if ! run_security_scan; then
exit 1
fi
# Get current branch
local current_branch
current_branch=$(git branch --show-current)
# Push to origin
log "Pushing branch '$current_branch' to origin..."
if git push origin "$current_branch"; then
log "✅ Successfully pushed to origin/$current_branch"
else
error "❌ Push failed"
exit 1
fi
}
show_help() {
cat <<EOF
Usage: $0 [OPTIONS] [COMMAND|FILTERS...]
COMMANDS:
push Commit, scan for secrets, and push to git origin
deps, check Check for required dependencies
help Show this help message
SCRIPT EXECUTION:
[FILTERS...] Run scripts matching filters from runs/ directory
OPTIONS:
--dry Show what would run without executing
--verbose Show detailed output during execution
EXAMPLES:
$0 deps # Check dependencies
$0 push # Secure git push with secret scanning
$0 docker # Run scripts containing 'docker'
$0 --dry base # Show what base scripts would run
$0 --verbose all # Run all scripts with detailed output
SECURITY:
The push command automatically scans for secrets using GitLeaks
before pushing to prevent accidental credential exposure.
EOF
}
get_description() {
local script="$1"
grep '^# NAME:' "$script" 2>/dev/null | cut -d: -f2- | sed 's/^ *//' || echo "No description"
}
matches_filters() {
local script_name="$1"
shift
local filters=("$@")
[[ ${#filters[@]} -eq 0 ]] && return 0
for filter in "${filters[@]}"; do
if [[ "$script_name" == *"$filter"* ]] || [[ "${script_name,,}" == *"${filter,,}"* ]]; then
return 0
fi
done
return 1
}
# ═══════════════════════════════════════════════════════════
# 🚦 INITIALIZATION
# ═══════════════════════════════════════════════════════════
# Create runs directory if missing
if [[ ! -d "$RUNS_DIR" ]]; then
log "Creating runs directory at: $RUNS_DIR"
mkdir -p "$RUNS_DIR"
# Create example script
cat >"$RUNS_DIR/example.sh" <<'EOF'
#!/usr/bin/env bash
# NAME: Example script
echo "Hello from runs/example.sh!"
echo "Edit this script or add your own to $RUNS_DIR"
EOF
chmod +x "$RUNS_DIR/example.sh"
log "✅ Created runs/ directory with example script"
exit 0
fi
# Create logs directory
mkdir -p "$LOGS_DIR"
# Load environment
[[ -f "$ENV_FILE" ]] && source "$ENV_FILE"
# ═══════════════════════════════════════════════════════════
# 📝 PARSE ARGUMENTS
# ═══════════════════════════════════════════════════════════
DRY_RUN=0
VERBOSE=0
FILTERS=()
COMMAND=""
while [[ $# -gt 0 ]]; do
case "$1" in
--dry) DRY_RUN=1 ;;
--verbose) VERBOSE=1 ;;
--help | help)
show_help
exit 0
;;
push) COMMAND="push" ;;
deps | check) COMMAND="deps" ;;
-*)
error "Unknown option: $1"
show_help
exit 1
;;
*) FILTERS+=("$1") ;;
esac
shift
done
# ═══════════════════════════════════════════════════════════
# 🚦 HANDLE COMMANDS
# ═══════════════════════════════════════════════════════════
# Handle special commands first
case "$COMMAND" in
"push")
handle_push
exit 0
;;
"deps")
check_dependencies
exit 0
;;
"")
# No command, continue with script execution
;;
*)
error "Unknown command: $COMMAND"
show_help
exit 1
;;
esac
# ═══════════════════════════════════════════════════════════
# 🔍 FIND SCRIPTS TO RUN
# ═══════════════════════════════════════════════════════════
mapfile -t ALL_SCRIPTS < <(find "$RUNS_DIR" -type f -executable | sort)
if [[ ${#ALL_SCRIPTS[@]} -eq 0 ]]; then
warn "No executable scripts found in $RUNS_DIR"
exit 0
fi
SCRIPTS_TO_RUN=()
for script in "${ALL_SCRIPTS[@]}"; do
script_name="$(basename "$script")"
if matches_filters "$script_name" "${FILTERS[@]}"; then
SCRIPTS_TO_RUN+=("$script")
fi
done
if [[ ${#SCRIPTS_TO_RUN[@]} -eq 0 ]]; then
warn "No scripts match the given filters: ${FILTERS[*]}"
exit 0
fi
# ═══════════════════════════════════════════════════════════
# ⚠️ CONFIRMATION FOR RUNNING ALL SCRIPTS
# ═══════════════════════════════════════════════════════════
if [[ ${#FILTERS[@]} -eq 0 && $DRY_RUN -eq 0 ]]; then
echo -e "${RED}⚠️ About to run ALL scripts:${NC}"
for script in "${SCRIPTS_TO_RUN[@]}"; do
name="$(basename "$script")"
desc="$(get_description "$script")"
echo "$name - $desc"
done
echo
read -p "Continue? (y/N): " -r answer
if [[ ! "$answer" =~ ^[Yy]$ ]]; then
echo "Cancelled"
exit 0
fi
fi
# ═══════════════════════════════════════════════════════════
# 🚀 EXECUTE SCRIPTS
# ═══════════════════════════════════════════════════════════
EXECUTED=()
FAILED=()
for script in "${SCRIPTS_TO_RUN[@]}"; do
name="$(basename "$script")"
desc="$(get_description "$script")"
log_file="$LOGS_DIR/${name%.*}.log"
if [[ $DRY_RUN -eq 1 ]]; then
echo -e "${BLUE}[DRY]${NC} Would run: $name - $desc"
continue
fi
echo -e "\n${BLUE}${NC} Running: $name"
[[ "$desc" != "No description" ]] && echo " $desc"
if [[ $VERBOSE -eq 1 ]]; then
if "$script" 2>&1 | tee "$log_file"; then
log "$name completed"
EXECUTED+=("$name")
else
error "$name failed (see $log_file)"
FAILED+=("$name")
fi
else
if "$script" &>"$log_file"; then
log "$name completed"
EXECUTED+=("$name")
else
error "$name failed (see $log_file)"
FAILED+=("$name")
fi
fi
done
# ═══════════════════════════════════════════════════════════
# 📊 SUMMARY
# ═══════════════════════════════════════════════════════════
if [[ $DRY_RUN -eq 0 ]]; then
echo -e "\n${BLUE}═══ SUMMARY ═══${NC}"
echo "✅ Completed: ${#EXECUTED[@]}"
[[ ${#FAILED[@]} -gt 0 ]] && echo "❌ Failed: ${#FAILED[@]}"
if [[ ${#FAILED[@]} -gt 0 ]]; then
echo -e "\n${RED}Failed scripts:${NC}"
for failed in "${FAILED[@]}"; do
echo "$failed"
done
exit 1
fi
fi
exit 0