name: Deploy on: push: branches: - main workflow_dispatch: jobs: deploy: runs-on: self-hosted steps: # Checkout code - name: Checkout uses: actions/checkout@v4 # Setup SSH for services server - name: Setup SSH shell: bash env: SERVICE_SSH_KEY: ${{ secrets.SERVICE_SSH_KEY }} SERVICE_HOST: ${{ secrets.SERVICE_HOST }} run: | set -euo pipefail if ! command -v ssh >/dev/null 2>&1; then if command -v apk >/dev/null 2>&1; then apk add --no-cache openssh-client elif command -v apt-get >/dev/null 2>&1; then apt-get update apt-get install -y openssh-client else echo "ssh client not found and no known package manager available" >&2 exit 1 fi fi : "${SERVICE_HOST:?Missing secret SERVICE_HOST}" : "${SERVICE_SSH_KEY:?Missing secret SERVICE_SSH_KEY}" SERVICE_IP="$(getent ahostsv4 "$SERVICE_HOST" | awk '{print $1; exit}')" if [[ -z "${SERVICE_IP}" ]]; then echo "ERROR: Could not resolve IPv4 for $SERVICE_HOST" >&2 getent hosts "$SERVICE_HOST" || true exit 1 fi timeout 5 bash -lc "&2 exit 1 } mkdir -p ~/.ssh printf '%s\n' "$SERVICE_SSH_KEY" | tr -d '\r' > ~/.ssh/id_ed25519 chmod 600 ~/.ssh/id_ed25519 ssh-keyscan -4 -T 5 -H "$SERVICE_HOST" "$SERVICE_IP" >> ~/.ssh/known_hosts || { echo "ERROR: ssh-keyscan failed for $SERVICE_HOST ($SERVICE_IP)" >&2 exit 1 } # Deploy infra-controller to services server - name: Deploy infra-controller (services server) shell: bash env: SERVICE_HOST: ${{ secrets.SERVICE_HOST }} SERVICE_USER: ${{ secrets.SERVICE_USER }} run: | set -euo pipefail : "${SERVICE_HOST:?Missing secret SERVICE_HOST}" : "${SERVICE_USER:?Missing secret SERVICE_USER}" GIT_SHA="${{ github.sha }}" REPO_URL="$(git remote get-url origin)" echo "Deploying infra-controller ($GIT_SHA) to $SERVICE_USER@$SERVICE_HOST ..." ssh -i ~/.ssh/id_ed25519 "$SERVICE_USER@$SERVICE_HOST" /bin/sh -lc "set -euo pipefail if ! command -v sudo >/dev/null 2>&1; then echo 'ERROR: sudo not installed on services server' >&2 exit 1 fi if ! sudo -n /usr/bin/git --version >/dev/null 2>&1; then echo 'ERROR: passwordless sudo is required for CI deploy (configure NOPASSWD for this SSH user and required commands)' >&2 exit 1 fi if ! getent hosts "$(hostname)" >/dev/null 2>&1; then sudo -n /usr/bin/python3 -c 'import os from pathlib import Path hn = os.popen("hostname").read().strip() p = Path("/etc/hosts") t = p.read_text(encoding="utf-8") if p.exists() else "" lines = t.splitlines() ok = any((ln.strip() and not ln.lstrip().startswith("#") and hn in ln.split()) for ln in lines) if not ok: if t and not t.endswith("\n"): t += "\n" t += f"127.0.1.1 {hn}\n" p.write_text(t, encoding="utf-8") ' fi sudo -n mkdir -p /opt/infra-controller /etc/infra-controller /var/lib/infra-controller /var/log/infra-controller if [ ! -d /opt/infra-controller/.git ]; then sudo -n rm -rf /opt/infra-controller/* sudo -n git clone '$REPO_URL' /opt/infra-controller fi cd /opt/infra-controller sudo -n git fetch --all --prune sudo -n git checkout -f '$GIT_SHA' if [ ! -d /opt/infra-controller/venv ]; then sudo -n python3 -m venv /opt/infra-controller/venv fi sudo -n /opt/infra-controller/venv/bin/pip install --upgrade pip sudo -n /opt/infra-controller/venv/bin/pip install -e . if [ ! -f /etc/infra-controller/config.toml ]; then sudo -n cp config/controller.toml.example /etc/infra-controller/config.toml fi if [ ! -f /etc/infra-controller/controller.env ]; then sudo -n cp systemd/infra-controller.env /etc/infra-controller/controller.env fi sudo -n cp systemd/infra-controller.service /etc/systemd/system/ sudo -n cp systemd/infra-controller-once.service /etc/systemd/system/ sudo -n cp systemd/infra-controller-watch.service /etc/systemd/system/ sudo -n systemctl daemon-reload sudo -n systemctl disable --now infra-controller.path 2>/dev/null || true sudo -n systemctl enable --now infra-controller-watch.service sudo -n systemctl restart infra-controller-watch.service sudo -n chown -R infractl:infractl /opt/infra-controller /var/lib/infra-controller /var/log/infra-controller || true /opt/infra-controller/venv/bin/infra-controller --once "