From 74e7c8e8d1ffa2d78d70d4e9609eae73ad1e1b13 Mon Sep 17 00:00:00 2001 From: Admin Date: Wed, 15 Apr 2026 19:22:06 +0500 Subject: [PATCH] chore: move tag logic into bake file, drop all metadata-action steps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docker-bake.hcl now owns semver tag generation via HCL locals + a reusable img_tags() function: GIT_TAG="v4.1.5" → :4.1.5, :4.1, :latest on all images. The Docker job shrinks from 13 steps to 6 — no docker/metadata-action needed. CI simply passes GIT_TAG, COMMIT, BUILD_TIME as env vars to bake-action. Co-Authored-By: Claude Sonnet 4.6 --- .gitea/workflows/release.yaml | 85 ++++----------------------------- docker-bake.hcl | 88 ++++++++++++++++++++++------------- 2 files changed, 64 insertions(+), 109 deletions(-) diff --git a/.gitea/workflows/release.yaml b/.gitea/workflows/release.yaml index 9f460d4..ba1fbb0 100644 --- a/.gitea/workflows/release.yaml +++ b/.gitea/workflows/release.yaml @@ -148,13 +148,9 @@ jobs: # SENTRY_PROJECT: ui # ── docker: build + push all images via docker bake ────────────────────────── - # docker-bake.hcl defines all five targets. BuildKit builds them with maximum - # parallelism: backend/runner/pocketbase share the Go builder stage (compiled - # once), while caddy and ui build concurrently alongside the Go targets. - # - # Metadata (tags + labels) is generated per-image by docker/metadata-action - # (bake-target mode), then merged into a single bake call via the bake-action - # targets input — one Docker Hub login, one buildx invocation. + # docker-bake.hcl owns all tag logic (semver, major.minor, latest) via HCL + # functions — no docker/metadata-action needed. BuildKit builds all five + # targets in parallel, sharing the Go builder stage across backend/runner/pocketbase. docker: name: Docker runs-on: ubuntu-latest @@ -170,7 +166,6 @@ jobs: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_TOKEN }} - # ── Prepare ui build artifact ───────────────────────────────────────────── - name: Download ui build artifacts uses: actions/download-artifact@v3 with: @@ -182,80 +177,16 @@ jobs: grep -v '^build$' ui/.dockerignore > ui/.dockerignore.tmp mv ui/.dockerignore.tmp ui/.dockerignore - # ── Generate per-image tags + labels (bake-target mode) ─────────────────── - - name: Docker meta / backend - id: meta-backend - uses: docker/metadata-action@v5 - with: - images: ${{ secrets.DOCKER_USER }}/libnovel-backend - bake-target: backend - tags: | - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=raw,value=latest - - - name: Docker meta / runner - id: meta-runner - uses: docker/metadata-action@v5 - with: - images: ${{ secrets.DOCKER_USER }}/libnovel-runner - bake-target: runner - tags: | - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=raw,value=latest - - - name: Docker meta / pocketbase - id: meta-pocketbase - uses: docker/metadata-action@v5 - with: - images: ${{ secrets.DOCKER_USER }}/libnovel-pocketbase - bake-target: pocketbase - tags: | - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=raw,value=latest - - - name: Docker meta / ui - id: meta-ui - uses: docker/metadata-action@v5 - with: - images: ${{ secrets.DOCKER_USER }}/libnovel-ui - bake-target: ui - tags: | - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=raw,value=latest - - - name: Docker meta / caddy - id: meta-caddy - uses: docker/metadata-action@v5 - with: - images: ${{ secrets.DOCKER_USER }}/libnovel-caddy - bake-target: caddy - tags: | - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=raw,value=latest - - # ── Build + push all images in parallel ─────────────────────────────────── - name: Build and push all images uses: docker/bake-action@v6 with: - files: | - docker-bake.hcl - ${{ steps.meta-backend.outputs.bake-file }} - ${{ steps.meta-runner.outputs.bake-file }} - ${{ steps.meta-pocketbase.outputs.bake-file }} - ${{ steps.meta-ui.outputs.bake-file }} - ${{ steps.meta-caddy.outputs.bake-file }} + files: docker-bake.hcl set: | - backend.args.VERSION=${{ steps.meta-backend.outputs.version }} - backend.args.COMMIT=${{ gitea.sha }} - runner.args.VERSION=${{ steps.meta-runner.outputs.version }} - runner.args.COMMIT=${{ gitea.sha }} - ui.args.BUILD_TIME=${{ gitea.event.head_commit.timestamp }} *.output=type=image,push=true + env: + GIT_TAG: ${{ gitea.ref_name }} + COMMIT: ${{ gitea.sha }} + BUILD_TIME: ${{ gitea.event.head_commit.timestamp }} # ── deploy: sync docker-compose.yml + restart prod ─────────────────────────── # Runs after all images are pushed to Docker Hub. diff --git a/docker-bake.hcl b/docker-bake.hcl index ae1858b..e1ae879 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -5,75 +5,99 @@ # - backend, runner, pocketbase share the Go builder stage (built once) # - caddy and ui build independently in parallel alongside the Go targets # -# Local build (no push): +# Local build (push=false by default): # docker buildx bake # -# CI sets per-target tags and labels via --set overrides in docker/bake-action. +# CI passes GIT_TAG (e.g. "v4.1.3") and the bake file computes all three tag +# variants (full semver, major.minor, latest) — no docker/metadata-action needed. -# ── Variables injected by CI via --set ─────────────────────────────────────── +# ── Variables injected by CI ────────────────────────────────────────────────── variable "DOCKER_USER" { default = "kalekber" } -variable "VERSION" { default = "dev" } +# Full git tag, e.g. "v4.1.3". CI passes this via --set or env. +# Locally defaults to "dev" so images get a :dev tag. +variable "GIT_TAG" { default = "dev" } + variable "COMMIT" { default = "unknown" } variable "BUILD_TIME" { default = "" } -# ── Shared defaults ─────────────────────────────────────────────────────────── +# ── Tag helpers ─────────────────────────────────────────────────────────────── -# All targets inherit these. CI overrides tags/labels per-target via --set. -target "_defaults" { - pull = true - output = ["type=image,push=false"] +locals { + # Strip leading "v": "v4.1.3" → "4.1.3" + version = trimprefix(GIT_TAG, "v") + + # major.minor only: "4.1.3" → "4.1" + major_minor = join(".", slice(split(".", local.version), 0, 2)) + + # true when building a real release tag (not local "dev" build) + is_release = GIT_TAG != "dev" } -# Registry cache shared across all targets in a run. -target "_cache" { +# Return the three standard tags for a given image repo. +# Always includes :latest and the full version; skips major.minor for dev builds. +function "img_tags" { + params = [repo] + result = local.is_release ? [ + "${repo}:${local.version}", + "${repo}:${local.major_minor}", + "${repo}:latest", + ] : ["${repo}:dev"] +} + +# ── Shared defaults ─────────────────────────────────────────────────────────── + +target "_defaults" { + pull = true + # CI overrides this to push=true via --set *.output=type=image,push=true + output = ["type=image,push=false"] cache-to = ["type=inline"] } # ── Go targets (share the backend/ build context + builder stage) ───────────── target "backend" { - inherits = ["_defaults", "_cache"] - context = "backend" - target = "backend" - tags = ["${DOCKER_USER}/libnovel-backend:latest"] + inherits = ["_defaults"] + context = "backend" + target = "backend" + tags = img_tags("${DOCKER_USER}/libnovel-backend") cache-from = ["type=registry,ref=${DOCKER_USER}/libnovel-backend:latest"] args = { - VERSION = VERSION + VERSION = local.version COMMIT = COMMIT } } target "runner" { - inherits = ["_defaults", "_cache"] - context = "backend" - target = "runner" - tags = ["${DOCKER_USER}/libnovel-runner:latest"] + inherits = ["_defaults"] + context = "backend" + target = "runner" + tags = img_tags("${DOCKER_USER}/libnovel-runner") cache-from = ["type=registry,ref=${DOCKER_USER}/libnovel-runner:latest"] args = { - VERSION = VERSION + VERSION = local.version COMMIT = COMMIT } } target "pocketbase" { - inherits = ["_defaults", "_cache"] - context = "backend" - target = "pocketbase" - tags = ["${DOCKER_USER}/libnovel-pocketbase:latest"] + inherits = ["_defaults"] + context = "backend" + target = "pocketbase" + tags = img_tags("${DOCKER_USER}/libnovel-pocketbase") cache-from = ["type=registry,ref=${DOCKER_USER}/libnovel-pocketbase:latest"] } # ── UI (SvelteKit — separate context) ──────────────────────────────────────── target "ui" { - inherits = ["_defaults", "_cache"] - context = "ui" - tags = ["${DOCKER_USER}/libnovel-ui:latest"] + inherits = ["_defaults"] + context = "ui" + tags = img_tags("${DOCKER_USER}/libnovel-ui") cache-from = ["type=registry,ref=${DOCKER_USER}/libnovel-ui:latest"] args = { - BUILD_VERSION = VERSION + BUILD_VERSION = local.version BUILD_COMMIT = COMMIT BUILD_TIME = BUILD_TIME PREBUILT = "1" @@ -83,9 +107,9 @@ target "ui" { # ── Caddy (custom plugins — separate context) ───────────────────────────────── target "caddy" { - inherits = ["_defaults", "_cache"] - context = "caddy" - tags = ["${DOCKER_USER}/libnovel-caddy:latest"] + inherits = ["_defaults"] + context = "caddy" + tags = img_tags("${DOCKER_USER}/libnovel-caddy") cache-from = ["type=registry,ref=${DOCKER_USER}/libnovel-caddy:latest"] }