The Dockerfile already centralizes all version pins into top-level ARGs
(GO_VERSION, ALPINE_IMAGE, CROWDSEC_VERSION, EXPR_LANG_VERSION, XNET_VERSION).
This change closes the remaining gaps so those ARGs are the single source of
truth end-to-end:
- nightly-build.yml now resolves the Alpine image digest at build time and
passes ALPINE_IMAGE as a build-arg, matching the docker-build.yml pattern.
Previously, nightly images were built with the Dockerfile ARG default and
without a pinned digest, making runtime Alpine differ from docker-build.yml.
- six CI workflows (quality-checks, codecov-upload, benchmark, e2e-tests-split,
release-goreleaser, codeql) declared a GO_VERSION env var but their setup-go
steps ignored it and hardcoded the version string directly. They now reference
${{ env.GO_VERSION }}, so Renovate only needs to update one value per file
and the env var actually serves its purpose.
- codeql.yml had no GO_VERSION env var at all; one is now added alongside the
existing GOTOOLCHAIN: auto entry.
When Renovate bumps Go, it updates the env var at the top of each workflow and
the Dockerfile ARG — zero manual hunting required.
524 lines
23 KiB
Markdown
524 lines
23 KiB
Markdown
# Dockerfile Version Consolidation Plan
|
||
|
||
**Status:** Draft
|
||
**Created:** 2026-03-06
|
||
**Scope:** Single PR — consolidate all duplicated version references in `Dockerfile` into top-level ARGs
|
||
|
||
---
|
||
|
||
## 1. Problem Statement
|
||
|
||
When Go was bumped from 1.26.0 to 1.26.1, the version appeared in 4 separate `FROM golang:X.XX.X-alpine` lines. Renovate's Docker manager updated some but not all, requiring manual fixes. The same class of duplication exists for Alpine base images, CrowdSec version/SHA, and Go dependency patch versions.
|
||
|
||
**Root cause:** Version values are duplicated across build stages instead of being declared once at the top level and referenced via ARG interpolation.
|
||
|
||
**EARS Requirement:**
|
||
> WHEN a dependency version is updated (by Renovate or manually),
|
||
> THE SYSTEM SHALL require exactly one edit location per version.
|
||
|
||
---
|
||
|
||
## 2. Full Inventory of Duplicated Version References
|
||
|
||
### 2.1 Go Toolchain Version (`golang:1.26.1-alpine`)
|
||
|
||
| # | Line | Stage | Current Code |
|
||
|---|------|-------|-------------|
|
||
| 1 | 47 | `gosu-builder` | `FROM --platform=$BUILDPLATFORM golang:1.26.1-alpine AS gosu-builder` |
|
||
| 2 | 98 | `backend-builder` | `FROM --platform=$BUILDPLATFORM golang:1.26.1-alpine AS backend-builder` |
|
||
| 3 | 200 | `caddy-builder` | `FROM --platform=$BUILDPLATFORM golang:1.26.1-alpine AS caddy-builder` |
|
||
| 4 | 297 | `crowdsec-builder` | `FROM --platform=$BUILDPLATFORM golang:1.26.1-alpine AS crowdsec-builder` |
|
||
|
||
**Renovate annotation:** Each line currently has its own `# renovate: datasource=docker depName=golang` comment. With 4 copies, Renovate may match and PR-update only a subset when generating grouped PRs, leaving the others stale.
|
||
|
||
### 2.2 Alpine Base Image (`alpine:3.23.3@sha256:...`)
|
||
|
||
| # | Line | Stage/Context | Current Code |
|
||
|---|------|--------------|-------------|
|
||
| 1 | 30 | Top-level ARG | `ARG CADDY_IMAGE=alpine:3.23.3@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659` |
|
||
| 2 | 358 | `crowdsec-fallback` | `FROM alpine:3.23.3@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659 AS crowdsec-fallback` |
|
||
|
||
**Problem:** The `CADDY_IMAGE` ARG is already top-level with a Renovate annotation (line 29), but the `crowdsec-fallback` stage hardcodes the same version+digest independently with its own Renovate annotation (line 357). Both can drift.
|
||
|
||
### 2.3 CrowdSec Version + SHA256
|
||
|
||
| # | Lines | Stage | Current Code |
|
||
|---|-------|-------|-------------|
|
||
| 1 | 303–305 | `crowdsec-builder` | `ARG CROWDSEC_VERSION=1.7.6` + `ARG CROWDSEC_RELEASE_SHA256=704e37...` |
|
||
| 2 | 367–368 | `crowdsec-fallback` | `ARG CROWDSEC_VERSION=1.7.6` + `ARG CROWDSEC_RELEASE_SHA256=704e37...` |
|
||
|
||
**Problem:** Both stages independently declare the same version and SHA. The `# renovate:` annotation exists on each, so Renovate may update one and miss the other in a grouped PR.
|
||
|
||
### 2.4 Go Dependency Patch: `github.com/expr-lang/expr`
|
||
|
||
| # | Line | Stage | Current Code |
|
||
|---|------|-------|-------------|
|
||
| 1 | 255 | `caddy-builder` | `go get github.com/expr-lang/expr@v1.17.7` |
|
||
| 2 | 325 | `crowdsec-builder` | `go get github.com/expr-lang/expr@v1.17.7` |
|
||
|
||
### 2.5 Go Dependency Patch: `golang.org/x/net`
|
||
|
||
| # | Line | Stage | Current Code |
|
||
|---|------|-------|-------------|
|
||
| 1 | 259 | `caddy-builder` | `go get golang.org/x/net@v0.51.0` |
|
||
| 2 | 327 | `crowdsec-builder` | `go get golang.org/x/net@v0.51.0` |
|
||
|
||
### 2.6 Non-Duplicated References (For Completeness)
|
||
|
||
These appear only once and need no consolidation but are noted for context:
|
||
|
||
| Dependency | Line | Stage |
|
||
|-----------|------|-------|
|
||
| `golang.org/x/crypto@v0.46.0` | 326 | `crowdsec-builder` only |
|
||
| `github.com/hslatman/ipstore@v0.4.0` | 257 | `caddy-builder` only |
|
||
| `github.com/slackhq/nebula@v1.9.7` | 268 | `caddy-builder` only (conditional) |
|
||
|
||
---
|
||
|
||
## 3. Proposed Top-Level ARG Consolidation
|
||
|
||
### 3.1 New Top-Level ARGs
|
||
|
||
Add these after the existing `ARG BUILD_DEBUG=0` block (around line 9) and before the Caddy-related ARGs:
|
||
|
||
```dockerfile
|
||
# ---- Pinned Toolchain Versions ----
|
||
# renovate: datasource=docker depName=golang versioning=docker
|
||
ARG GO_VERSION=1.26.1
|
||
|
||
# renovate: datasource=docker depName=alpine versioning=docker
|
||
ARG ALPINE_IMAGE=alpine:3.23.3@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659
|
||
|
||
# ---- CrowdSec Version ----
|
||
# renovate: datasource=github-releases depName=crowdsecurity/crowdsec
|
||
ARG CROWDSEC_VERSION=1.7.6
|
||
# CrowdSec fallback tarball checksum (v${CROWDSEC_VERSION})
|
||
ARG CROWDSEC_RELEASE_SHA256=704e37121e7ac215991441cef0d8732e33fa3b1a2b2b88b53a0bfe5e38f863bd
|
||
|
||
# ---- Shared Go Security Patches ----
|
||
# renovate: datasource=go depName=github.com/expr-lang/expr
|
||
ARG EXPR_LANG_VERSION=1.17.7
|
||
# renovate: datasource=go depName=golang.org/x/net
|
||
ARG XNET_VERSION=0.51.0
|
||
```
|
||
|
||
### 3.2 ARG Naming Conventions
|
||
|
||
| ARG Name | Value | Tracks |
|
||
|----------|-------|--------|
|
||
| `GO_VERSION` | `1.26.1` | Go toolchain version (bare semver, appended to `golang:` in FROM) |
|
||
| `ALPINE_IMAGE` | `alpine:3.23.3@sha256:...` | Full image reference with digest pin |
|
||
| `CROWDSEC_VERSION` | `1.7.6` | CrowdSec release tag (moved from per-stage) |
|
||
| `CROWDSEC_RELEASE_SHA256` | `704e37...` | Tarball checksum for fallback (moved from per-stage) |
|
||
| `EXPR_LANG_VERSION` | `1.17.7` | `github.com/expr-lang/expr` Go module version |
|
||
| `XNET_VERSION` | `0.51.0` | `golang.org/x/net` Go module version |
|
||
|
||
### 3.3 Existing ARG Rename
|
||
|
||
The current `ARG CADDY_IMAGE=alpine:3.23.3@sha256:...` (line 30) should be **replaced** by the new `ALPINE_IMAGE` ARG. All references to `CADDY_IMAGE` downstream must be updated to `ALPINE_IMAGE`.
|
||
|
||
---
|
||
|
||
## 4. Line-by-Line Change Specification
|
||
|
||
### 4.1 New Top-Level ARG Block
|
||
|
||
**Location:** After line 9 (`ARG BUILD_DEBUG=0`), insert the new ARGs before Caddy version ARGs.
|
||
|
||
```
|
||
Old (lines 9–14):
|
||
ARG BUILD_DEBUG=0
|
||
<blank>
|
||
# Allow pinning Caddy version...
|
||
|
||
New (lines 9–28):
|
||
ARG BUILD_DEBUG=0
|
||
|
||
# ---- Pinned Toolchain Versions ----
|
||
# renovate: datasource=docker depName=golang versioning=docker
|
||
ARG GO_VERSION=1.26.1
|
||
|
||
# renovate: datasource=docker depName=alpine versioning=docker
|
||
ARG ALPINE_IMAGE=alpine:3.23.3@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659
|
||
|
||
# ---- CrowdSec Version ----
|
||
# renovate: datasource=github-releases depName=crowdsecurity/crowdsec
|
||
ARG CROWDSEC_VERSION=1.7.6
|
||
ARG CROWDSEC_RELEASE_SHA256=704e37121e7ac215991441cef0d8732e33fa3b1a2b2b88b53a0bfe5e38f863bd
|
||
|
||
# ---- Shared Go Security Patches ----
|
||
# renovate: datasource=go depName=github.com/expr-lang/expr
|
||
ARG EXPR_LANG_VERSION=1.17.7
|
||
# renovate: datasource=go depName=golang.org/x/net
|
||
ARG XNET_VERSION=0.51.0
|
||
|
||
# Allow pinning Caddy version...
|
||
```
|
||
|
||
### 4.2 Remove `CADDY_IMAGE` ARG (Old Alpine Reference)
|
||
|
||
**Line 29-30** — Remove the entire `CADDY_IMAGE` ARG block:
|
||
```
|
||
Old:
|
||
# renovate: datasource=docker depName=alpine versioning=docker
|
||
ARG CADDY_IMAGE=alpine:3.23.3@sha256:...
|
||
|
||
New:
|
||
(removed — replaced by top-level ALPINE_IMAGE)
|
||
```
|
||
|
||
Find all downstream references to `${CADDY_IMAGE}` and replace with `${ALPINE_IMAGE}`. Search for usage in the runtime stage (likely `FROM ${CADDY_IMAGE}`).
|
||
|
||
### 4.3 Go FROM Lines (4 Changes)
|
||
|
||
Each `FROM golang:1.26.1-alpine` line changes to `FROM golang:${GO_VERSION}-alpine`. The Renovate comment above each is **removed** (the single top-level annotation handles tracking).
|
||
|
||
#### 4.3.1 gosu-builder (line ~47)
|
||
```
|
||
Old:
|
||
# renovate: datasource=docker depName=golang
|
||
FROM --platform=$BUILDPLATFORM golang:1.26.1-alpine AS gosu-builder
|
||
|
||
New:
|
||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS gosu-builder
|
||
```
|
||
|
||
#### 4.3.2 backend-builder (line ~98)
|
||
```
|
||
Old:
|
||
# renovate: datasource=docker depName=golang
|
||
FROM --platform=$BUILDPLATFORM golang:1.26.1-alpine AS backend-builder
|
||
|
||
New:
|
||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS backend-builder
|
||
```
|
||
|
||
#### 4.3.3 caddy-builder (line ~200)
|
||
```
|
||
Old:
|
||
# renovate: datasource=docker depName=golang
|
||
FROM --platform=$BUILDPLATFORM golang:1.26.1-alpine AS caddy-builder
|
||
|
||
New:
|
||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS caddy-builder
|
||
```
|
||
|
||
#### 4.3.4 crowdsec-builder (line ~297)
|
||
```
|
||
Old:
|
||
# renovate: datasource=docker depName=golang versioning=docker
|
||
FROM --platform=$BUILDPLATFORM golang:1.26.1-alpine AS crowdsec-builder
|
||
|
||
New:
|
||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS crowdsec-builder
|
||
```
|
||
|
||
### 4.4 Alpine FROM Line (crowdsec-fallback)
|
||
|
||
**Line ~357-358:**
|
||
```
|
||
Old:
|
||
# renovate: datasource=docker depName=alpine versioning=docker
|
||
FROM alpine:3.23.3@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659 AS crowdsec-fallback
|
||
|
||
New:
|
||
FROM ${ALPINE_IMAGE} AS crowdsec-fallback
|
||
```
|
||
|
||
### 4.5 CrowdSec Version ARGs (Remove Per-Stage Duplicates)
|
||
|
||
#### 4.5.1 crowdsec-builder stage (lines ~303-305)
|
||
|
||
```
|
||
Old:
|
||
# CrowdSec version - Renovate can update this
|
||
# renovate: datasource=github-releases depName=crowdsecurity/crowdsec
|
||
ARG CROWDSEC_VERSION=1.7.6
|
||
# CrowdSec fallback tarball checksum (v${CROWDSEC_VERSION})
|
||
ARG CROWDSEC_RELEASE_SHA256=704e37121e7ac215991441cef0d8732e33fa3b1a2b2b88b53a0bfe5e38f863bd
|
||
|
||
New:
|
||
ARG CROWDSEC_VERSION
|
||
ARG CROWDSEC_RELEASE_SHA256
|
||
```
|
||
|
||
The bare `ARG` re-declarations (without defaults) inherit the top-level values. Remove the Renovate comments — tracking is on the top-level ARG.
|
||
|
||
#### 4.5.2 crowdsec-fallback stage (lines ~367-368)
|
||
|
||
```
|
||
Old:
|
||
# CrowdSec version - Renovate can update this
|
||
# renovate: datasource=github-releases depName=crowdsecurity/crowdsec
|
||
ARG CROWDSEC_VERSION=1.7.6
|
||
ARG CROWDSEC_RELEASE_SHA256=704e37121e7ac215991441cef0d8732e33fa3b1a2b2b88b53a0bfe5e38f863bd
|
||
|
||
New:
|
||
ARG CROWDSEC_VERSION
|
||
ARG CROWDSEC_RELEASE_SHA256
|
||
```
|
||
|
||
### 4.6 Go Dependency Patches
|
||
|
||
#### 4.6.1 Caddy builder — expr-lang (line ~254-255)
|
||
|
||
```
|
||
Old:
|
||
# renovate: datasource=go depName=github.com/expr-lang/expr
|
||
go get github.com/expr-lang/expr@v1.17.7; \
|
||
|
||
New:
|
||
go get github.com/expr-lang/expr@v${EXPR_LANG_VERSION}; \
|
||
```
|
||
|
||
Requires adding `ARG EXPR_LANG_VERSION` re-declaration inside the `caddy-builder` stage (after the existing `ARG` block). Remove the per-line Renovate comment.
|
||
|
||
#### 4.6.2 Caddy builder — golang.org/x/net (line ~258-259)
|
||
|
||
```
|
||
Old:
|
||
# renovate: datasource=go depName=golang.org/x/net
|
||
go get golang.org/x/net@v0.51.0; \
|
||
|
||
New:
|
||
go get golang.org/x/net@v${XNET_VERSION}; \
|
||
```
|
||
|
||
Requires adding `ARG XNET_VERSION` re-declaration inside the `caddy-builder` stage. Remove the per-line Renovate comment.
|
||
|
||
#### 4.6.3 CrowdSec builder — expr-lang + net (lines ~322-327)
|
||
|
||
```
|
||
Old:
|
||
# renovate: datasource=go depName=github.com/expr-lang/expr
|
||
# renovate: datasource=go depName=golang.org/x/crypto
|
||
# renovate: datasource=go depName=golang.org/x/net
|
||
RUN go get github.com/expr-lang/expr@v1.17.7 && \
|
||
go get golang.org/x/crypto@v0.46.0 && \
|
||
go get golang.org/x/net@v0.51.0 && \
|
||
go mod tidy
|
||
|
||
New:
|
||
# renovate: datasource=go depName=golang.org/x/crypto
|
||
RUN go get github.com/expr-lang/expr@v${EXPR_LANG_VERSION} && \
|
||
go get golang.org/x/crypto@v0.46.0 && \
|
||
go get golang.org/x/net@v${XNET_VERSION} && \
|
||
go mod tidy
|
||
```
|
||
|
||
The `golang.org/x/crypto` Renovate comment stays because that dependency only appears here (not duplicated). The `expr-lang` and `x/net` Renovate comments are removed — they're tracked on the top-level ARGs. Requires `ARG EXPR_LANG_VERSION` and `ARG XNET_VERSION` re-declarations in the `crowdsec-builder` stage.
|
||
|
||
### 4.7 ARG Re-declarations Per Stage
|
||
|
||
Docker's scoping rules require that top-level ARGs be re-declared (without value) inside each stage that uses them. The following `ARG` lines must be added to each stage:
|
||
|
||
| Stage | Required ARG Re-declarations |
|
||
|-------|------------------------------|
|
||
| `gosu-builder` | (none — `GO_VERSION` used in FROM, automatically available) |
|
||
| `backend-builder` | (none — same) |
|
||
| `caddy-builder` | `ARG EXPR_LANG_VERSION` and `ARG XNET_VERSION` (used in RUN) |
|
||
| `crowdsec-builder` | `ARG CROWDSEC_VERSION` ¹, `ARG CROWDSEC_RELEASE_SHA256` ¹, `ARG EXPR_LANG_VERSION`, `ARG XNET_VERSION` |
|
||
| `crowdsec-fallback` | `ARG CROWDSEC_VERSION` ¹, `ARG CROWDSEC_RELEASE_SHA256` ¹ |
|
||
|
||
¹ Already re-declared, just need default values removed and Renovate comments removed.
|
||
|
||
**Important Docker ARG scoping note:**
|
||
- ARGs used in `FROM` interpolation (`GO_VERSION`, `ALPINE_IMAGE`) are evaluated at the global scope — they do NOT need re-declaration inside the stage.
|
||
- ARGs used in `RUN`, `ENV`, or other instructions inside a stage MUST be re-declared with a bare `ARG NAME` (no default) inside that stage to inherit the top-level value.
|
||
|
||
---
|
||
|
||
## 5. Renovate Annotation Strategy
|
||
|
||
### 5.1 Annotations to Keep (Top-Level Only)
|
||
|
||
| Location | Annotation | Tracks |
|
||
|----------|------------|--------|
|
||
| Top-level `ARG GO_VERSION` | `# renovate: datasource=docker depName=golang versioning=docker` | Go Docker image tag |
|
||
| Top-level `ARG ALPINE_IMAGE` | `# renovate: datasource=docker depName=alpine versioning=docker` | Alpine Docker image + digest |
|
||
| Top-level `ARG CROWDSEC_VERSION` | `# renovate: datasource=github-releases depName=crowdsecurity/crowdsec` | CrowdSec GitHub releases |
|
||
| Top-level `ARG EXPR_LANG_VERSION` | `# renovate: datasource=go depName=github.com/expr-lang/expr` | expr-lang Go module |
|
||
| Top-level `ARG XNET_VERSION` | `# renovate: datasource=go depName=golang.org/x/net` | x/net Go module |
|
||
|
||
### 5.2 Annotations to Remove
|
||
|
||
| Location | Reason |
|
||
|----------|--------|
|
||
| Line ~46 `# renovate: datasource=docker depName=golang` (gosu-builder) | Tracked by top-level `GO_VERSION` |
|
||
| Line ~97 `# renovate: datasource=docker depName=golang` (backend-builder) | Same |
|
||
| Line ~199 `# renovate: datasource=docker depName=golang` (caddy-builder) | Same |
|
||
| Line ~296 `# renovate: datasource=docker depName=golang versioning=docker` (crowdsec-builder) | Same |
|
||
| Line ~29 `# renovate: datasource=docker depName=alpine versioning=docker` (CADDY_IMAGE) | Replaced by `ALPINE_IMAGE` |
|
||
| Line ~357 `# renovate: datasource=docker depName=alpine versioning=docker` (crowdsec-fallback FROM) | Tracked by top-level `ALPINE_IMAGE` |
|
||
| Lines ~302-303 `# renovate:` annotations on crowdsec-builder CROWDSEC_VERSION | Tracked by top-level |
|
||
| Lines ~366-367 `# renovate:` annotations on crowdsec-fallback CROWDSEC_VERSION | Tracked by top-level |
|
||
| Line ~254 `# renovate: datasource=go depName=github.com/expr-lang/expr` (caddy-builder) | Tracked by top-level `EXPR_LANG_VERSION` |
|
||
| Line ~258 `# renovate: datasource=go depName=golang.org/x/net` (caddy-builder) | Tracked by top-level `XNET_VERSION` |
|
||
| Lines ~322-324 `# renovate:` for expr-lang and x/net (crowdsec-builder) | Tracked by top-level |
|
||
|
||
### 5.3 Renovate Configuration Update
|
||
|
||
The existing regex custom manager in `.github/renovate.json` for Go dependency patches currently matches the pattern:
|
||
|
||
```
|
||
#\s*renovate:\s*datasource=go\s+depName=(?<depName>[^\s]+)\s*\n\s*go get (?<depName2>[^@]+)@v(?<currentValue>[^\s|]+)
|
||
```
|
||
|
||
After this change, the `go get` lines will use `${EXPR_LANG_VERSION}` and `${XNET_VERSION}` instead of hardcoded versions. The regex manager will **no longer match** the `go get` lines for these two deps.
|
||
|
||
**Required renovate.json changes:**
|
||
|
||
Add new custom manager entries for the new top-level ARGs:
|
||
|
||
```json
|
||
{
|
||
"customType": "regex",
|
||
"description": "Track expr-lang version ARG in Dockerfile",
|
||
"managerFilePatterns": ["/^Dockerfile$/"],
|
||
"matchStrings": [
|
||
"ARG EXPR_LANG_VERSION=(?<currentValue>[^\\s]+)"
|
||
],
|
||
"depNameTemplate": "github.com/expr-lang/expr",
|
||
"datasourceTemplate": "go",
|
||
"versioningTemplate": "semver"
|
||
},
|
||
{
|
||
"customType": "regex",
|
||
"description": "Track golang.org/x/net version ARG in Dockerfile",
|
||
"managerFilePatterns": ["/^Dockerfile$/"],
|
||
"matchStrings": [
|
||
"ARG XNET_VERSION=(?<currentValue>[^\\s]+)"
|
||
],
|
||
"depNameTemplate": "golang.org/x/net",
|
||
"datasourceTemplate": "go",
|
||
"versioningTemplate": "semver"
|
||
}
|
||
```
|
||
|
||
**Also add for `GO_VERSION`** — Renovate's built-in Docker manager matches `FROM golang:X.X.X-alpine`, but after this change the FROM uses `${GO_VERSION}` interpolation. Renovate's Docker manager cannot parse variable-interpolated FROM lines. A custom regex manager is needed:
|
||
|
||
```json
|
||
{
|
||
"customType": "regex",
|
||
"description": "Track Go toolchain version ARG in Dockerfile",
|
||
"managerFilePatterns": ["/^Dockerfile$/"],
|
||
"matchStrings": [
|
||
"#\\s*renovate:\\s*datasource=docker\\s+depName=golang.*\\nARG GO_VERSION=(?<currentValue>[^\\s]+)"
|
||
],
|
||
"depNameTemplate": "golang",
|
||
"datasourceTemplate": "docker",
|
||
"versioningTemplate": "docker"
|
||
}
|
||
```
|
||
|
||
The existing Alpine regex manager already matches `ARG CADDY_IMAGE=...`. Update the regex to match the new ARG name `ALPINE_IMAGE`:
|
||
|
||
```
|
||
Old matchString:
|
||
"ARG CADDY_IMAGE=alpine:(?<currentValue>[^\\s@]+@sha256:[a-f0-9]+)"
|
||
|
||
New matchString:
|
||
"ARG ALPINE_IMAGE=alpine:(?<currentValue>[^\\s@]+@sha256:[a-f0-9]+)"
|
||
```
|
||
|
||
**CrowdSec version:** Already tracked by the existing regex manager pattern that matches `ARG CROWDSEC_VERSION=...`. Since we keep that ARG name unchanged, no renovate.json change is needed for CrowdSec — but verify only one match occurs (the top-level one) after removing the per-stage defaults.
|
||
|
||
---
|
||
|
||
## 6. Implementation Plan
|
||
|
||
### Phase 1: Playwright Tests (N/A)
|
||
|
||
This change is a build-system refactor with no UI/UX changes. No Playwright tests are needed. The Docker build itself is the validation artifact.
|
||
|
||
### Phase 2: Dockerfile Changes
|
||
|
||
| Step | Action | Files |
|
||
|------|--------|-------|
|
||
| 2.1 | Add new top-level ARG block | `Dockerfile` |
|
||
| 2.2 | Remove `CADDY_IMAGE` ARG, replace references with `ALPINE_IMAGE` | `Dockerfile` |
|
||
| 2.3 | Replace 4 `FROM golang:1.26.1-alpine` lines with `FROM golang:${GO_VERSION}-alpine`; remove per-line Renovate comments | `Dockerfile` |
|
||
| 2.4 | Replace `FROM alpine:3.23.3@sha256:...` in crowdsec-fallback with `FROM ${ALPINE_IMAGE}`; remove Renovate comment | `Dockerfile` |
|
||
| 2.5 | Convert per-stage `CROWDSEC_VERSION`/`SHA` to bare `ARG` re-declarations | `Dockerfile` |
|
||
| 2.6 | Add `ARG EXPR_LANG_VERSION` and `ARG XNET_VERSION` re-declarations in `caddy-builder` and `crowdsec-builder` stages | `Dockerfile` |
|
||
| 2.7 | Replace hardcoded `@v1.17.7` and `@v0.51.0` in `go get` with `@v${EXPR_LANG_VERSION}` and `@v${XNET_VERSION}` | `Dockerfile` |
|
||
|
||
### Phase 3: Renovate Configuration
|
||
|
||
| Step | Action | Files |
|
||
|------|--------|-------|
|
||
| 3.1 | Add `GO_VERSION` regex custom manager | `.github/renovate.json` |
|
||
| 3.2 | Add `EXPR_LANG_VERSION` regex custom manager | `.github/renovate.json` |
|
||
| 3.3 | Add `XNET_VERSION` regex custom manager | `.github/renovate.json` |
|
||
| 3.4 | Update Alpine regex manager to use `ALPINE_IMAGE` | `.github/renovate.json` |
|
||
| 3.5 | Verify the existing `go get` regex manager no longer double-matches consolidated deps | `.github/renovate.json` |
|
||
|
||
### Phase 4: Validation
|
||
|
||
| Step | Verification |
|
||
|------|--------------|
|
||
| 4.1 | `docker build --platform=linux/amd64 -t charon:test .` succeeds |
|
||
| 4.2 | `docker build --platform=linux/arm64 -t charon:test-arm .` succeeds (if cross-build infra available) |
|
||
| 4.3 | `grep -c 'golang:1.26' Dockerfile` returns `0` (no hardcoded Go versions remain in FROM lines) |
|
||
| 4.4 | `grep -c 'alpine:3.23' Dockerfile` returns `1` (only the top-level `ALPINE_IMAGE` ARG) |
|
||
| 4.5 | `grep -c 'CROWDSEC_VERSION=1.7' Dockerfile` returns `1` (only the top-level ARG) |
|
||
| 4.6 | `grep -c 'expr-lang/expr@v' Dockerfile` returns `0` (no hardcoded expr-lang versions in go get) |
|
||
| 4.7 | `grep -c 'x/net@v' Dockerfile` returns `0` (no hardcoded x/net versions in go get) |
|
||
| 4.8 | Renovate dry-run validates all new regex managers match exactly once |
|
||
|
||
### Phase 5: Documentation
|
||
|
||
Update `CHANGELOG.md` with a `chore: consolidate Dockerfile version ARGs` entry.
|
||
|
||
---
|
||
|
||
## 7. Risk Assessment
|
||
|
||
| Risk | Severity | Mitigation |
|
||
|------|----------|------------|
|
||
| **Docker ARG scoping**: ARG used in `FROM` requires global-scope declaration; ARG in `RUN` requires per-stage re-declaration | High | Explicitly tested in §4.1. Docker's scoping rules are well-documented. |
|
||
| **Renovate stops tracking**: Switching from inline `FROM` annotation to ARG-based regex manager may cause Renovate to lose tracking until config is deployed | Medium | Add regex managers in the same PR. Test with `renovate --dry-run` or Dependency Dashboard. |
|
||
| **Renovate double-matching**: Old `go get` regex manager might still match non-parameterized `go get` lines (crypto, ipstore) | Low | These deps keep their inline `# renovate:` comments and hardcoded versions. The regex correctly matches them. |
|
||
| **CADDY_IMAGE rename**: Any docker-compose override or CI script referencing `--build-arg CADDY_IMAGE=...` will break | Medium | Search codebase for `CADDY_IMAGE` references outside `Dockerfile`. Update CI workflows if found. |
|
||
| **Build cache invalidation**: Changing ARG values at the top level invalidates all downstream layer caches | Low | This is expected and already happens today when any version changes. No behavioral change. |
|
||
| **Cross-compilation**: `${GO_VERSION}` interpolation in multi-platform builds | Low | Docker resolves global ARGs before platform selection. Verified by BuildKit semantics. |
|
||
|
||
---
|
||
|
||
## 8. Commit Slicing Strategy
|
||
|
||
**Decision:** Single PR.
|
||
|
||
**Rationale:**
|
||
- All changes are confined to `Dockerfile` and `.github/renovate.json`
|
||
- No cross-domain changes (no backend/frontend code)
|
||
- The changes are logically atomic — partial consolidation would leave a confusing state
|
||
- Total diff is small (~30 lines changed, ~15 lines added, ~20 lines removed)
|
||
- Rollback is trivial: revert the single commit
|
||
|
||
**PR Slice:**
|
||
|
||
| Slice | Scope | Files | Validation Gate |
|
||
|-------|-------|-------|----------------|
|
||
| PR-1 (only) | Full consolidation | `Dockerfile`, `.github/renovate.json` | Docker build succeeds on amd64; `grep` checks pass; Renovate dry-run confirms tracking |
|
||
|
||
**Rollback:** `git revert <sha>` — single commit, no dependencies.
|
||
|
||
---
|
||
|
||
## 9. Acceptance Criteria
|
||
|
||
- [ ] `Dockerfile` has exactly one declaration of Go version (`ARG GO_VERSION=...`)
|
||
- [ ] `Dockerfile` has exactly one declaration of Alpine image+digest (`ARG ALPINE_IMAGE=...`)
|
||
- [ ] `Dockerfile` has exactly one declaration of CrowdSec version and SHA256 (top-level)
|
||
- [ ] `Dockerfile` has exactly one declaration of `expr-lang/expr` version (top-level ARG)
|
||
- [ ] `Dockerfile` has exactly one declaration of `golang.org/x/net` version (top-level ARG)
|
||
- [ ] All 4 Go `FROM` lines use `${GO_VERSION}` interpolation
|
||
- [ ] `crowdsec-fallback` FROM uses `${ALPINE_IMAGE}` interpolation
|
||
- [ ] Per-stage `CROWDSEC_VERSION` re-declarations are bare `ARG` (no default, no Renovate comment)
|
||
- [ ] `go get` lines use `${EXPR_LANG_VERSION}` and `${XNET_VERSION}` interpolation
|
||
- [ ] Renovate `# renovate:` annotations exist only on top-level ARGs (one per tracked dep)
|
||
- [ ] `.github/renovate.json` has regex managers for `GO_VERSION`, `EXPR_LANG_VERSION`, `XNET_VERSION`, and updated `ALPINE_IMAGE`
|
||
- [ ] `docker build` succeeds without errors
|
||
- [ ] No duplicate version values remain (verified by grep checks in §4)
|