Fix deploy pipeline and add missing event registrations migration.
Deploy Ladill Events / deploy (push) Failing after 20s
Deploy Ladill Events / deploy (push) Failing after 20s
Correct CI workflow for events.ladill.com, default deploy paths, nginx provisioning step, and qr_event_registrations table migration. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
name: Deploy Ladill QR Plus
|
name: Deploy Ladill Events
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -10,7 +10,7 @@ permissions:
|
|||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: deploy-qr-plus
|
group: deploy-events
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -19,9 +19,9 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
NODE_ROOT: /tmp/ladill-node-22-r1
|
NODE_ROOT: /tmp/ladill-node-22-r1
|
||||||
NODE_VERSION: "22.14.0"
|
NODE_VERSION: "22.14.0"
|
||||||
RELEASE_ARCHIVE: /tmp/ladill-qr-plus-release-${{ gitea.run_id }}-${{ gitea.run_attempt }}.tgz
|
RELEASE_ARCHIVE: /tmp/ladill-events-release-${{ gitea.run_id }}-${{ gitea.run_attempt }}.tgz
|
||||||
WORKSPACE: /tmp/${{ gitea.repository_owner }}-qr-plus-${{ gitea.run_id }}-${{ gitea.run_attempt }}
|
WORKSPACE: /tmp/${{ gitea.repository_owner }}-events-${{ gitea.run_id }}-${{ gitea.run_attempt }}
|
||||||
LADILL_APP_ROOT: /var/www/ladill-qr-plus
|
LADILL_APP_ROOT: /var/www/ladill-events
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
shell: bash {0}
|
shell: bash {0}
|
||||||
@@ -78,12 +78,26 @@ jobs:
|
|||||||
- name: Deploy release
|
- name: Deploy release
|
||||||
shell: bash {0}
|
shell: bash {0}
|
||||||
env:
|
env:
|
||||||
LADILL_RELEASE_ARCHIVE: /tmp/ladill-qr-plus-release-${{ gitea.run_id }}-${{ gitea.run_attempt }}.tgz
|
LADILL_RELEASE_ARCHIVE: /tmp/ladill-events-release-${{ gitea.run_id }}-${{ gitea.run_attempt }}.tgz
|
||||||
run: |
|
run: |
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
: "${LADILL_APP_ROOT:?Set LADILL_APP_ROOT}"
|
: "${LADILL_APP_ROOT:?Set LADILL_APP_ROOT}"
|
||||||
bash "$WORKSPACE/deploy/deploy.sh"
|
bash "$WORKSPACE/deploy/deploy.sh"
|
||||||
|
|
||||||
|
- name: Ensure nginx vhost
|
||||||
|
shell: bash {0}
|
||||||
|
run: |
|
||||||
|
set -Eeuo pipefail
|
||||||
|
NGINX_SCRIPT="$WORKSPACE/deployment/setup-service-subdomain-nginx.sh"
|
||||||
|
if [ ! -f "$NGINX_SCRIPT" ]; then
|
||||||
|
NGINX_SCRIPT="/var/www/ladill.com/current/deployment/setup-service-subdomain-nginx.sh"
|
||||||
|
fi
|
||||||
|
if [ -f "$NGINX_SCRIPT" ]; then
|
||||||
|
sudo bash "$NGINX_SCRIPT" events --app /var/www/ladill-events/current
|
||||||
|
else
|
||||||
|
echo "WARN: setup-service-subdomain-nginx.sh not found — configure events.ladill.com vhost manually"
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Cleanup
|
- name: Cleanup
|
||||||
if: always()
|
if: always()
|
||||||
shell: bash {0}
|
shell: bash {0}
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('qr_event_registrations', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignId('qr_code_id')->constrained('qr_codes')->cascadeOnDelete();
|
||||||
|
$table->foreignId('user_id')->constrained('users')->cascadeOnDelete(); // organizer
|
||||||
|
$table->string('reference', 40)->unique();
|
||||||
|
$table->string('badge_code', 16)->unique();
|
||||||
|
$table->string('tier_name', 80);
|
||||||
|
$table->unsignedInteger('amount_minor')->default(0);
|
||||||
|
$table->string('currency', 3)->default('GHS');
|
||||||
|
$table->string('attendee_name', 120);
|
||||||
|
$table->string('attendee_email', 200)->nullable();
|
||||||
|
$table->string('attendee_phone', 30)->nullable();
|
||||||
|
$table->json('badge_fields')->nullable();
|
||||||
|
$table->string('status', 20)->default('pending'); // pending | confirmed | failed | cancelled
|
||||||
|
$table->string('payment_reference', 60)->nullable();
|
||||||
|
$table->timestamp('paid_at')->nullable();
|
||||||
|
$table->timestamp('checked_in_at')->nullable();
|
||||||
|
$table->json('metadata')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->index(['qr_code_id', 'status']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('qr_event_registrations');
|
||||||
|
}
|
||||||
|
};
|
||||||
+2
-2
@@ -2,7 +2,7 @@
|
|||||||
# Fast on-host release deploy (same model as climpme/web/deploy/deploy.sh).
|
# Fast on-host release deploy (same model as climpme/web/deploy/deploy.sh).
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
APP_ROOT="${LADILL_APP_ROOT:-/var/www/ladill-qr-plus}"
|
APP_ROOT="${LADILL_APP_ROOT:-/var/www/ladill-events}"
|
||||||
RELEASES_DIR="$APP_ROOT/releases"
|
RELEASES_DIR="$APP_ROOT/releases"
|
||||||
SHARED_DIR="$APP_ROOT/shared"
|
SHARED_DIR="$APP_ROOT/shared"
|
||||||
CURRENT_LINK="$APP_ROOT/current"
|
CURRENT_LINK="$APP_ROOT/current"
|
||||||
@@ -230,7 +230,7 @@ if [ -L "$CURRENT_LINK" ] && [ -f "$CURRENT_LINK/artisan" ]; then
|
|||||||
(cd "$CURRENT_LINK" && php artisan queue:restart) || true
|
(cd "$CURRENT_LINK" && php artisan queue:restart) || true
|
||||||
fi
|
fi
|
||||||
if command -v supervisorctl >/dev/null 2>&1; then
|
if command -v supervisorctl >/dev/null 2>&1; then
|
||||||
supervisorctl restart ladill-qr-plus-worker:* 2>/dev/null || true
|
supervisorctl restart ladill-events-worker:* 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log "Cleaning old releases"
|
log "Cleaning old releases"
|
||||||
|
|||||||
Executable
+153
@@ -0,0 +1,153 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Provision an nginx vhost for one of Ladill's own service subdomains
|
||||||
|
# (auth./account./pay./qr./... .ladill.com), reusing the existing
|
||||||
|
# *.ladill.com wildcard TLS certificate.
|
||||||
|
#
|
||||||
|
# The monolith phase needs nothing here — the ladill.com vhost already
|
||||||
|
# serves *.ladill.com over the wildcard cert. Use this when a service gets
|
||||||
|
# its OWN app (separate Laravel app dir, or a backend on another host/port),
|
||||||
|
# so its subdomain stops falling through to the catch-all and routes to the
|
||||||
|
# real service instead.
|
||||||
|
#
|
||||||
|
# Idempotent + safe: writes to sites-available, validates with `nginx -t`,
|
||||||
|
# and only reloads on success (restores the previous config on failure).
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# setup-service-subdomain-nginx.sh <subdomain> --app <APP_DIR> [--fpm <SOCK>]
|
||||||
|
# setup-service-subdomain-nginx.sh <subdomain> --proxy <URL>
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
# --app DIR Serve a Laravel app from DIR/public via PHP-FPM.
|
||||||
|
# --fpm SOCK PHP-FPM socket for --app (default: /run/php/php8.4-fpm-ladill.sock).
|
||||||
|
# --proxy URL Reverse-proxy the subdomain to URL (e.g. http://127.0.0.1:9001).
|
||||||
|
# --zone ZONE Apex zone (default: ladill.com).
|
||||||
|
# --cert NAME Let's Encrypt lineage under /etc/letsencrypt/live (default: ladill-wildcard).
|
||||||
|
# --dry-run Print the rendered vhost and exit without writing anything.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# setup-service-subdomain-nginx.sh auth --app /var/www/auth.ladill.com/current
|
||||||
|
# setup-service-subdomain-nginx.sh pay --proxy http://127.0.0.1:9002
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SUB="${1:-}"; shift || true
|
||||||
|
[ -n "$SUB" ] || { echo "ERROR: missing <subdomain> (e.g. 'auth')"; exit 2; }
|
||||||
|
|
||||||
|
MODE=""; APP_DIR=""; PROXY_URL=""
|
||||||
|
FPM_SOCK="/run/php/php8.4-fpm-ladill.sock"
|
||||||
|
ZONE="ladill.com"
|
||||||
|
CERT="ladill-wildcard"
|
||||||
|
DRY_RUN=0
|
||||||
|
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--app) MODE="app"; APP_DIR="${2:?--app needs a dir}"; shift 2;;
|
||||||
|
--proxy) MODE="proxy"; PROXY_URL="${2:?--proxy needs a url}"; shift 2;;
|
||||||
|
--fpm) FPM_SOCK="${2:?}"; shift 2;;
|
||||||
|
--zone) ZONE="${2:?}"; shift 2;;
|
||||||
|
--cert) CERT="${2:?}"; shift 2;;
|
||||||
|
--dry-run) DRY_RUN=1; shift;;
|
||||||
|
*) echo "ERROR: unknown arg '$1'"; exit 2;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
[ -n "$MODE" ] || { echo "ERROR: choose a backend: --app <DIR> or --proxy <URL>"; exit 2; }
|
||||||
|
|
||||||
|
FQDN="${SUB}.${ZONE}"
|
||||||
|
CERT_DIR="/etc/letsencrypt/live/${CERT}"
|
||||||
|
WEBROOT="${APP_DIR:-/var/www/${ZONE}/current}/public"
|
||||||
|
|
||||||
|
if [ "$MODE" = "app" ]; then
|
||||||
|
BODY=$(cat <<EOF
|
||||||
|
root ${APP_DIR}/public;
|
||||||
|
index index.php;
|
||||||
|
|
||||||
|
location / { try_files \$uri \$uri/ /index.php?\$query_string; }
|
||||||
|
|
||||||
|
location ~ \.php\$ {
|
||||||
|
include snippets/fastcgi-php.conf;
|
||||||
|
fastcgi_pass unix:${FPM_SOCK};
|
||||||
|
fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /\.(?!well-known).* { deny all; }
|
||||||
|
|
||||||
|
location ~* \.(?:css|js|jpg|jpeg|gif|png|svg|webp|ico|woff2?)\$ {
|
||||||
|
expires 30d; access_log off;
|
||||||
|
add_header Cache-Control "public, max-age=2592000";
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
else
|
||||||
|
BODY=$(cat <<EOF
|
||||||
|
location / {
|
||||||
|
proxy_pass ${PROXY_URL};
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host \$host;
|
||||||
|
proxy_set_header X-Real-IP \$remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
VHOST=$(cat <<EOF
|
||||||
|
# Managed by setup-service-subdomain-nginx.sh — Ladill service subdomain.
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
server_name ${FQDN};
|
||||||
|
location ^~ /.well-known/acme-challenge/ { root ${WEBROOT}; allow all; }
|
||||||
|
location / { return 301 https://\$host\$request_uri; }
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
listen [::]:443 ssl http2;
|
||||||
|
server_name ${FQDN};
|
||||||
|
client_max_body_size 64M;
|
||||||
|
|
||||||
|
ssl_certificate ${CERT_DIR}/fullchain.pem;
|
||||||
|
ssl_certificate_key ${CERT_DIR}/privkey.pem;
|
||||||
|
include /etc/letsencrypt/options-ssl-nginx.conf;
|
||||||
|
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
||||||
|
|
||||||
|
${BODY}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
if [ "$DRY_RUN" = "1" ]; then
|
||||||
|
echo "# --- rendered vhost for ${FQDN} (dry-run, not written) ---"
|
||||||
|
echo "$VHOST"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -d "$CERT_DIR" ] || { echo "ERROR: cert lineage not found: $CERT_DIR (run certbot for ${CERT} first)"; exit 1; }
|
||||||
|
|
||||||
|
AVAIL="/etc/nginx/sites-available/${FQDN}.conf"
|
||||||
|
ENABLED="/etc/nginx/sites-enabled/${FQDN}.conf"
|
||||||
|
|
||||||
|
if [ -f "$AVAIL" ]; then
|
||||||
|
BK="${AVAIL}.bak.$(date +%Y%m%d%H%M%S)"
|
||||||
|
cp -a "$AVAIL" "$BK"
|
||||||
|
echo ">> Backed up existing vhost to $BK"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s\n' "$VHOST" > "$AVAIL"
|
||||||
|
ln -sfn "$AVAIL" "$ENABLED"
|
||||||
|
|
||||||
|
if nginx -t; then
|
||||||
|
systemctl reload nginx
|
||||||
|
echo ">> ${FQDN} vhost active (${MODE})."
|
||||||
|
else
|
||||||
|
echo "ERROR: nginx config test failed — rolling back."
|
||||||
|
if [ -n "${BK:-}" ] && [ -f "${BK:-}" ]; then
|
||||||
|
cp -a "$BK" "$AVAIL"
|
||||||
|
else
|
||||||
|
rm -f "$AVAIL" "$ENABLED"
|
||||||
|
fi
|
||||||
|
nginx -t && systemctl reload nginx || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user