feat(docker): add webhook to auto-redeploy on master commit, remove old docker-related files
Build and deploy / deploy (push) Successful in 35s
Build and deploy / deploy (push) Successful in 35s
This commit is contained in:
+4
-5
@@ -1,4 +1,4 @@
|
|||||||
# Le Tribunal — Docker deployment with managed Supabase
|
# Le Tribunal — Docker deployment
|
||||||
|
|
||||||
# --- Domain ---
|
# --- Domain ---
|
||||||
APP_DOMAIN=acropole.alxczl.fr
|
APP_DOMAIN=acropole.alxczl.fr
|
||||||
@@ -6,7 +6,6 @@ APP_DOMAIN=acropole.alxczl.fr
|
|||||||
# --- Traefik ---
|
# --- Traefik ---
|
||||||
TRAEFIK_NETWORK=proxy
|
TRAEFIK_NETWORK=proxy
|
||||||
|
|
||||||
# --- Supabase (managed) ---
|
# --- Webhook (auto-deploy on push) ---
|
||||||
# From your Supabase project: Settings -> API
|
# openssl rand -hex 16
|
||||||
SUPABASE_URL=
|
WEBHOOK_SECRET=
|
||||||
SUPABASE_ANON_KEY=
|
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
name: Build and deploy
|
||||||
|
run-name: ${{ gitea.actor }} is deploying Le Tribunal
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
|
||||||
|
- run: npm ci
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
npm run build
|
||||||
|
cp -r .next/static .next/standalone/.next/static
|
||||||
|
cp -r public .next/standalone/public
|
||||||
|
env:
|
||||||
|
NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
|
||||||
|
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }}
|
||||||
|
|
||||||
|
- name: Deploy
|
||||||
|
run: |
|
||||||
|
tar -czf - -C .next/standalone . | \
|
||||||
|
curl -sf \
|
||||||
|
-H "X-Webhook-Secret: ${{ secrets.WEBHOOK_SECRET }}" \
|
||||||
|
-H "Content-Type: application/octet-stream" \
|
||||||
|
--data-binary @- \
|
||||||
|
"${{ secrets.DEPLOY_WEBHOOK }}"
|
||||||
+31
-5
@@ -1,11 +1,16 @@
|
|||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
build:
|
image: node:22-alpine
|
||||||
context: .
|
|
||||||
args:
|
|
||||||
NEXT_PUBLIC_SUPABASE_URL: ${SUPABASE_URL}
|
|
||||||
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${SUPABASE_ANON_KEY}
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
working_dir: /app
|
||||||
|
command: ["/entrypoint.sh"]
|
||||||
|
environment:
|
||||||
|
NODE_ENV: production
|
||||||
|
PORT: 3000
|
||||||
|
HOSTNAME: 0.0.0.0
|
||||||
|
volumes:
|
||||||
|
- app-data:/app
|
||||||
|
- ./docker/entrypoint.sh:/entrypoint.sh:ro
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.http.routers.tribunal.rule=Host(`${APP_DOMAIN}`)"
|
- "traefik.http.routers.tribunal.rule=Host(`${APP_DOMAIN}`)"
|
||||||
@@ -16,6 +21,27 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- proxy
|
- proxy
|
||||||
|
|
||||||
|
webhook:
|
||||||
|
build: ./docker/webhook
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
WEBHOOK_SECRET: ${WEBHOOK_SECRET}
|
||||||
|
volumes:
|
||||||
|
- app-data:/data
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.tribunal-webhook.rule=Host(`containers01.lan.alxczl.fr`) && PathPrefix(`/tribunal-webhook`)"
|
||||||
|
- "traefik.http.routers.tribunal-webhook.tls.certresolver=voidca"
|
||||||
|
- "traefik.http.routers.tribunal-webhook.entrypoints=https"
|
||||||
|
- "traefik.http.routers.tribunal-webhook.middlewares=hsts-headers@file,tribunal-webhook-strip"
|
||||||
|
- "traefik.http.middlewares.tribunal-webhook-strip.stripprefix.prefixes=/tribunal-webhook"
|
||||||
|
- "traefik.http.services.tribunal-webhook.loadbalancer.server.port=9000"
|
||||||
|
networks:
|
||||||
|
- proxy
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
app-data:
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
proxy:
|
proxy:
|
||||||
external: true
|
external: true
|
||||||
|
|||||||
Executable
+13
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
while [ ! -f /app/server.js ]; do sleep 2; done
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
STAMP=$(cat /app/.deploy-timestamp 2>/dev/null)
|
||||||
|
node /app/server.js &
|
||||||
|
PID=$!
|
||||||
|
while [ "$(cat /app/.deploy-timestamp 2>/dev/null)" = "$STAMP" ]; do
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
kill $PID
|
||||||
|
wait $PID 2>/dev/null
|
||||||
|
done
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
FROM python:3-alpine
|
||||||
|
COPY receive.py /receive.py
|
||||||
|
CMD ["python", "/receive.py"]
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||||
|
import os, tarfile, io, shutil, time
|
||||||
|
|
||||||
|
SECRET = os.environ["WEBHOOK_SECRET"]
|
||||||
|
|
||||||
|
|
||||||
|
class Handler(BaseHTTPRequestHandler):
|
||||||
|
def do_POST(self):
|
||||||
|
if self.headers.get("X-Webhook-Secret") != SECRET:
|
||||||
|
self.send_response(401)
|
||||||
|
self.end_headers()
|
||||||
|
return
|
||||||
|
|
||||||
|
body = self.rfile.read(int(self.headers["Content-Length"]))
|
||||||
|
|
||||||
|
for item in os.listdir("/data"):
|
||||||
|
path = os.path.join("/data", item)
|
||||||
|
if os.path.isdir(path):
|
||||||
|
shutil.rmtree(path)
|
||||||
|
else:
|
||||||
|
os.remove(path)
|
||||||
|
|
||||||
|
with tarfile.open(fileobj=io.BytesIO(body), mode="r:gz") as tar:
|
||||||
|
tar.extractall("/data", filter="data")
|
||||||
|
|
||||||
|
with open("/data/.deploy-timestamp", "w") as f:
|
||||||
|
f.write(str(time.time()))
|
||||||
|
|
||||||
|
self.send_response(200)
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(b"OK")
|
||||||
|
|
||||||
|
|
||||||
|
HTTPServer(("0.0.0.0", 9000), Handler).serve_forever()
|
||||||
Reference in New Issue
Block a user