From fa0436441336a18f876ce03393876e12ca06e541 Mon Sep 17 00:00:00 2001 From: Rene Nochebuena Guerrero Date: Tue, 2 Jun 2026 19:07:31 +0000 Subject: [PATCH] chore(spa-server): add Makefile and OCI image labels to Dockerfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Makefile: - dry-run target (default) shows registry, branch, SHA, version, all computed image tags, and build args — no side effects - build/push/release targets wire docker build and docker push - Branch-aware tag strategy: main → + latest develop → -dev- release/* → -qa- - Version resolved from the highest semver tag at HEAD; falls back to the most recent reachable tag via git describe - BASE_DIGEST resolved at build time from the local Docker daemon; dry-run prints a hint when the base image is not yet pulled - Note: case/esac cannot be used inside $(shell) — Make's parser matches the pattern ")" as the function's closing paren before the shell sees it; if/elif/else/fi used instead Dockerfile: - OCI standard labels added to the final stage via build args (VERSION, GIT_SHA, BASE_DIGEST): org.opencontainers.image.source — repo URL org.opencontainers.image.version — semver tag org.opencontainers.image.revision — git commit SHA org.opencontainers.image.base.name — alpine:3.21 org.opencontainers.image.base.digest — digest of base image at build time - Custom label: dev.nochebuena.healthz — /health --- Dockerfile | 12 ++++++++ Makefile | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 Makefile diff --git a/Dockerfile b/Dockerfile index f338811..98f1bfb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,6 +13,18 @@ FROM alpine:3.21 RUN apk add --no-cache ca-certificates tzdata WORKDIR /app COPY --from=builder /app/spa-server . + +ARG VERSION=dev +ARG GIT_SHA=unknown +ARG BASE_DIGEST= + +LABEL org.opencontainers.image.source="https://code.nochebuena.dev/einherjar/spa-server" +LABEL org.opencontainers.image.version="$VERSION" +LABEL org.opencontainers.image.revision="$GIT_SHA" +LABEL org.opencontainers.image.base.name="alpine:3.21" +LABEL org.opencontainers.image.base.digest="$BASE_DIGEST" +LABEL dev.nochebuena.healthz="/health" + # Mount your SPA dist/ here, or COPY it in a downstream Dockerfile. VOLUME ["/srv/www"] EXPOSE 8080 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1a1bca4 --- /dev/null +++ b/Makefile @@ -0,0 +1,89 @@ +# ── Configuration ───────────────────────────────────────────────────────────── + +REGISTRY := code.nochebuena.dev/einherjar/spa-server +BASE_IMAGE := alpine:3.21 + +# ── Git metadata ────────────────────────────────────────────────────────────── + +BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null || echo unknown) +SHA := $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown) + +# Highest semver tag pointing at HEAD; falls back to the most recent reachable tag. +_TAG_AT_HEAD := $(shell git tag --points-at HEAD 2>/dev/null | sort -V | tail -1) +VERSION := $(if $(_TAG_AT_HEAD),$(_TAG_AT_HEAD),$(shell git describe --tags --abbrev=0 2>/dev/null || echo v0.0.0)) + +# ── Branch suffix ───────────────────────────────────────────────────────────── +# main → (none) +# develop → -dev- +# release/* → -qa- +# other → -dev- +# +# Note: case/esac cannot be used inside $(shell) — the pattern delimiters ")" +# are mistaken by Make's parser for the closing paren of the function call. +# if/elif is used instead. + +BRANCH_SUFFIX := $(shell \ + b='$(BRANCH)'; s='$(SHA)'; \ + if [ "$$b" = "main" ]; then \ + echo ""; \ + elif [ "$$b" = "develop" ]; then \ + echo "-dev-$$s"; \ + elif echo "$$b" | grep -q "^release/"; then \ + echo "-qa-$$s"; \ + else \ + echo "-dev-$$s"; \ + fi) + +# ── Image tags ──────────────────────────────────────────────────────────────── + +VERSIONED_TAG := $(REGISTRY):$(VERSION)$(BRANCH_SUFFIX) +LATEST_TAG := $(if $(filter main,$(BRANCH)),$(REGISTRY):latest,) +ALL_TAGS := $(strip $(VERSIONED_TAG) $(LATEST_TAG)) +TAG_FLAGS := $(foreach t,$(ALL_TAGS),--tag $(t) ) + +# ── Targets ─────────────────────────────────────────────────────────────────── + +.DEFAULT_GOAL := dry-run + +.PHONY: dry-run build push release help + +## dry-run : show what would be built and pushed — no side effects. +dry-run: + @echo "" + @echo " Registry : $(REGISTRY)" + @echo " Branch : $(BRANCH)" + @echo " SHA : $(SHA)" + @echo " Version : $(VERSION)" + @echo "" + @echo " Tags:" + @$(foreach t,$(ALL_TAGS),echo " $(t)";) + @echo "" + @echo " Build args:" + @echo " VERSION = $(VERSION)" + @echo " GIT_SHA = $(SHA)" + @BASE=$$(docker inspect --format='{{index .RepoDigests 0}}' $(BASE_IMAGE) 2>/dev/null | head -1); \ + echo " BASE_DIGEST = $${BASE:-(not resolved — run: docker pull $(BASE_IMAGE))}" + @echo "" + +## build : build the image with all computed tags and OCI labels. +build: + docker build \ + $(TAG_FLAGS) \ + --build-arg VERSION=$(VERSION) \ + --build-arg GIT_SHA=$(SHA) \ + --build-arg BASE_DIGEST=$$(docker inspect --format='{{index .RepoDigests 0}}' $(BASE_IMAGE) 2>/dev/null | head -1) \ + . + +## push : push all computed tags to the registry. +push: + @for tag in $(ALL_TAGS); do \ + echo "→ pushing $$tag"; \ + docker push $$tag || exit 1; \ + done + +## release : build then push. +release: build push + +## help : list available targets. +help: + @grep -E '^## ' Makefile | sed 's/^## / /'