diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..5bdb3ef --- /dev/null +++ b/.env.example @@ -0,0 +1,16 @@ +# Copy this file to .env and fill in the values. +# .env is gitignored — never commit real credentials. + +# ── Container engine (docker | podman) ── +CONTAINER_ENGINE=docker + +# ── Docker Registry ── +REGISTRY=kcr.kollect.biz +REGISTRY_USER='robot$kollect-tools+ci-builder' +REGISTRY_PASS=harbor-robot-password +IMAGE_TAG=latest + +# ── OWASP Dependency-Check ── +# Get an API key at https://nvd.nist.gov/developers/request-an-api-key +# Optional but strongly recommended — without it NVD rate-limits to 5 req / 30s. +NVD_API_KEY= diff --git a/.gitea/workflows/build-and-push.yaml b/.gitea/workflows/build-and-push.yaml index a0f6d81..8b137cf 100644 --- a/.gitea/workflows/build-and-push.yaml +++ b/.gitea/workflows/build-and-push.yaml @@ -74,8 +74,26 @@ jobs: --password-stdin - name: Build image + env: + DOCKER_BUILDKIT: "1" run: | + # Pull the previous :latest to use as a layer cache source. + # First builds (no prior image) fail this pull — that's fine. + docker pull "${{ steps.meta.outputs.tag_latest }}" || true + + # On schedule / manual dispatch, bust the OWASP NVD layer so the + # weekly rebuild actually refreshes the NVD database. Push builds + # leave it empty so unchanged layers stay cached. + NVD_REFRESH="" + if [ "${{ github.event_name }}" = "schedule" ] || \ + [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + NVD_REFRESH="$(date +%Y%m%d)" + fi + docker build \ + --build-arg BUILDKIT_INLINE_CACHE=1 \ + --build-arg NVD_REFRESH="${NVD_REFRESH}" \ + --cache-from "${{ steps.meta.outputs.tag_latest }}" \ --label "org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}" \ --label "org.opencontainers.image.revision=${{ github.sha }}" \ -t "${{ steps.meta.outputs.tag_sha }}" \ diff --git a/build-and-push.ps1 b/build-and-push.ps1 index abc39c3..8a0b58c 100644 --- a/build-and-push.ps1 +++ b/build-and-push.ps1 @@ -1,7 +1,10 @@ #Requires -Version 5.1 <# .SYNOPSIS - Build and push a CI image to the Harbor registry using podman. + Build and push a CI image to the Harbor registry. + + The container engine is read from CONTAINER_ENGINE in .env + (docker | podman). Defaults to docker when unset. .EXAMPLE .\build-and-push.ps1 -Image ci/java-builder @@ -36,6 +39,16 @@ foreach ($v in 'REGISTRY', 'REGISTRY_USER', 'REGISTRY_PASS') { } } +# --- Resolve container engine --- +$engine = if ($env:CONTAINER_ENGINE) { $env:CONTAINER_ENGINE.Trim().ToLower() } else { 'docker' } +if ($engine -notin @('docker', 'podman')) { + throw "CONTAINER_ENGINE must be 'docker' or 'podman' (got '$engine')" +} +if (-not (Get-Command $engine -ErrorAction SilentlyContinue)) { + throw "Container engine '$engine' not found on PATH" +} +Write-Host "=> Using container engine: $engine" + # --- Resolve paths --- # $Image is the path to the image directory relative to the repo root, and # also the image name pushed to the registry (e.g. "ci/java-builder"). @@ -72,7 +85,7 @@ $tags = @('latest', $Version, $date) # (PowerShell's native pipe appends CRLF, which breaks --password-stdin). Write-Host "=> Logging in to $regHost" $psi = New-Object System.Diagnostics.ProcessStartInfo -$psi.FileName = 'podman' +$psi.FileName = $engine $psi.Arguments = "login $regHost --username `"$($env:REGISTRY_USER)`" --password-stdin" $psi.RedirectStandardInput = $true $psi.UseShellExecute = $false @@ -80,7 +93,7 @@ $proc = [System.Diagnostics.Process]::Start($psi) $proc.StandardInput.Write($env:REGISTRY_PASS) $proc.StandardInput.Close() $proc.WaitForExit() -if ($proc.ExitCode -ne 0) { throw "podman login failed (exit $($proc.ExitCode))" } +if ($proc.ExitCode -ne 0) { throw "$engine login failed (exit $($proc.ExitCode))" } # --- Build --- Write-Host "=> Building ${repo}:latest" @@ -90,21 +103,21 @@ if ($env:NVD_API_KEY) { } $buildArgs += $imageDir -& podman @buildArgs -if ($LASTEXITCODE -ne 0) { throw "podman build failed" } +& $engine @buildArgs +if ($LASTEXITCODE -ne 0) { throw "$engine build failed" } # --- Tag --- foreach ($tag in $Version, $date) { Write-Host "=> Tagging ${repo}:$tag" - podman tag "${repo}:latest" "${repo}:$tag" - if ($LASTEXITCODE -ne 0) { throw "podman tag failed for $tag" } + & $engine tag "${repo}:latest" "${repo}:$tag" + if ($LASTEXITCODE -ne 0) { throw "$engine tag failed for $tag" } } # --- Push --- foreach ($tag in $tags) { Write-Host "=> Pushing ${repo}:$tag" - podman push "${repo}:$tag" - if ($LASTEXITCODE -ne 0) { throw "podman push failed for $tag" } + & $engine push "${repo}:$tag" + if ($LASTEXITCODE -ne 0) { throw "$engine push failed for $tag" } } Write-Host "Done. Pushed $($tags.Count) tags to $repo" diff --git a/ci/java-builder/Dockerfile b/ci/java-builder/Dockerfile index f7819ff..ce78353 100644 --- a/ci/java-builder/Dockerfile +++ b/ci/java-builder/Dockerfile @@ -66,11 +66,14 @@ RUN curl -fsSL "https://dlcdn.apache.org/maven/maven-3/${MAVEN_VERSION}/binaries # # Rebuild this image weekly to keep the NVD database fresh. # ───────────────────────────────────────────────────────────────────── -ARG OWASP_DC_VERSION=12.2.1 +ARG OWASP_DC_VERSION=12.2.2 ARG NVD_API_KEY="" +# Bump to invalidate the cached NVD layer (e.g. weekly date stamp from CI). +ARG NVD_REFRESH="" ENV OWASP_DATA_DIR=/opt/owasp/dependency-check-data -RUN if [ -n "${NVD_API_KEY}" ]; then \ +RUN echo "NVD_REFRESH=${NVD_REFRESH}" \ + && if [ -n "${NVD_API_KEY}" ]; then \ echo "NVD API key: set (length=$(printf %s "${NVD_API_KEY}" | wc -c))"; \ else \ echo "WARNING: NVD_API_KEY is empty — NVD will rate-limit at 5 req / 30s"; \ @@ -113,7 +116,7 @@ RUN curl -fsSL "https://github.com/bufbuild/buf/releases/download/v${BUF_VERSION # Last because it's the most volatile pin and corepack prepare is the # cheapest layer; bumping pnpm shouldn't force any other layer to rebuild. # ───────────────────────────────────────────────────────────────────── -ARG PNPM_VERSION=11.0.6 +ARG PNPM_VERSION=11.1.1 RUN corepack enable \ && corepack prepare "pnpm@${PNPM_VERSION}" --activate