#!/usr/bin/env bash set -euo pipefail export DEBIAN_FRONTEND=noninteractive echo "== Base packages ==" if command -v apt-get >/dev/null 2>&1; then apt-get update apt-get install -y \ bash \ ca-certificates \ curl \ git \ jq \ unzip \ wget \ python3 \ python3-pip \ python3-venv \ shellcheck else echo "This script currently expects an apt-based environment." exit 1 fi echo "== yq ==" if ! command -v yq >/dev/null 2>&1; then YQ_VERSION="v4.44.3" ARCH="$(dpkg --print-architecture)" case "$ARCH" in amd64) YQ_ARCH="amd64" ;; arm64) YQ_ARCH="arm64" ;; *) echo "Unsupported architecture: $ARCH"; exit 1 ;; esac wget -qO /usr/local/bin/yq "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_${YQ_ARCH}" chmod +x /usr/local/bin/yq fi echo "== Docker CLI + Compose plugin ==" if ! command -v docker >/dev/null 2>&1; then apt-get install -y docker.io docker-compose-v2 || true fi echo "== Python tooling ==" python3 -m pip install --break-system-packages --upgrade pip python3 -m pip install --break-system-packages \ yamllint \ ansible \ ansible-lint echo "== Terraform ==" if ! command -v terraform >/dev/null 2>&1; then TF_VERSION="1.8.5" ARCH="$(dpkg --print-architecture)" case "$ARCH" in amd64) TF_ARCH="amd64" ;; arm64) TF_ARCH="arm64" ;; *) echo "Unsupported architecture: $ARCH"; exit 1 ;; esac wget -qO /tmp/terraform.zip \ "https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_linux_${TF_ARCH}.zip" unzip -o /tmp/terraform.zip -d /tmp install /tmp/terraform /usr/local/bin/terraform fi echo "== TFLint ==" if ! command -v tflint >/dev/null 2>&1; then TFLINT_VERSION="v0.56.0" ARCH="$(dpkg --print-architecture)" case "$ARCH" in amd64) TFLINT_ARCH="amd64" ;; arm64) TFLINT_ARCH="arm64" ;; *) echo "Unsupported architecture: $ARCH"; exit 1 ;; esac wget -qO /tmp/tflint.zip \ "https://github.com/terraform-linters/tflint/releases/download/${TFLINT_VERSION}/tflint_linux_${TFLINT_ARCH}.zip" unzip -o /tmp/tflint.zip -d /tmp install /tmp/tflint /usr/local/bin/tflint fi echo "== Dummy secret material for compose validation ==" REPO_ROOT="${CODEX_REPO_DIR:-$PWD}" SECRETS_DIR="$REPO_ROOT/secrets" INVENTORY_JSON="$SECRETS_DIR/inventory.json" EXAMPLE_ENV="$SECRETS_DIR/.env.secrets.example" STACK_ENV="$SECRETS_DIR/stack-secrets.env" if [[ ! -f "$INVENTORY_JSON" ]]; then echo "Missing inventory file: $INVENTORY_JSON" exit 1 fi if [[ ! -f "$EXAMPLE_ENV" ]]; then echo "Missing example env file: $EXAMPLE_ENV" exit 1 fi mkdir -p "$SECRETS_DIR" dummy_value_for_key() { local key="$1" case "$key" in *EMAIL* ) echo "dummy@example.com" ;; *DB_USER* ) echo "dummyuser" ;; *USERNAME*|*USER* ) echo "dummy-user" ;; *DOMAIN* ) echo "example.lan.ddnsgeek.com" ;; *TZ ) echo "Australia/Brisbane" ;; *URL* ) echo "https://example.lan.ddnsgeek.com" ;; *PORT* ) echo "1234" ;; *PASSWORD*|*PASS*|*TOKEN*|*SECRET*|*KEY*|*JWT* ) echo "dummy-${key,,}" ;; *FINGERPRINT* ) echo "0000000000000000000000000000000000000000" ;; *DB_NAME* ) echo "dummydb" ;; *NAME* ) echo "dummy-name" ;; *ADDRESS* ) echo "dummy" ;; * ) echo "dummy-value" ;; esac } render_dummy_stack_env() { cp "$EXAMPLE_ENV" "$STACK_ENV.tmp" while IFS= read -r var; do [[ -z "$var" ]] && continue dummy="$(dummy_value_for_key "$var")" if grep -Eq "^[[:space:]]*${var}=" "$STACK_ENV.tmp"; then sed -i "s|^[[:space:]]*${var}=.*|${var}=${dummy}|" "$STACK_ENV.tmp" else printf '%s=%s\n' "$var" "$dummy" >> "$STACK_ENV.tmp" fi done < <(jq -r '.env_template_variables[].variable' "$INVENTORY_JSON") mv "$STACK_ENV.tmp" "$STACK_ENV" chmod 600 "$STACK_ENV" || true } ensure_dummy_secret_files() { jq -r '.file_based_secrets[].path' "$INVENTORY_JSON" | while IFS= read -r relpath; do [[ -z "$relpath" ]] && continue abspath="$REPO_ROOT/$relpath" mkdir -p "$(dirname "$abspath")" if [[ ! -f "$abspath" ]]; then printf 'dummy-secret\n' > "$abspath" fi chmod 600 "$abspath" || true done } render_dummy_stack_env ensure_dummy_secret_files ANSIBLE_DIR="$REPO_ROOT/infrastructure/ansible" ANSIBLE_CONFIG="$ANSIBLE_DIR/ansible.cfg" ANSIBLE_COLLECTIONS_REQ="$ANSIBLE_DIR/collections/requirements.yml" ANSIBLE_INVENTORY="$ANSIBLE_DIR/inventory/hosts.yml" ANSIBLE_PING_PLAYBOOK="$ANSIBLE_DIR/playbooks/ping.yml" if [[ -f "$ANSIBLE_COLLECTIONS_REQ" ]]; then echo "== Ansible collections (bootstrap) ==" ansible-galaxy collection install -r "$ANSIBLE_COLLECTIONS_REQ" -p "$ANSIBLE_DIR/collections" || true fi if command -v ansible >/dev/null 2>&1; then echo "== Ansible bootstrap validation ==" ANSIBLE_CONFIG="$ANSIBLE_CONFIG" ansible --version | head -n 1 || true if command -v ansible-lint >/dev/null 2>&1; then ansible-lint --version || true else echo "ansible-lint not available; skipping version check" fi if [[ -f "$ANSIBLE_INVENTORY" ]]; then ANSIBLE_CONFIG="$ANSIBLE_CONFIG" \ ansible-inventory -i "$ANSIBLE_INVENTORY" --list > /dev/null || true fi if [[ -f "$ANSIBLE_PING_PLAYBOOK" && -f "$ANSIBLE_INVENTORY" ]]; then ANSIBLE_CONFIG="$ANSIBLE_CONFIG" \ ansible-playbook -i "$ANSIBLE_INVENTORY" "$ANSIBLE_PING_PLAYBOOK" --syntax-check || true fi fi echo echo "== Installed versions ==" bash --version | head -n 1 || true git --version || true docker --version || true docker compose version || true python3 --version || true ansible --version | head -n 1 || true ansible-lint --version || true terraform version | head -n 1 || true tflint --version || true shellcheck --version | head -n 1 || true yamllint --version || true yq --version || true jq --version || true echo echo "== Dummy secret files prepared ==" echo "$STACK_ENV" jq -r '.file_based_secrets[].path' "$INVENTORY_JSON" || true