From 6c8b400a4a091f456c5ec385c54ffc11d4c90fee Mon Sep 17 00:00:00 2001 From: Flavio Fois Date: Mon, 23 Mar 2026 11:09:25 +0100 Subject: [PATCH] add docker configuration and gitea workflow for automated image builds Introduce a multi-stage Dockerfile, docker-compose setup, and a Gitea Actions workflow to automate building and pushing images to the registry. The configuration includes an entrypoint script for persistent logging and a .dockerignore to optimize the build context. --- .dockerignore | 31 +++++++++++++++++++++ .gitea/workflows/docker.yml | 55 +++++++++++++++++++++++++++++++++++++ .gitignore | 3 +- Dockerfile | 29 +++++++++++++++++++ docker-compose.yml | 25 +++++++++++++++++ docker/entrypoint.sh | 8 ++++++ internal/routes/routes.go | 5 +++- 7 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 .dockerignore create mode 100644 .gitea/workflows/docker.yml create mode 100644 Dockerfile create mode 100644 docker-compose.yml create mode 100644 docker/entrypoint.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..5b6dff9 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,31 @@ +# Build artifacts +build/ +tmp/ +*.exe + +# Test artifacts +*.out +*.test +coverage.* +*.coverprofile + +# Dev tooling +.air.toml + +# Secrets & local config +.env + +# Version control +.git/ +.gitignore + +# IDE +.idea/ +.vscode/ + +# Docs +*.md +LICENSE + +# Claude +.claude/ diff --git a/.gitea/workflows/docker.yml b/.gitea/workflows/docker.yml new file mode 100644 index 0000000..d6cc0b4 --- /dev/null +++ b/.gitea/workflows/docker.yml @@ -0,0 +1,55 @@ +name: Build & Publish Docker Image + +on: + push: + branches: + - main + tags: + - "v*" + +jobs: + build-and-push: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + # Strip "https://" from the server URL to get the registry hostname + - name: Resolve registry host + id: reg + run: | + host=$(echo "${{ gitea.server_url }}" | sed 's|https://||;s|http://||;s|/||g') + echo "host=$host" >> $GITHUB_OUTPUT + echo "image=$host/${{ gitea.repository }}" >> $GITHUB_OUTPUT + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Gitea registry + uses: docker/login-action@v3 + with: + registry: ${{ steps.reg.outputs.host }} + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ steps.reg.outputs.image }} + tags: | + type=raw,value=latest,enable=${{ gitea.ref == 'refs/heads/main' }} + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha,prefix=sha-,format=short + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.gitignore b/.gitignore index 8f95592..1f3fb2e 100644 --- a/.gitignore +++ b/.gitignore @@ -24,8 +24,9 @@ profile.cov go.work go.work.sum -# env file +# env files .env +.env.local # Editor/IDE .idea/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..67fdf2c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,29 @@ +# --- Build stage --- +FROM golang:alpine AS builder + +WORKDIR /build + +COPY go.mod go.sum ./ +RUN go mod download + +COPY . . +RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o emly-api . + +# --- Runtime stage --- +FROM alpine:latest + +RUN apk --no-cache add ca-certificates tzdata + +WORKDIR /app + +COPY --from=builder /build/emly-api . +COPY --from=builder /build/internal/database/schema ./internal/database/schema +COPY --from=builder /build/internal/handlers/templates ./internal/handlers/templates +COPY docker/entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +RUN mkdir -p /logs + +EXPOSE 8080 + +ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..76291fa --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,25 @@ +services: + api: + build: . + restart: unless-stopped + ports: + - "${PORT:-8080}:8080" + environment: + PORT: ${PORT:-8080} + DB_DSN: ${DB_DSN} + DATABASE_NAME: ${DATABASE_NAME} + API_KEY: ${API_KEY} + ADMIN_KEY: ${ADMIN_KEY} + DB_MAX_OPEN_CONNS: ${DB_MAX_OPEN_CONNS:-25} + DB_MAX_IDLE_CONNS: ${DB_MAX_IDLE_CONNS:-5} + DB_CONN_MAX_LIFETIME: ${DB_CONN_MAX_LIFETIME:-5} + volumes: + - logs:/logs + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "5" + +volumes: + logs: diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100644 index 0000000..8e412aa --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,8 @@ +#!/bin/sh +set -e + +mkdir -p /logs + +# Run the app and tee output to both stdout (for `docker logs`) and a +# persistent log file inside the mounted volume. +exec ./emly-api 2>&1 | tee -a /logs/app.log \ No newline at end of file diff --git a/internal/routes/routes.go b/internal/routes/routes.go index ea1afbd..07f2b72 100644 --- a/internal/routes/routes.go +++ b/internal/routes/routes.go @@ -15,7 +15,10 @@ import ( // r.Mount("/v2", v2.NewRouter(db)) func RegisterAll(r chi.Router, db *sqlx.DB) { r.Get("/", func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("emly-api-go")) + _, err := w.Write([]byte("emly-api-go")) + if err != nil { + return + } }) r.Mount("/v1", v1.NewRouter(db))