feat: publish Swarm images to Gitea registry
Some checks failed
CI / Validate frontend and API (push) Successful in 29s
CI / Build and publish Docker images (push) Failing after 5s

This commit is contained in:
Cauê Faleiros
2026-08-31 13:35:53 -03:00
parent 4932f6bf04
commit 7a18251916
4 changed files with 48 additions and 17 deletions

View File

@@ -14,3 +14,8 @@ JWT_SECRET=replace-this-with-a-long-random-secret-before-deploying
BOOTSTRAP_ADMIN_EMAIL=admin@example.com BOOTSTRAP_ADMIN_EMAIL=admin@example.com
BOOTSTRAP_ADMIN_PASSWORD=change-this-password BOOTSTRAP_ADMIN_PASSWORD=change-this-password
BOOTSTRAP_ADMIN_NAME=Compor HUB Admin BOOTSTRAP_ADMIN_NAME=Compor HUB Admin
# Docker Swarm / Portainer image tags. The defaults are the Gitea Container Registry images.
API_IMAGE=gitea.blyzer.com.br/blyzer/compor-academy-api
WEB_IMAGE=gitea.blyzer.com.br/blyzer/compor-academy-web
IMAGE_TAG=latest

View File

@@ -7,6 +7,7 @@ on:
permissions: permissions:
contents: read contents: read
packages: write
jobs: jobs:
validate: validate:
@@ -20,3 +21,21 @@ jobs:
- run: npm ci --include=dev - run: npm ci --include=dev
- run: npm run typecheck - run: npm run typecheck
- run: npm run build - run: npm run build
publish-images:
name: Build and publish Docker images
needs: validate
if: gitea.event_name == 'push' && gitea.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Sign in to the Gitea Container Registry
run: echo "${{ secrets.GITEA_TOKEN }}" | docker login gitea.blyzer.com.br -u "${{ gitea.actor }}" --password-stdin
- name: Build and publish API
run: |
docker build --pull -f Dockerfile.api -t gitea.blyzer.com.br/blyzer/compor-academy-api:latest .
docker push gitea.blyzer.com.br/blyzer/compor-academy-api:latest
- name: Build and publish web
run: |
docker build --pull -f Dockerfile.web -t gitea.blyzer.com.br/blyzer/compor-academy-web:latest .
docker push gitea.blyzer.com.br/blyzer/compor-academy-web:latest

View File

@@ -2,6 +2,8 @@
Use `docker-compose.yml` as a Portainer Stack from this repository. It deploys the frontend, API, and PostgreSQL as one internal Docker network. Only the web container exposes a port; it proxies `/api` to the API container. Use `docker-compose.yml` as a Portainer Stack from this repository. It deploys the frontend, API, and PostgreSQL as one internal Docker network. Only the web container exposes a port; it proxies `/api` to the API container.
This is a Docker Swarm stack: Portainer pulls prebuilt API and web images from the Gitea Container Registry. It never builds Dockerfiles itself.
## Required Portainer environment variables ## Required Portainer environment variables
- `POSTGRES_PASSWORD`: a long, unique database password. Avoid characters that are not URL-safe because it is used in `DATABASE_URL`. - `POSTGRES_PASSWORD`: a long, unique database password. Avoid characters that are not URL-safe because it is used in `DATABASE_URL`.
@@ -13,12 +15,16 @@ Optional variables:
- `POSTGRES_DB` (default `compor_hub`) - `POSTGRES_DB` (default `compor_hub`)
- `POSTGRES_USER` (default `compor`) - `POSTGRES_USER` (default `compor`)
- `WEB_PORT` (default `8080`) - `WEB_PORT` (default `8080`)
- `IMAGE_TAG` (default `latest`; set a specific release tag when available)
- `API_IMAGE` and `WEB_IMAGE` only if the Gitea registry namespace differs from the defaults.
## Before publishing ## Before publishing
1. Deploy the stack with a temporary `WEB_PORT` and verify `/api/v1/health` through the public domain. 1. Push to `main` and wait for Gitea Actions to publish `gitea.blyzer.com.br/blyzer/compor-academy-api:latest` and `gitea.blyzer.com.br/blyzer/compor-academy-web:latest`.
2. Create the production administrator using the API container's console and `npm run db:bootstrap-admin`, with the `BOOTSTRAP_ADMIN_*` variables supplied only for that one command. 2. Ensure the Portainer endpoint can pull from the Gitea Container Registry. If the images are private, add Gitea registry credentials to the endpoint/stack deployment configuration.
3. Place the web service behind HTTPS, normally through your existing reverse proxy (Traefik, Nginx Proxy Manager, or Cloudflare Tunnel), and set `FRONTEND_ORIGIN` to that HTTPS address. 3. Deploy the stack with a temporary `WEB_PORT` and verify `/api/v1/health` through the public domain.
4. Back up the `compor_postgres_data` volume before updates. 4. Create the production administrator using the API container's console and `npm run db:bootstrap-admin`, with the `BOOTSTRAP_ADMIN_*` variables supplied only for that one command.
5. Place the web service behind HTTPS, normally through your existing reverse proxy (Traefik, Nginx Proxy Manager, or Cloudflare Tunnel), and set `FRONTEND_ORIGIN` to that HTTPS address.
6. Back up the `compor_postgres_data` volume before updates.
Do not expose port 5432 or port 3001 publicly. Do not expose port 5432 or port 3001 publicly.

View File

@@ -1,7 +1,6 @@
services: services:
postgres: postgres:
image: postgres:16-alpine image: postgres:16-alpine
restart: unless-stopped
environment: environment:
POSTGRES_DB: ${POSTGRES_DB:-compor_hub} POSTGRES_DB: ${POSTGRES_DB:-compor_hub}
POSTGRES_USER: ${POSTGRES_USER:-compor} POSTGRES_USER: ${POSTGRES_USER:-compor}
@@ -13,30 +12,32 @@ services:
interval: 10s interval: 10s
timeout: 5s timeout: 5s
retries: 10 retries: 10
deploy:
replicas: 1
restart_policy:
condition: any
api: api:
build: image: ${API_IMAGE:-gitea.blyzer.com.br/blyzer/compor-academy-api}:${IMAGE_TAG:-latest}
context: .
dockerfile: Dockerfile.api
restart: unless-stopped
environment: environment:
APP_ENV: production APP_ENV: production
API_PORT: 3001 API_PORT: 3001
FRONTEND_ORIGIN: ${FRONTEND_ORIGIN:?Set the public https URL in Portainer} FRONTEND_ORIGIN: ${FRONTEND_ORIGIN:?Set the public https URL in Portainer}
DATABASE_URL: postgresql://${POSTGRES_USER:-compor}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-compor_hub} DATABASE_URL: postgresql://${POSTGRES_USER:-compor}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-compor_hub}
JWT_SECRET: ${JWT_SECRET:?Set a long random JWT_SECRET in Portainer} JWT_SECRET: ${JWT_SECRET:?Set a long random JWT_SECRET in Portainer}
depends_on: deploy:
- postgres replicas: 1
restart_policy:
condition: any
web: web:
build: image: ${WEB_IMAGE:-gitea.blyzer.com.br/blyzer/compor-academy-web}:${IMAGE_TAG:-latest}
context: .
dockerfile: Dockerfile.web
restart: unless-stopped
ports: ports:
- "${WEB_PORT:-8080}:80" - "${WEB_PORT:-8080}:80"
depends_on: deploy:
- api replicas: 1
restart_policy:
condition: any
volumes: volumes:
compor_postgres_data: compor_postgres_data: