#!/usr/bin/env bash set -euo pipefail # === METADATA === SCRIPT_NAME="$(basename "$0")" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" SKILL_ROOT="$(dirname "$SCRIPT_DIR")" # === CONFIGURATION === : "${NODE_NAME:=node-a}" : "${GITOPS_ROOT:=$HOME/infrastructure}" : "${OUTPUT_DIR:=$SKILL_ROOT/outputs}" # === FUNCTIONS === log_info() { echo "[INFO] $(date -Iseconds) $*"; } log_warn() { echo "[WARN] $(date -Iseconds) $*" >&2; } log_error() { echo "[ERROR] $(date -Iseconds) $*" >&2; } die() { log_error "$@"; exit 1; } preflight() { # Expand ~ in GITOPS_ROOT GITOPS_ROOT="${GITOPS_ROOT/#\~/$HOME}" [[ -d "$OUTPUT_DIR" ]] || mkdir -p "$OUTPUT_DIR" [[ -d "$GITOPS_ROOT" ]] || mkdir -p "$GITOPS_ROOT" } create_bare_repo() { local name="$1" local description="$2" local repo_path="$GITOPS_ROOT/${name}.git" if [[ -d "$repo_path" ]]; then log_info "Repository $name.git already exists - skipping" return 0 fi log_info "Creating bare repository: $name.git" git init --bare "$repo_path" # Set description echo "$description" > "$repo_path/description" # Create initial branch structure by setting HEAD git -C "$repo_path" symbolic-ref HEAD refs/heads/main log_info "Created $repo_path" } create_hook() { local repo="$1" local hook_name="$2" local hook_content="$3" local hook_path="$GITOPS_ROOT/${repo}.git/hooks/$hook_name" if [[ -f "$hook_path" ]] && [[ -x "$hook_path" ]]; then log_info "Hook $hook_name for $repo already exists - skipping" return 0 fi echo "$hook_content" > "$hook_path" chmod +x "$hook_path" log_info "Created hook: $repo.git/hooks/$hook_name" } setup_hooks() { # Config repo hook - validate YAML create_hook "config" "post-receive" '#!/usr/bin/env bash # Post-receive hook for config repository # Validates YAML files on push echo "[config-hook] Validating pushed files..." while read oldrev newrev refname; do if [[ "$newrev" == "0000000000000000000000000000000000000000" ]]; then continue # Branch deleted fi # List changed files files=$(git diff-tree --no-commit-id --name-only -r "$newrev" 2>/dev/null || echo "") for file in $files; do if [[ "$file" == *.yml ]] || [[ "$file" == *.yaml ]]; then echo "[config-hook] YAML file changed: $file" fi done done echo "[config-hook] Validation complete" ' # Secrets repo hook - verify GPG create_hook "secrets" "post-receive" '#!/usr/bin/env bash # Post-receive hook for secrets repository # Verifies that pushed files are GPG encrypted echo "[secrets-hook] Verifying encryption..." while read oldrev newrev refname; do if [[ "$newrev" == "0000000000000000000000000000000000000000" ]]; then continue fi files=$(git diff-tree --no-commit-id --name-only -r "$newrev" 2>/dev/null || echo "") for file in $files; do if [[ "$file" == *.gpg ]]; then echo "[secrets-hook] Encrypted file: $file" elif [[ "$file" != ".gitignore" ]] && [[ "$file" != "README"* ]]; then echo "[secrets-hook] WARNING: Unencrypted file detected: $file" fi done done echo "[secrets-hook] Verification complete" ' # Manifests repo hook - validate manifests create_hook "manifests" "post-receive" '#!/usr/bin/env bash # Post-receive hook for manifests repository # Validates Kubernetes/deployment manifests on push echo "[manifests-hook] Validating manifests..." while read oldrev newrev refname; do if [[ "$newrev" == "0000000000000000000000000000000000000000" ]]; then continue fi files=$(git diff-tree --no-commit-id --name-only -r "$newrev" 2>/dev/null || echo "") for file in $files; do if [[ "$file" == *.yml ]] || [[ "$file" == *.yaml ]]; then echo "[manifests-hook] Manifest changed: $file" fi done done echo "[manifests-hook] Validation complete" ' } generate_manifest() { local manifest="$OUTPUT_DIR/gitops_manifest.json" cat > "$manifest" <