# Forgejo Actions (host-mode runner: docker CLI + node + rsync + ssh). # Secrets: DEPLOY_SSH_KEY, DEPLOY_SSH_HOST_KEY name: CI and deploy on: pull_request: push: branches: - main workflow_dispatch: inputs: skip_tests: description: Skip the Test job and deploy only required: false default: false type: boolean concurrency: group: tarakan-${{ github.workflow }}-${{ github.ref }} cancel-in-progress: false jobs: test: name: Test if: ${{ github.event_name != 'workflow_dispatch' || github.event.inputs.skip_tests != 'true' }} runs-on: ubuntu-latest env: MIX_ENV: test PG_CONTAINER: tarakan-ci-pg-${{ github.run_id }}-${{ github.run_attempt }} steps: - name: Check out source uses: https://data.forgejo.org/actions/checkout@v4 - name: Start Postgres run: | set -euo pipefail docker rm -f "$PG_CONTAINER" 2>/dev/null || true docker run -d --name "$PG_CONTAINER" --network host \ -e POSTGRES_USER=postgres \ -e POSTGRES_PASSWORD=postgres \ -e POSTGRES_DB=postgres \ postgres:16 for i in $(seq 1 30); do if docker exec "$PG_CONTAINER" pg_isready -U postgres >/dev/null 2>&1; then echo "postgres ready" exit 0 fi sleep 1 done echo "postgres failed to become ready" >&2 docker logs "$PG_CONTAINER" || true exit 1 - name: Run pre-commit checks run: | set -euo pipefail # Host-mode + host docker.sock: bind mounts must be host paths. # Workspace under /data in the runner → /opt/forgejo-runner/data on the host. case "$GITHUB_WORKSPACE" in /data/*) HOST_WORKSPACE="/opt/forgejo-runner/data${GITHUB_WORKSPACE#/data}" ;; *) HOST_WORKSPACE="$GITHUB_WORKSPACE" ;; esac test -f "$GITHUB_WORKSPACE/mix.exs" echo "HOST_WORKSPACE=$HOST_WORKSPACE" docker run --rm --network host \ --volume "$HOST_WORKSPACE:/app" \ --workdir /app \ --env MIX_ENV=test \ docker.io/hexpm/elixir:1.20.1-erlang-29.0.2-debian-trixie-20260610-slim \ bash -lc ' set -euo pipefail apt-get update apt-get install -y --no-install-recommends build-essential git openssh-client mix local.hex --force mix local.rebar --force mix deps.get mix ecto.create --quiet || true mix precommit ' - name: Stop Postgres if: always() run: docker rm -f "$PG_CONTAINER" 2>/dev/null || true deploy: name: Deploy production needs: test if: >- always() && github.ref == 'refs/heads/main' && github.event_name != 'pull_request' && (needs.test.result == 'success' || needs.test.result == 'skipped') runs-on: ubuntu-latest env: DEPLOY_HOST: 66.42.113.222 DEPLOY_USER: tarakan-deploy DEPLOY_PORT: "22" IMAGE: tarakan-app:${{ github.sha }} steps: - name: Check out source uses: https://data.forgejo.org/actions/checkout@v4 - name: Build production image run: | set -euo pipefail docker build --pull -f deploy/docker/Dockerfile --tag "$IMAGE" . - name: Package production image run: | set -euo pipefail mkdir -p /tmp docker save "$IMAGE" | gzip -1 > /tmp/tarakan-image.tar.gz ls -lh /tmp/tarakan-image.tar.gz - name: Configure deployment SSH env: DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }} DEPLOY_SSH_HOST_KEY: ${{ secrets.DEPLOY_SSH_HOST_KEY || '66.42.113.222 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINdLaUbq4l8akmbiTurBX3F1nTw8AxJZ1efIN3t2ptGq' }} run: | set -euo pipefail install -d -m 0700 ~/.ssh printf '%s\n' "$DEPLOY_SSH_KEY" | sed 's/\r$//' | sed 's/\\n/\n/g' > ~/.ssh/id_ed25519 chmod 0600 ~/.ssh/id_ed25519 : > ~/.ssh/known_hosts if [ -n "${DEPLOY_SSH_HOST_KEY:-}" ]; then printf '%s\n' "$DEPLOY_SSH_HOST_KEY" | sed 's/\r$//' | sed 's/\\n/\n/g' >> ~/.ssh/known_hosts fi ssh-keyscan -p "$DEPLOY_PORT" -T 5 -t ed25519,rsa \ "$DEPLOY_HOST" 2>/dev/null >> ~/.ssh/known_hosts || true ip=$(getent ahostsv4 "$DEPLOY_HOST" 2>/dev/null | awk '{print $1; exit}' || true) if [ -n "${ip:-}" ]; then ssh-keyscan -p "$DEPLOY_PORT" -T 5 -t ed25519,rsa "$ip" 2>/dev/null >> ~/.ssh/known_hosts || true fi chmod 0600 ~/.ssh/known_hosts echo "known_hosts:" cat ~/.ssh/known_hosts ssh-keygen -y -f ~/.ssh/id_ed25519 >/dev/null # Smoke-test auth (BatchMode never prompts) ssh -p "$DEPLOY_PORT" \ -i "$HOME/.ssh/id_ed25519" \ -o BatchMode=yes \ -o IdentitiesOnly=yes \ -o StrictHostKeyChecking=yes \ -o UserKnownHostsFile="$HOME/.ssh/known_hosts" \ "$DEPLOY_USER@$DEPLOY_HOST" 'echo ssh_ok; hostname' - name: Upload release run: | set -euo pipefail RSYNC_RSH="ssh -p ${DEPLOY_PORT} -i ${HOME}/.ssh/id_ed25519 -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile=${HOME}/.ssh/known_hosts" rsync -az --delete-delay \ -e "$RSYNC_RSH" \ --chown="$DEPLOY_USER:linuxuser" \ --chmod=D755,F644 \ --exclude='.git/' \ --exclude='.env' \ --exclude='.deploy/' \ --exclude='.secrets/' \ --exclude='backups/' \ --exclude='_build/' \ --exclude='deps/' \ --exclude='tmp/' \ --exclude='erl_crash.dump' \ --exclude='assets/node_modules/' \ --exclude='priv/hosted/' \ --exclude='priv/mirrors/' \ --exclude='priv/ssh/' \ --exclude='priv/static/assets/' \ --exclude='priv/static/cache_manifest.json' \ ./ "$DEPLOY_USER@$DEPLOY_HOST:/opt/tarakan/" ssh -p "$DEPLOY_PORT" \ -i "$HOME/.ssh/id_ed25519" \ -o BatchMode=yes \ -o IdentitiesOnly=yes \ -o StrictHostKeyChecking=yes \ -o UserKnownHostsFile="$HOME/.ssh/known_hosts" \ "$DEPLOY_USER@$DEPLOY_HOST" \ "chmod 755 /opt/tarakan/scripts/deploy/*.sh /opt/tarakan/ops/*.sh 2>/dev/null || true" rsync -az -e "$RSYNC_RSH" \ /tmp/tarakan-image.tar.gz \ "$DEPLOY_USER@$DEPLOY_HOST:/opt/tarakan/.deploy/tarakan-image.tar.gz" - name: Activate release run: | set -euo pipefail ssh -p "$DEPLOY_PORT" \ -i "$HOME/.ssh/id_ed25519" \ -o BatchMode=yes \ -o IdentitiesOnly=yes \ -o StrictHostKeyChecking=yes \ -o UserKnownHostsFile="$HOME/.ssh/known_hosts" \ "$DEPLOY_USER@$DEPLOY_HOST" \ "cd /opt/tarakan && ./scripts/deploy/deploy.sh '$IMAGE'" - name: Health check run: | set -euo pipefail ssh -p "$DEPLOY_PORT" \ -i "$HOME/.ssh/id_ed25519" \ -o BatchMode=yes \ -o IdentitiesOnly=yes \ -o StrictHostKeyChecking=yes \ -o UserKnownHostsFile="$HOME/.ssh/known_hosts" \ "$DEPLOY_USER@$DEPLOY_HOST" \ "docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Image}}' | head -20"