Add Gitea build, container tests and registry publication
All checks were successful
Build, test and publish / verify (push) Successful in 1m41s

This commit is contained in:
2026-10-28 17:07:23 +01:00
parent e3e362c2ac
commit 74a5aa1237
26 changed files with 1180 additions and 12 deletions

76
ci/publish.sh Normal file
View File

@@ -0,0 +1,76 @@
#!/usr/bin/env bash
source "$(dirname -- "$0")/common.sh"
# Also enforce this inside the script, independently of the workflow expression.
[[ "${CI_REF:-}" == refs/heads/main ]] || { echo 'Branch is not main; no publication'; exit 0; }
[[ -f "$ARTIFACTS/verified-commit" && "$(cat "$ARTIFACTS/verified-commit")" == "$CI_COMMIT" ]] || {
echo 'This commit has not passed all verification steps' >&2; exit 1;
}
: "${REGISTRY_USER:?Missing REGISTRY_USER secret}"
: "${REGISTRY_TOKEN:?Missing REGISTRY_TOKEN secret}"
: "${GITEA_SERVER_URL:?Missing Gitea server URL}"
: "${CI_REPOSITORY:?Missing repository}"
require_platform
exec > >(tee "$ARTIFACTS/publish.log") 2>&1
REGISTRY=${GITEA_SERVER_URL#http://}
REGISTRY=${REGISTRY#https://}
REGISTRY=${REGISTRY%/}
[[ "$REGISTRY" != */* ]] || { echo 'Registry must be hosted at the Gitea server root'; exit 1; }
OWNER=${CI_REPOSITORY%%/*}
OWNER=${OWNER,,}
[[ "$OWNER" =~ ^[a-z0-9][a-z0-9_.-]*$ ]] || { echo 'Invalid registry namespace'; exit 1; }
# Gitea 1.25 ignores workflow concurrency. All publications on the sole runner
# share this lock, outside checkout directories and across workflow attempts.
lock_dir=${CI_PUBLISH_LOCK_DIR:-${XDG_STATE_HOME:-$HOME/.local/state}/agiledancers-ci}
mkdir -p "$lock_dir"
exec 9>"$lock_dir/publish.lock"
flock -w 600 9
# Preserve the selected daemon and isolate credentials from the developer's config.
isolate_docker_config
trap 'rm -rf -- "$DOCKER_CONFIG"' EXIT
trap 'exit 143' TERM
trap 'exit 130' INT
printf '%s' "$REGISTRY_TOKEN" | docker login "$REGISTRY" --username "$REGISTRY_USER" --password-stdin
ensure_commit_image() {
local role=$1 source=$2 repository="$REGISTRY/$OWNER/agiledancers-$1" ref error
ref="$repository:$CI_COMMIT"
error=$(mktemp)
if docker manifest inspect --insecure "$ref" > /dev/null 2>"$error"; then
echo "Preserving existing $ref"
elif grep -Eqi 'manifest unknown|no such manifest' "$error"; then
docker tag "$source" "$ref"
docker push "$ref"
else
# Authentication/network/server errors are never treated as missing tags.
cat "$error" >&2
rm -f "$error"
return 1
fi
rm -f "$error"
docker pull --platform "$TARGET_PLATFORM" "$ref"
[[ "$(docker image inspect --format '{{index .Config.Labels "org.opencontainers.image.revision"}}' "$ref")" == "$CI_COMMIT" ]] || {
echo "Existing $role image has an unexpected source revision" >&2; return 1;
}
docker image inspect --format '{{range .RepoDigests}}{{println .}}{{end}}' "$ref" \
> "$ARTIFACTS/$role-digests.txt"
grep -Fq "$repository@sha256:" "$ARTIFACTS/$role-digests.txt" || { echo 'Missing registry digest'; return 1; }
}
ensure_commit_image backend "$BACKEND_IMAGE"
ensure_commit_image frontend "$FRONTEND_IMAGE"
# Both immutable images are now pull-verified. Only current main may move aliases.
head=$(python3 ci/main-head.py)
if [[ "$head" != "$CI_COMMIT" ]]; then
echo 'A newer main commit exists; leaving moving tags unchanged.'
exit 0
fi
for role in backend frontend; do
repository="$REGISTRY/$OWNER/agiledancers-$role"
# Recheck under the publication lock before each moving-tag write.
[[ "$(python3 ci/main-head.py)" == "$CI_COMMIT" ]] || {
echo 'Main advanced during publication; leaving remaining moving tags unchanged.'
exit 0
}
docker tag "$repository:$CI_COMMIT" "$repository:main"
docker push "$repository:main"
docker pull --platform "$TARGET_PLATFORM" "$repository:main"
done