From 8a457b766858c7b90d4c1a4c60ad69e75a05103b Mon Sep 17 00:00:00 2001 From: Dusan Vojacek Date: Sat, 4 Apr 2026 18:58:59 +0200 Subject: [PATCH] pred prvnim --- .gitea/workflows/build.yml | 13 ---- .gitea/workflows/deploy.yml | 32 ++++++++++ .gitea/workflows/test.yml | 17 ++++-- deploy/deploy.sh | 69 +++++++++++++++++++++ deploy/docker-compose.yml | 117 ++++++++++++++++++++++++++++++++++++ 5 files changed, 231 insertions(+), 17 deletions(-) delete mode 100644 .gitea/workflows/build.yml create mode 100644 .gitea/workflows/deploy.yml create mode 100755 deploy/deploy.sh create mode 100644 deploy/docker-compose.yml diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml deleted file mode 100644 index 474840f..0000000 --- a/.gitea/workflows/build.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: build - -on: - push: - branches: - - main - -jobs: - docker-build-check: - runs-on: ubuntu-latest - steps: - - run: echo "docker build check" - - run: docker version \ No newline at end of file diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..d9bd98c --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,32 @@ +# Deploy na single server: build a compose běží na hostu přes /opt/ems-deploy/deploy.sh (bez DinD). +# +# Vyžaduje act_runner na stejném stroji jako Docker s host executorem, nebo upravit job na SSH. +# Sladit `runs-on` s labely registrace runneru (např. self-hosted + ems-deploy). + +name: deploy + +on: + push: + branches: + - main + workflow_dispatch: + +jobs: + deploy: + runs-on: self-hosted + steps: + - name: Run deploy script on host + run: /opt/ems-deploy/deploy.sh + + # Alternativa: runner v Dockeru bez přístupu k hostu — odkomentovat a upravit SERVER + secrets. + # deploy-ssh: + # runs-on: ubuntu-latest + # steps: + # - name: Deploy over SSH + # env: + # SSH_PRIVATE_KEY: ${{ secrets.DEPLOY_SSH_KEY }} + # run: | + # mkdir -p ~/.ssh + # printf '%s\n' "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519 + # chmod 600 ~/.ssh/id_ed25519 + # ssh -o StrictHostKeyChecking=yes -i ~/.ssh/id_ed25519 deploy@SERVER '/opt/ems-deploy/deploy.sh' diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index 8d27a83..718436b 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -11,7 +11,16 @@ jobs: smoke-test: runs-on: ubuntu-latest steps: - - run: echo "runner funguje" - - run: uname -a - - run: pwd - - run: ls -la \ No newline at end of file + - uses: actions/checkout@v4 + + - name: Repo layout + run: | + test -f docker-compose.yml + test -f deploy/docker-compose.yml + test -x deploy/deploy.sh + + - name: Runner info + run: | + uname -a + pwd + ls -la diff --git a/deploy/deploy.sh b/deploy/deploy.sh new file mode 100755 index 0000000..6f87728 --- /dev/null +++ b/deploy/deploy.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +# EMS single-server deploy: aktualizace git checkoutu, sync compose, docker compose build + up. +# +# Jednorázový bootstrap na serveru (po prvním merge tohoto skriptu do main): +# sudo mkdir -p /opt/ems-deploy/app +# sudo chown -R "$USER:$USER" /opt/ems-deploy +# git clone ssh://git@git.vojacek.eu:2222/vojacekd/ems.git /opt/ems-deploy/app +# cp /opt/ems-deploy/app/.env.example /opt/ems-deploy/.env +# # upravit /opt/ems-deploy/.env (chmod 600) +# install -m 755 /opt/ems-deploy/app/deploy/deploy.sh /opt/ems-deploy/deploy.sh +# /opt/ems-deploy/deploy.sh + +set -euo pipefail + +ROOT="${EMS_DEPLOY_ROOT:-/opt/ems-deploy}" +APP="${ROOT}/app" +COMPOSE_SRC="${APP}/deploy/docker-compose.yml" +COMPOSE_DST="${ROOT}/docker-compose.yml" +ENV_FILE="${ROOT}/.env" +LOCK_FILE="${ROOT}/.deploy.lock" + +log() { + echo "[$(date -Iseconds)] $*" +} + +if [[ ! -d "$APP/.git" ]]; then + log "ERROR: missing git checkout at $APP (see bootstrap header in this script)." + exit 1 +fi + +if [[ ! -f "$ENV_FILE" ]]; then + log "ERROR: missing $ENV_FILE" + exit 1 +fi + +if [[ ! -f "$COMPOSE_SRC" ]]; then + log "ERROR: missing $COMPOSE_SRC (is deploy/docker-compose.yml in the repo?)" + exit 1 +fi + +exec 9>"$LOCK_FILE" +flock 9 +log "Starting deploy (ROOT=$ROOT)" + +log "Git: fetch origin" +git -C "$APP" fetch origin + +log "Git: checkout main && reset --hard origin/main" +git -C "$APP" checkout main +git -C "$APP" reset --hard "origin/main" + +log "Sync compose -> $COMPOSE_DST" +install -m 0644 "$COMPOSE_SRC" "$COMPOSE_DST" + +export COMPOSE_FILE="$COMPOSE_DST" + +log "docker compose config (validate)" +docker compose -f "$COMPOSE_DST" --env-file "$ENV_FILE" config >/dev/null + +log "docker compose build" +docker compose -f "$COMPOSE_DST" --env-file "$ENV_FILE" build + +log "docker compose up -d" +docker compose -f "$COMPOSE_DST" --env-file "$ENV_FILE" up -d + +log "docker image prune (dangling only)" +docker image prune -f + +log "Deploy finished OK" diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml new file mode 100644 index 0000000..c7a6886 --- /dev/null +++ b/deploy/docker-compose.yml @@ -0,0 +1,117 @@ +# Produkční stack pro single-server deploy. +# Na serveru: kopíruje se do /opt/ems-deploy/docker-compose.yml (viz deploy/deploy.sh). +# Cesty ./app/* jsou relativní k adresáři, kde leží tento soubor po zkopírování (/opt/ems-deploy). + +services: + + db: + image: timescale/timescaledb:latest-pg16 + restart: unless-stopped + environment: + POSTGRES_DB: ems + POSTGRES_USER: ${DB_USER} + POSTGRES_PASSWORD: ${DB_PASSWORD} + volumes: + - db_data:/var/lib/postgresql/data + ports: + - "127.0.0.1:5432:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ems"] + interval: 10s + timeout: 5s + retries: 5 + + flyway: + image: flyway/flyway:10 + depends_on: + db: + condition: service_healthy + environment: + FLYWAY_URL: jdbc:postgresql://db:5432/ems + FLYWAY_USER: ${DB_USER} + FLYWAY_PASSWORD: ${DB_PASSWORD} + FLYWAY_SCHEMAS: ems + FLYWAY_LOCATIONS: filesystem:/flyway/sql/migration,filesystem:/flyway/sql/routines,filesystem:/flyway/sql/views + FLYWAY_BASELINE_ON_MIGRATE: "false" + command: migrate + volumes: + - ./app/db/migration:/flyway/sql/migration + - ./app/db/routines:/flyway/sql/routines + - ./app/db/views:/flyway/sql/views + + postgrest: + image: postgrest/postgrest:v12.2.3 + restart: unless-stopped + depends_on: + db: + condition: service_healthy + flyway: + condition: service_completed_successfully + environment: + PGRST_DB_URI: postgres://${DB_USER}:${DB_PASSWORD}@db:5432/ems + PGRST_DB_SCHEMA: ems + PGRST_DB_EXTRA_SEARCH_PATH: ems + PGRST_DB_ANON_ROLE: ${POSTGREST_ANON_ROLE:-ems_anon} + PGRST_JWT_SECRET: ${POSTGREST_JWT_SECRET} + PGRST_SERVER_PORT: 3000 + # Za Caddy změň na veřejnou bázi URL API (např. https://ems.example.com/rest). + PGRST_OPENAPI_SERVER_PROXY_URI: http://localhost/rest + ports: + - "127.0.0.1:3000:3000" + + backend: + build: + context: ./app/backend + restart: unless-stopped + depends_on: + db: + condition: service_healthy + flyway: + condition: service_completed_successfully + env_file: + - .env + command: ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] + environment: + TZ: Europe/Prague + DB_HOST: db + DB_PORT: "5432" + POSTGRES_HOST: db + POSTGRES_PORT: "5432" + DB_NAME: ems + DB_USER: ${DB_USER} + DB_PASSWORD: ${DB_PASSWORD} + OTE_API_URL: ${OTE_API_URL:-https://www.ote-cr.cz/cs/kratkodobe-trhy/elektrina/denni-trh/@@chart-data} + EUR_CZK_RATE: ${EUR_CZK_RATE:-25.0} + OPEN_METEO_API_URL: ${OPEN_METEO_API_URL:-https://api.open-meteo.com/v1/forecast} + TELEMETRY_POLL_INTERVAL_SEC: ${TELEMETRY_POLL_INTERVAL_SEC:-60} + PLANNING_HORIZON_HOURS: ${PLANNING_HORIZON_HOURS:-36} + PLANNING_HP_MAX_COST_CZK_KWH: ${PLANNING_HP_MAX_COST_CZK_KWH:-3.0} + LOXONE_USER: ${LOXONE_USER:-} + LOXONE_PASSWORD: ${LOXONE_PASSWORD:-} + POSTGREST_JWT_SECRET: ${POSTGREST_JWT_SECRET} + POSTGREST_ANON_ROLE: ${POSTGREST_ANON_ROLE:-ems_anon} + ports: + - "127.0.0.1:8000:8000" + + frontend: + build: + context: ./app/frontend + restart: unless-stopped + ports: + - "127.0.0.1:8080:80" + depends_on: + - backend + - postgrest + +volumes: + db_data: + driver: local + +networks: + default: + name: ems_net + driver: bridge + ipam: + config: + - subnet: 172.28.240.0/24 + gateway: 172.28.240.1