Images: --provenance=false on buildx pushes; workflow passes the token via env, registry in one place, git version sort, temp builder config; build-images.sh shares the Gradle lock check
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
This commit is contained in:
@@ -10,16 +10,20 @@
|
|||||||
# write:repository for the tag push) of the user that pushes (owner step, infra/README.md).
|
# write:repository for the tag push) of the user that pushes (owner step, infra/README.md).
|
||||||
#
|
#
|
||||||
# RISK: the runner is a Raspberry Pi 3 with 1 GB RAM and the server image compiles about 232 Rust crates
|
# RISK: the runner is a Raspberry Pi 3 with 1 GB RAM and the server image compiles about 232 Rust crates
|
||||||
# (aws-lc-sys's C code included). The build passes CARGO_BUILD_JOBS=1 to keep memory down; server/Dockerfile must
|
# (aws-lc-sys's C code included). The build passes CARGO_BUILD_JOBS=1 to keep memory down; server/Dockerfile
|
||||||
# declare `ARG CARGO_BUILD_JOBS` in its build stage for it to take effect (contract request in the M2c-pi report;
|
# honours it (`ARG CARGO_BUILD_JOBS`, SPEC §10.3). The Dockerfile keeps the cargo registry and the target directory
|
||||||
# until then buildx warns that the argument is unused and cargo uses every core). The Dockerfile already keeps
|
# in BuildKit cache mounts, which persist in the reused builder `local-http-builder`, so later builds are
|
||||||
# the cargo registry and the target directory in BuildKit cache mounts, which persist in the reused builder
|
# incremental. First-build duration on the Pi: unknown.
|
||||||
# `local-http-builder`, so later builds are incremental. First-build duration on the Pi: unknown.
|
# The builder's buildkitd.toml is read only when the builder is created; after changing it, remove the builder on
|
||||||
|
# the runner once (docker buildx rm local-http-builder), which also drops its cache.
|
||||||
# Fallback: infra/build-images.sh on the Mac.
|
# Fallback: infra/build-images.sh on the Mac.
|
||||||
name: Build Kvitt images
|
name: Build Kvitt images
|
||||||
env:
|
env:
|
||||||
MAJOR: 1
|
MAJOR: 1
|
||||||
MINOR: 0
|
MINOR: 0
|
||||||
|
REGISTRY_HOST: gitea.lan
|
||||||
|
# ${KVITT_REGISTRY} of SPEC §10.3 (D-104).
|
||||||
|
REGISTRY: gitea.lan/jacobh2
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -41,17 +45,22 @@ jobs:
|
|||||||
- name: Install Git and Docker CLI
|
- name: Install Git and Docker CLI
|
||||||
run: apk add --no-cache git docker-cli docker-cli-buildx
|
run: apk add --no-cache git docker-cli docker-cli-buildx
|
||||||
- name: Log in to Gitea Container Registry
|
- name: Log in to Gitea Container Registry
|
||||||
run: echo "${{ secrets.kvitt_cicd }}" | docker login gitea.lan -u ${{ github.actor }} --password-stdin
|
# The token reaches the shell as an environment variable, never as text inside the script.
|
||||||
|
env:
|
||||||
|
REGISTRY_TOKEN: ${{ secrets.kvitt_cicd }}
|
||||||
|
REGISTRY_USER: ${{ github.actor }}
|
||||||
|
run: printf '%s' "$REGISTRY_TOKEN" | docker login "$REGISTRY_HOST" -u "$REGISTRY_USER" --password-stdin
|
||||||
- name: Create Custom Buildx Builder for HTTP
|
- name: Create Custom Buildx Builder for HTTP
|
||||||
run: |
|
run: |
|
||||||
# 1. Write a configuration rule forcing plain HTTP for the local registry
|
# Create the builder once and reuse it, so its BuildKit cache (cargo registry, target) survives runs.
|
||||||
echo '[registry."gitea.lan"]' > buildkitd.toml
|
|
||||||
echo ' http = true' >> buildkitd.toml
|
|
||||||
# 2. Create the builder once and reuse it, so its BuildKit cache (cargo registry, target) survives runs
|
|
||||||
if docker buildx inspect local-http-builder >/dev/null 2>&1; then
|
if docker buildx inspect local-http-builder >/dev/null 2>&1; then
|
||||||
docker buildx use local-http-builder
|
docker buildx use local-http-builder
|
||||||
else
|
else
|
||||||
docker buildx create --use --name local-http-builder --config buildkitd.toml
|
# A configuration rule forcing plain HTTP for the local registry.
|
||||||
|
cfg=$(mktemp)
|
||||||
|
printf '[registry."%s"]\n http = true\n' "$REGISTRY_HOST" > "$cfg"
|
||||||
|
docker buildx create --use --name local-http-builder --config "$cfg"
|
||||||
|
rm -f "$cfg"
|
||||||
fi
|
fi
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -61,30 +70,35 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Determine Version
|
- name: Determine Version
|
||||||
run: |
|
run: |
|
||||||
LATEST_TAG=$(git tag -l "v${{ env.MAJOR }}.${{ env.MINOR }}.*" | sort -V | tail -n 1)
|
# git's own version sort: busybox sort on the Alpine runner may lack -V.
|
||||||
|
LATEST_TAG=$(git tag -l "v$MAJOR.$MINOR.*" --sort=-v:refname | head -n 1)
|
||||||
if [ -z "$LATEST_TAG" ]; then
|
if [ -z "$LATEST_TAG" ]; then
|
||||||
VERSION="v${{ env.MAJOR }}.${{ env.MINOR }}.0"
|
VERSION="v$MAJOR.$MINOR.0"
|
||||||
else
|
else
|
||||||
# Strip the 'v' and get the patch version
|
# The patch number is the part after the last dot.
|
||||||
PATCH=$(echo "$LATEST_TAG" | sed 's/v//' | cut -d'.' -f3)
|
VERSION="v$MAJOR.$MINOR.$((${LATEST_TAG##*.} + 1))"
|
||||||
NEW_PATCH=$((PATCH + 1))
|
|
||||||
VERSION="v${{ env.MAJOR }}.${{ env.MINOR }}.$NEW_PATCH"
|
|
||||||
fi
|
fi
|
||||||
|
echo "Version: $VERSION"
|
||||||
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
|
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
|
||||||
|
# --provenance=false on every build: one plain linux/arm64 manifest per tag, as Podman and infra/build-images.sh
|
||||||
|
# push. Without it buildx pushes an index with an attestation entry (platform unknown/unknown), which the Gitea
|
||||||
|
# package page lists as an extra platform; nothing here reads attestations.
|
||||||
- name: Build cert-sync image
|
- name: Build cert-sync image
|
||||||
run: |
|
run: |
|
||||||
docker buildx build \
|
docker buildx build \
|
||||||
--platform linux/arm64 \
|
--platform linux/arm64 \
|
||||||
-t gitea.lan/jacobh2/kvitt-cert-sync:${{ env.VERSION }} \
|
--provenance=false \
|
||||||
-t gitea.lan/jacobh2/kvitt-cert-sync:latest \
|
-t "$REGISTRY/kvitt-cert-sync:$VERSION" \
|
||||||
|
-t "$REGISTRY/kvitt-cert-sync:latest" \
|
||||||
./infra/cert-sync \
|
./infra/cert-sync \
|
||||||
--push
|
--push
|
||||||
- name: Build backup image
|
- name: Build backup image
|
||||||
run: |
|
run: |
|
||||||
docker buildx build \
|
docker buildx build \
|
||||||
--platform linux/arm64 \
|
--platform linux/arm64 \
|
||||||
-t gitea.lan/jacobh2/kvitt-backup:${{ env.VERSION }} \
|
--provenance=false \
|
||||||
-t gitea.lan/jacobh2/kvitt-backup:latest \
|
-t "$REGISTRY/kvitt-backup:$VERSION" \
|
||||||
|
-t "$REGISTRY/kvitt-backup:latest" \
|
||||||
./infra/backup \
|
./infra/backup \
|
||||||
--push
|
--push
|
||||||
- name: Build server image
|
- name: Build server image
|
||||||
@@ -92,10 +106,11 @@ jobs:
|
|||||||
# Context server/: server/Dockerfile copies the Cargo workspace from its own directory.
|
# Context server/: server/Dockerfile copies the Cargo workspace from its own directory.
|
||||||
docker buildx build \
|
docker buildx build \
|
||||||
--platform linux/arm64 \
|
--platform linux/arm64 \
|
||||||
|
--provenance=false \
|
||||||
--build-arg CARGO_BUILD_JOBS=1 \
|
--build-arg CARGO_BUILD_JOBS=1 \
|
||||||
-f ./server/Dockerfile \
|
-f ./server/Dockerfile \
|
||||||
-t gitea.lan/jacobh2/kvitt-server:${{ env.VERSION }} \
|
-t "$REGISTRY/kvitt-server:$VERSION" \
|
||||||
-t gitea.lan/jacobh2/kvitt-server:latest \
|
-t "$REGISTRY/kvitt-server:latest" \
|
||||||
./server \
|
./server \
|
||||||
--push
|
--push
|
||||||
- name: Tag and Push Version
|
- name: Tag and Push Version
|
||||||
|
|||||||
+11
-5
@@ -15,10 +15,11 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
cd "$repo_root"
|
cd "$repo_root"
|
||||||
|
# shellcheck source=../tools/gradle-lock.sh
|
||||||
|
. "$repo_root/tools/gradle-lock.sh"
|
||||||
|
|
||||||
[[ -z "$(git status --porcelain)" ]] || { echo "build-images.sh: the working tree is dirty; commit or stash first." >&2; exit 1; }
|
[[ -z "$(git status --porcelain)" ]] || { echo "build-images.sh: the working tree is dirty; commit or stash first." >&2; exit 1; }
|
||||||
lock=/tmp/kvitt-gradle.lock
|
if pid="$(gradle_lock_holder)"; then
|
||||||
if [[ -d "$lock" ]] && pid="$(cat "$lock/pid" 2>/dev/null)" && [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
|
|
||||||
echo "build-images.sh: a Gradle build is running (pid $pid); try again when it is done." >&2
|
echo "build-images.sh: a Gradle build is running (pid $pid); try again when it is done." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -27,10 +28,11 @@ tag="${1:-$(git rev-parse --short HEAD)}"
|
|||||||
[[ "$tag" =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$ ]] || { echo "build-images.sh: not a valid tag: $tag" >&2; exit 2; }
|
[[ "$tag" =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$ ]] || { echo "build-images.sh: not a valid tag: $tag" >&2; exit 2; }
|
||||||
|
|
||||||
reg_file="$repo_root/infra/secrets/registry.env"
|
reg_file="$repo_root/infra/secrets/registry.env"
|
||||||
registry="gitea.lan/jacobh2"; user=""; token=""; push=0
|
default_registry="gitea.lan/jacobh2" # SPEC §10.3, D-104
|
||||||
|
registry="$default_registry"; user=""; token=""; push=0
|
||||||
if [[ -f "$reg_file" ]]; then
|
if [[ -f "$reg_file" ]]; then
|
||||||
value() { sed -n "s/^$1=//p" "$reg_file" | tail -n 1; }
|
value() { sed -n "s/^$1=//p" "$reg_file" | tail -n 1; }
|
||||||
registry="$(value KVITT_REGISTRY)"; registry="${registry:-gitea.lan/jacobh2}"
|
registry="$(value KVITT_REGISTRY)"; registry="${registry:-$default_registry}"
|
||||||
user="$(value KVITT_REGISTRY_USER)"; token="$(value KVITT_REGISTRY_TOKEN)"
|
user="$(value KVITT_REGISTRY_USER)"; token="$(value KVITT_REGISTRY_TOKEN)"
|
||||||
push=1
|
push=1
|
||||||
fi
|
fi
|
||||||
@@ -58,9 +60,11 @@ if [[ $engine == docker && $push == 1 ]]; then
|
|||||||
# A buildx builder that may push to the plain-HTTP registry (same rule as the Gitea workflow).
|
# A buildx builder that may push to the plain-HTTP registry (same rule as the Gitea workflow).
|
||||||
if ! docker buildx inspect kvitt-http-builder >/dev/null 2>&1; then
|
if ! docker buildx inspect kvitt-http-builder >/dev/null 2>&1; then
|
||||||
cfg="$(mktemp)"
|
cfg="$(mktemp)"
|
||||||
|
trap 'rm -f "$cfg"' EXIT
|
||||||
printf '[registry."%s"]\n http = true\n' "$registry_host" > "$cfg"
|
printf '[registry."%s"]\n http = true\n' "$registry_host" > "$cfg"
|
||||||
docker buildx create --name kvitt-http-builder --config "$cfg" >/dev/null
|
docker buildx create --name kvitt-http-builder --config "$cfg" >/dev/null
|
||||||
rm -f "$cfg"
|
rm -f "$cfg"
|
||||||
|
trap - EXIT
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -75,7 +79,9 @@ for spec in "${images[@]}"; do
|
|||||||
podman push --tls-verify=false "$ref:latest"
|
podman push --tls-verify=false "$ref:latest"
|
||||||
fi
|
fi
|
||||||
elif [[ $push == 1 ]]; then
|
elif [[ $push == 1 ]]; then
|
||||||
docker buildx build --builder kvitt-http-builder --platform linux/arm64 -f "$dockerfile" \
|
# --provenance=false: push a plain image manifest, as Podman does, rather than an index with an attestation
|
||||||
|
# entry (same choice as the Gitea workflow).
|
||||||
|
docker buildx build --builder kvitt-http-builder --platform linux/arm64 --provenance=false -f "$dockerfile" \
|
||||||
-t "$ref:$tag" -t "$ref:latest" --push "$context"
|
-t "$ref:$tag" -t "$ref:latest" --push "$context"
|
||||||
else
|
else
|
||||||
docker buildx build --platform linux/arm64 -f "$dockerfile" -t "$ref:$tag" -t "$ref:latest" --load "$context"
|
docker buildx build --platform linux/arm64 -f "$dockerfile" -t "$ref:$tag" -t "$ref:latest" --load "$context"
|
||||||
|
|||||||
Reference in New Issue
Block a user