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.
This commit is contained in:
Flavio Fois
2026-03-23 11:09:25 +01:00
parent 84521d8d59
commit 6c8b400a4a
7 changed files with 154 additions and 2 deletions

31
.dockerignore Normal file
View File

@@ -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/

View File

@@ -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

3
.gitignore vendored
View File

@@ -24,8 +24,9 @@ profile.cov
go.work
go.work.sum
# env file
# env files
.env
.env.local
# Editor/IDE
.idea/

29
Dockerfile Normal file
View File

@@ -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"]

25
docker-compose.yml Normal file
View File

@@ -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:

8
docker/entrypoint.sh Normal file
View File

@@ -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

View File

@@ -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))