name: Build directus image on: push: branches: [main] paths: - 'snapshots/**' - 'db-init/**' - 'db-init-post/**' - 'extensions/**' - 'scripts/**' - 'entrypoint.sh' - 'Dockerfile' - '.gitea/workflows/build.yml' workflow_dispatch: jobs: build-and-publish: runs-on: ubuntu-22.04 # --------------------------------------------------------------------------- # Throwaway Postgres for the dry-run boot step. # # Image: pinned to the same concrete tag used in compose.dev.yaml — NOT the # floating :pg16-latest alias (which does NOT exist on Docker Hub). # # PGDATA: the timescaledb-ha image initialises at /home/postgres/pgdata/data; # the healthcheck uses pg_isready, which doesn't depend on the PGDATA path. # # Port mapping: 15432:5432 — host port 15432 is the conventional # Postgres-second-instance port. We deliberately do NOT use 5432 on the # runner because the runner host typically has another Postgres on 5432 # (dev stack, stage instance) which would cause a port-allocation collision. # The dry-run docker run uses --network host so DB_HOST=localhost reaches # the service on the runner's loopback at port 15432. # --------------------------------------------------------------------------- services: postgres: image: timescale/timescaledb-ha:pg16.6-ts2.17.2-all env: POSTGRES_USER: directus POSTGRES_PASSWORD: directus POSTGRES_DB: directus ports: - '15432:5432' options: >- --health-cmd "pg_isready -U directus -d directus" --health-interval 5s --health-timeout 5s --health-retries 20 steps: - name: Checkout uses: actions/checkout@v4 # ------------------------------------------------------------------------- # Build the image locally (trm-directus:ci). # # We use a plain `docker build` rather than docker/build-push-action because # we need the image available in the *local Docker daemon* for the subsequent # `docker run` dry-run step. docker/build-push-action with the # docker-container Buildx driver exports into a separate buildkitd cache not # accessible to `docker run`. # ------------------------------------------------------------------------- - name: Build image run: docker build -t trm-directus:ci . # ------------------------------------------------------------------------- # Dry-run boot — the gate that protects the registry from broken images. # # Runs the pre-boot script chain (apply-db-init.sh → schema-apply.sh → # apply-db-init.sh against db-init-post) against the throwaway Postgres # service above. Mirrors the entrypoint's first three steps. # Intentionally does NOT run `directus bootstrap` or `directus start` — # that would require waiting for the HTTP server to come up, which adds # minutes and tests nothing new. # # --network host: the service container is mapped on 127.0.0.1:5432; the # docker run container sees it as localhost:5432 only when host networking # is used. Without --network host, the container would be in a separate # bridge network and could not reach the service by name or IP. # # --entrypoint bash: overrides /directus/entrypoint.sh so we execute only # the script chain, not the full pm2-runtime boot. # # Required Directus env vars: DB_CLIENT + connection params are mandatory # for `node cli.js schema apply`. KEY + SECRET are required by Directus's # env initialisation even when only the schema subcommand is invoked. # ADMIN_EMAIL + ADMIN_PASSWORD are included defensively (some Directus # versions assert on them during CLI init). PUBLIC_URL silences the # missing-public-url warning. # # If this step exits non-zero the workflow halts and the registry login / # push steps are never reached — the broken image is never published. # ------------------------------------------------------------------------- - name: Dry-run boot against throwaway Postgres run: | docker run --rm \ --network host \ --entrypoint bash \ -e DB_CLIENT=pg \ -e DB_HOST=localhost \ -e DB_PORT=15432 \ -e DB_USER=directus \ -e DB_PASSWORD=directus \ -e DB_DATABASE=directus \ -e KEY=ci-key-placeholder-not-secret \ -e SECRET=ci-secret-placeholder-not-secret \ -e ADMIN_EMAIL=ci@example.com \ -e ADMIN_PASSWORD=ci-password-not-secret \ -e PUBLIC_URL=http://localhost:8055 \ trm-directus:ci \ -c '/directus/scripts/apply-db-init.sh && /directus/scripts/schema-apply.sh && DB_INIT_DIR=/directus/db-init-post /directus/scripts/apply-db-init.sh && echo "dry-run ok"' # ------------------------------------------------------------------------- # Registry login — runs only if the dry-run succeeded (default: workflow # halts on non-zero exit, so reaching this step implies dry-run passed). # ------------------------------------------------------------------------- - name: Login to Gitea registry uses: docker/login-action@v3 with: registry: git.dev.microservices.al username: ${{ secrets.REGISTRY_USERNAME }} password: ${{ secrets.REGISTRY_PASSWORD }} # ------------------------------------------------------------------------- # Tag and push two tags: # :main — mutable; always points at the latest commit on main. # : — immutable; pinned to this specific commit. # The deploy stack can reference either; :main for rolling updates, # : for pinned deployments that need explicit rollback control. # ------------------------------------------------------------------------- - name: Tag and push run: | docker tag trm-directus:ci git.dev.microservices.al/trm/directus:main docker tag trm-directus:ci git.dev.microservices.al/trm/directus:${{ github.sha }} docker push git.dev.microservices.al/trm/directus:main docker push git.dev.microservices.al/trm/directus:${{ github.sha }} # ------------------------------------------------------------------------- # Optional Portainer redeploy webhook. # Fires only when PORTAINER_WEBHOOK_URL secret is configured in the repo. # If the secret is absent the condition evaluates false and the step is # skipped — no error, no noise. # ------------------------------------------------------------------------- - name: Trigger Portainer redeploy (optional) if: ${{ secrets.PORTAINER_WEBHOOK_URL != '' }} run: curl -fsS -X POST "${{ secrets.PORTAINER_WEBHOOK_URL }}"