Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8922111471 | ||
|
|
74e7c8e8d1 | ||
|
|
2f74b2b229 | ||
|
|
cb9598a786 |
@@ -147,10 +147,10 @@ jobs:
|
||||
# SENTRY_ORG: libnovel
|
||||
# SENTRY_PROJECT: ui
|
||||
|
||||
# ── docker: all images in one job (single login) ──────────────────────────────
|
||||
# backend, runner, ui, and caddy are built sequentially in one job so Docker
|
||||
# Hub only needs to be authenticated once. This also eliminates 3 redundant
|
||||
# checkout + setup-buildx + scheduler round-trips compared to separate jobs.
|
||||
# ── docker: build + push all images via docker bake ──────────────────────────
|
||||
# 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
|
||||
@@ -160,65 +160,12 @@ jobs:
|
||||
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
|
||||
# Single login — credential is written to ~/.docker/config.json and
|
||||
# reused by all subsequent build-push-action steps in this job.
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_TOKEN }}
|
||||
|
||||
# ── backend ──────────────────────────────────────────────────────────────
|
||||
- name: Docker meta / backend
|
||||
id: meta-backend
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ secrets.DOCKER_USER }}/libnovel-backend
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=raw,value=latest
|
||||
|
||||
- name: Build and push / backend
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: backend
|
||||
target: backend
|
||||
push: true
|
||||
tags: ${{ steps.meta-backend.outputs.tags }}
|
||||
labels: ${{ steps.meta-backend.outputs.labels }}
|
||||
build-args: |
|
||||
VERSION=${{ steps.meta-backend.outputs.version }}
|
||||
COMMIT=${{ gitea.sha }}
|
||||
cache-from: type=registry,ref=${{ secrets.DOCKER_USER }}/libnovel-backend:latest
|
||||
cache-to: type=inline
|
||||
|
||||
# ── runner ───────────────────────────────────────────────────────────────
|
||||
- name: Docker meta / runner
|
||||
id: meta-runner
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ secrets.DOCKER_USER }}/libnovel-runner
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=raw,value=latest
|
||||
|
||||
- name: Build and push / runner
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: backend
|
||||
target: runner
|
||||
push: true
|
||||
tags: ${{ steps.meta-runner.outputs.tags }}
|
||||
labels: ${{ steps.meta-runner.outputs.labels }}
|
||||
build-args: |
|
||||
VERSION=${{ steps.meta-runner.outputs.version }}
|
||||
COMMIT=${{ gitea.sha }}
|
||||
cache-from: type=registry,ref=${{ secrets.DOCKER_USER }}/libnovel-runner:latest
|
||||
cache-to: type=inline
|
||||
|
||||
# ── ui ───────────────────────────────────────────────────────────────────
|
||||
- name: Download ui build artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
@@ -230,73 +177,16 @@ jobs:
|
||||
grep -v '^build$' ui/.dockerignore > ui/.dockerignore.tmp
|
||||
mv ui/.dockerignore.tmp ui/.dockerignore
|
||||
|
||||
- name: Docker meta / ui
|
||||
id: meta-ui
|
||||
uses: docker/metadata-action@v5
|
||||
- name: Build and push all images
|
||||
uses: docker/bake-action@v6
|
||||
with:
|
||||
images: ${{ secrets.DOCKER_USER }}/libnovel-ui
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=raw,value=latest
|
||||
|
||||
- name: Build and push / ui
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: ui
|
||||
push: true
|
||||
tags: ${{ steps.meta-ui.outputs.tags }}
|
||||
labels: ${{ steps.meta-ui.outputs.labels }}
|
||||
build-args: |
|
||||
BUILD_VERSION=${{ steps.meta-ui.outputs.version }}
|
||||
BUILD_COMMIT=${{ gitea.sha }}
|
||||
BUILD_TIME=${{ gitea.event.head_commit.timestamp }}
|
||||
PREBUILT=1
|
||||
cache-from: type=registry,ref=${{ secrets.DOCKER_USER }}/libnovel-ui:latest
|
||||
cache-to: type=inline
|
||||
|
||||
# ── caddy ────────────────────────────────────────────────────────────────
|
||||
- name: Docker meta / caddy
|
||||
id: meta-caddy
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ secrets.DOCKER_USER }}/libnovel-caddy
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=raw,value=latest
|
||||
|
||||
- name: Build and push / caddy
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: caddy
|
||||
push: true
|
||||
tags: ${{ steps.meta-caddy.outputs.tags }}
|
||||
labels: ${{ steps.meta-caddy.outputs.labels }}
|
||||
cache-from: type=registry,ref=${{ secrets.DOCKER_USER }}/libnovel-caddy:latest
|
||||
cache-to: type=inline
|
||||
|
||||
# ── pocketbase ───────────────────────────────────────────────────────────
|
||||
- name: Docker meta / pocketbase
|
||||
id: meta-pocketbase
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ secrets.DOCKER_USER }}/libnovel-pocketbase
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=raw,value=latest
|
||||
|
||||
- name: Build and push / pocketbase
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: backend
|
||||
target: pocketbase
|
||||
push: true
|
||||
tags: ${{ steps.meta-pocketbase.outputs.tags }}
|
||||
labels: ${{ steps.meta-pocketbase.outputs.labels }}
|
||||
cache-from: type=registry,ref=${{ secrets.DOCKER_USER }}/libnovel-pocketbase:latest
|
||||
cache-to: type=inline
|
||||
files: docker-bake.hcl
|
||||
set: |
|
||||
*.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.
|
||||
@@ -336,7 +226,7 @@ jobs:
|
||||
"${{ secrets.PROD_USER }}@${{ secrets.PROD_HOST }}" \
|
||||
'set -euo pipefail
|
||||
cd /opt/libnovel
|
||||
doppler run -- docker compose pull
|
||||
doppler run -- docker compose pull backend runner ui caddy pocketbase
|
||||
doppler run -- docker compose up -d --remove-orphans'
|
||||
|
||||
# ── Gitea release ─────────────────────────────────────────────────────────────
|
||||
|
||||
120
docker-bake.hcl
Normal file
120
docker-bake.hcl
Normal file
@@ -0,0 +1,120 @@
|
||||
# docker-bake.hcl — defines all five production images.
|
||||
#
|
||||
# Used by CI (docker/bake-action) to build and push all images in one call,
|
||||
# with shared BuildKit cache and maximum parallelism:
|
||||
# - backend, runner, pocketbase share the Go builder stage (built once)
|
||||
# - caddy and ui build independently in parallel alongside the Go targets
|
||||
#
|
||||
# Local build (push=false by default):
|
||||
# docker buildx bake
|
||||
#
|
||||
# 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 ──────────────────────────────────────────────────
|
||||
|
||||
variable "DOCKER_USER" { default = "kalekber" }
|
||||
|
||||
# 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 = "" }
|
||||
|
||||
# ── Tag helpers ───────────────────────────────────────────────────────────────
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
# 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"]
|
||||
context = "backend"
|
||||
target = "backend"
|
||||
tags = img_tags("${DOCKER_USER}/libnovel-backend")
|
||||
cache-from = ["type=registry,ref=${DOCKER_USER}/libnovel-backend:latest"]
|
||||
args = {
|
||||
VERSION = local.version
|
||||
COMMIT = COMMIT
|
||||
}
|
||||
}
|
||||
|
||||
target "runner" {
|
||||
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 = local.version
|
||||
COMMIT = COMMIT
|
||||
}
|
||||
}
|
||||
|
||||
target "pocketbase" {
|
||||
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"]
|
||||
context = "ui"
|
||||
tags = img_tags("${DOCKER_USER}/libnovel-ui")
|
||||
cache-from = ["type=registry,ref=${DOCKER_USER}/libnovel-ui:latest"]
|
||||
args = {
|
||||
BUILD_VERSION = local.version
|
||||
BUILD_COMMIT = COMMIT
|
||||
BUILD_TIME = BUILD_TIME
|
||||
PREBUILT = "1"
|
||||
}
|
||||
}
|
||||
|
||||
# ── Caddy (custom plugins — separate context) ─────────────────────────────────
|
||||
|
||||
target "caddy" {
|
||||
inherits = ["_defaults"]
|
||||
context = "caddy"
|
||||
tags = img_tags("${DOCKER_USER}/libnovel-caddy")
|
||||
cache-from = ["type=registry,ref=${DOCKER_USER}/libnovel-caddy:latest"]
|
||||
}
|
||||
|
||||
# ── Default group: all five images ────────────────────────────────────────────
|
||||
|
||||
group "default" {
|
||||
targets = ["backend", "runner", "pocketbase", "ui", "caddy"]
|
||||
}
|
||||
@@ -96,7 +96,7 @@ services:
|
||||
|
||||
# ─── Meilisearch (full-text search) ──────────────────────────────────────────
|
||||
meilisearch:
|
||||
image: getmeili/meilisearch:latest
|
||||
image: getmeili/meilisearch:v1.40.0
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MEILI_MASTER_KEY: "${MEILI_MASTER_KEY}"
|
||||
|
||||
2
justfile
2
justfile
@@ -63,7 +63,7 @@ build-push: build push
|
||||
|
||||
# Pull all images from Docker Hub (uses GIT_TAG from Doppler)
|
||||
pull-images:
|
||||
{{doppler}} docker compose pull backend runner ui caddy
|
||||
{{doppler}} docker compose pull backend runner ui caddy pocketbase
|
||||
|
||||
# Pull all third-party base images (minio, pocketbase, etc.)
|
||||
pull-infra:
|
||||
|
||||
Reference in New Issue
Block a user