From 3e7463cf46de4653d849c897e418a133e38d9abc Mon Sep 17 00:00:00 2001 From: olsch01 Date: Thu, 9 Apr 2026 09:22:28 -0400 Subject: [PATCH 1/2] fix: replace curl with Docker health status and wget for health check The health check used curl which is not installed on the prod server. Replace with a dual approach: 1. Primary: check Docker's own container health status (already running via docker-compose.prod.yml healthcheck with wget inside container) 2. Secondary: wget from host as fallback signal Also add diagnostic logging (container status + recent backend logs) before triggering rollback on health check failure. Co-Authored-By: Claude Opus 4.6 --- scripts/deploy-prod.sh | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/scripts/deploy-prod.sh b/scripts/deploy-prod.sh index a329196..63df3d1 100755 --- a/scripts/deploy-prod.sh +++ b/scripts/deploy-prod.sh @@ -359,34 +359,42 @@ fi # ==================================================================== echo "" log "--- Step 5/6: Verifying application health ---" -log "Waiting ${HEALTH_START_WAIT}s for backend to initialize ..." +log "Waiting ${HEALTH_START_WAIT}s for backend to initialize (matches Docker start_period) ..." sleep "$HEALTH_START_WAIT" +# Primary check: Docker's own container health status +# (docker-compose.prod.yml already defines a healthcheck using wget inside the container) HEALTHY=false for ((i=1; i<=HEALTH_RETRIES; i++)); do - if curl -sf "$HEALTH_URL" >/dev/null 2>&1; then + CONTAINER_HEALTH=$($COMPOSE_CMD ps backend --format '{{.Health}}' 2>/dev/null || echo "unknown") + if [ "$CONTAINER_HEALTH" = "healthy" ]; then HEALTHY=true break fi - log " Health check attempt $i/$HEALTH_RETRIES failed, retrying in ${HEALTH_INTERVAL}s ..." + + # Also try a direct HTTP check from the host as a secondary signal + # Use wget (available on Ubuntu) since curl may not be installed + if wget -qO- --timeout=5 "$HEALTH_URL" >/dev/null 2>&1; then + HEALTHY=true + break + fi + + log " Health check attempt $i/$HEALTH_RETRIES — container status: ${CONTAINER_HEALTH}, retrying in ${HEALTH_INTERVAL}s ..." sleep "$HEALTH_INTERVAL" done if [ "$HEALTHY" = true ]; then - ok "Backend is healthy and responding at $HEALTH_URL" + ok "Backend is healthy and responding" else + # Log diagnostics before triggering rollback err "Backend failed to respond after $((HEALTH_START_WAIT + HEALTH_RETRIES * HEALTH_INTERVAL))s" + warn "Container status: $($COMPOSE_CMD ps backend 2>/dev/null || echo 'unknown')" + warn "Recent backend logs:" + $COMPOSE_CMD logs --tail=20 backend 2>/dev/null || true err "Triggering automatic rollback ..." exit 1 # trap will handle rollback fi -# Also verify the container reports healthy via Docker -if $COMPOSE_CMD ps backend 2>/dev/null | grep -q "healthy"; then - ok "Backend container health check: healthy" -else - warn "Backend container health status is not 'healthy' yet (may still be within start_period)" -fi - # ==================================================================== # STEP 7: Post-upgrade database backup # ==================================================================== -- 2.49.1 From 19bd19b0c48f8652bed4bbcfca3e9e9f242c4173 Mon Sep 17 00:00:00 2001 From: olsch01 Date: Thu, 9 Apr 2026 09:35:49 -0400 Subject: [PATCH 2/2] docs: add Gitea Actions runner setup guide for production server Step-by-step guide covering act_runner installation, registration, host execution mode configuration, systemd service setup, and troubleshooting for the HOALedgerIQ production deployment workflow. Co-Authored-By: Claude Sonnet 4.6 --- docs/gitea-runner-setup.md | 230 +++++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 docs/gitea-runner-setup.md diff --git a/docs/gitea-runner-setup.md b/docs/gitea-runner-setup.md new file mode 100644 index 0000000..651a56b --- /dev/null +++ b/docs/gitea-runner-setup.md @@ -0,0 +1,230 @@ +# Gitea Actions Runner Setup — HOALedgerIQ Production Server + +This guide walks through setting up a self-hosted Gitea Actions runner on the production server so the deployment workflow (`.gitea/workflows/deploy.yml`) can execute automatically. + +The runner uses **host execution mode** — jobs run directly on the server (not inside Docker containers) so the deploy script has access to Docker, the git repo, and the local filesystem. + +--- + +## Prerequisites + +- Ubuntu Linux production server +- Gitea instance (e.g., `https://git.sensetostyle.com`) +- Docker and Docker Compose installed on the server +- The HOALedgerIQ repo cloned at `/opt/hoa-ledgeriq` + +--- + +## Step 1: Enable Actions in Gitea + +Ensure Actions are enabled in your Gitea configuration (`/etc/gitea/app.ini`): + +```ini +[actions] +ENABLED = true +``` + +Restart Gitea after making changes: + +```bash +sudo systemctl restart gitea +``` + +--- + +## Step 2: Get a Registration Token + +1. Log into your Gitea instance +2. Navigate to **Site Administration** → **Actions** → **Runners** +3. Copy the **Registration Token** + +> **Tip:** For tighter security, you can get a repo-scoped token instead: +> Repo → **Settings** → **Actions** → **Runners** → copy the token shown there. +> This limits the runner to only execute workflows from that specific repository. + +--- + +## Step 3: Install the Act Runner Binary + +```bash +# Download the latest act_runner for x86_64 Linux +wget https://dl.gitea.com/act_runner/latest/act_runner-linux-amd64 + +# Make executable and install to system path +chmod +x act_runner-linux-amd64 +sudo mv act_runner-linux-amd64 /usr/local/bin/act_runner + +# Verify installation +act_runner --version +``` + +> For ARM64 servers, use `act_runner-linux-arm64` instead. + +--- + +## Step 4: Generate and Edit the Configuration + +```bash +sudo mkdir -p /etc/act_runner +act_runner generate-config > /tmp/config.yaml +``` + +Edit `/tmp/config.yaml` and set the **labels to use host execution mode**: + +```yaml +runner: + labels: + - "ubuntu-latest:host" + - "ubuntu-22.04:host" +``` + +The `:host` suffix tells the runner to execute jobs directly on the server rather than spinning up Docker containers. This is required because the deploy script needs access to: + +- The Docker socket (to run `docker compose`) +- The git repository at `/opt/hoa-ledgeriq` +- The backup scripts and database + +Move the config into place and lock down permissions: + +```bash +sudo mv /tmp/config.yaml /etc/act_runner/config.yaml +sudo chmod 600 /etc/act_runner/config.yaml +``` + +--- + +## Step 5: Register the Runner + +```bash +act_runner register \ + --no-interactive \ + --instance "https://git.sensetostyle.com" \ + --token "YOUR_REGISTRATION_TOKEN_HERE" \ + --name "hoaledgeriq-prod" \ + --labels "ubuntu-latest:host,ubuntu-22.04:host" \ + --config /etc/act_runner/config.yaml +``` + +This creates a `.runner` file in the current directory containing the registration state. + +> **Interactive alternative:** Run `act_runner register --config /etc/act_runner/config.yaml` and follow the prompts. + +--- + +## Step 6: Set Up as a Systemd Service + +Create the service file at `/etc/systemd/system/act_runner.service`: + +```ini +[Unit] +Description=Gitea Actions Runner (HOALedgerIQ Prod) +Documentation=https://docs.gitea.com/usage/actions/act-runner +After=docker.service network-online.target + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/hoa-ledgeriq +ExecStart=/usr/local/bin/act_runner daemon --config /etc/act_runner/config.yaml +Restart=always +RestartSec=10 +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=multi-user.target +``` + +> **Security note on `User=root`:** The deploy script needs to run `docker compose`, `git reset --hard`, etc. If you have a dedicated deploy user in the `docker` group with write access to `/opt/hoa-ledgeriq`, use that instead. Running as root is the simplest option but grants maximum privileges. + +Enable and start the service: + +```bash +sudo systemctl daemon-reload +sudo systemctl enable act_runner +sudo systemctl start act_runner +``` + +--- + +## Step 7: Verify the Runner Is Online + +Check the service is running: + +```bash +sudo systemctl status act_runner +``` + +View logs: + +```bash +sudo journalctl -u act_runner -f +``` + +Then confirm in Gitea: + +1. Go to **Site Administration** → **Actions** → **Runners** +2. You should see **"hoaledgeriq-prod"** listed with status **Online** + +--- + +## Step 8: Test the Workflow + +1. Go to your repo on Gitea → **Actions** tab +2. Select the **"Deploy to Production"** workflow +3. Click **Run Workflow** +4. If this is the first deployment against an existing database, check the **"Mark existing migrations as applied"** box +5. Monitor the run in the Actions tab + +--- + +## Troubleshooting + +### Runner shows as Offline + +```bash +# Check service status and logs +sudo systemctl status act_runner +sudo journalctl -u act_runner -n 50 + +# Verify the instance URL is reachable from the server +wget -qO- https://git.sensetostyle.com/api/v1/version +``` + +### Workflow stuck on "Waiting for runner" + +- Verify the runner labels match what the workflow expects. The workflow uses `runs-on: ubuntu-latest` which must match the `ubuntu-latest:host` label. +- Check the runner is registered at the correct scope (instance-wide, org-level, or repo-level). + +### Permission denied errors during deploy + +- Ensure the systemd service `User` has Docker access (`usermod -aG docker `) +- Ensure the user has write access to `/opt/hoa-ledgeriq` + +### Re-registering after token expiry + +```bash +sudo systemctl stop act_runner +# Get a new token from Gitea admin panel, then: +act_runner register \ + --no-interactive \ + --instance "https://git.sensetostyle.com" \ + --token "NEW_TOKEN_HERE" \ + --name "hoaledgeriq-prod" \ + --labels "ubuntu-latest:host,ubuntu-22.04:host" \ + --config /etc/act_runner/config.yaml +sudo systemctl start act_runner +``` + +--- + +## Security Best Practices + +| Concern | Recommendation | +|---------|----------------| +| Runner user | Use a dedicated user with `docker` group access rather than `root` when possible | +| Registration token | Rotate periodically in the Gitea admin panel | +| Config file | Keep `/etc/act_runner/config.yaml` at mode `600` (owner-read only) | +| Runner scope | Register at the **repo level** instead of instance-wide so only this repo can trigger deployments | +| Workflow triggers | The deploy workflow uses `workflow_dispatch` (manual only) — no automatic triggers on push | +| Network | Ensure Gitea is accessed over HTTPS with valid SSL certificates | -- 2.49.1