Skip to content

Commit 806d54f

Browse files
refactor(build): standardize image push with shared docker utilities
- Add openshift/scripts/utils/docker.sh with push_to_artifactory() and build_and_push() functions (retry 3x with 15s backoff) - Moodle: restore --cache-to registry (removed in 5a537a8, caused manifest invalid errors), use push_to_artifactory for separate push - PHP: restore --output=type=image,push=true (working pattern), wrap with build_and_push for retry on transient Artifactory errors - Redis Proxy: same restore + build_and_push wrapper, extract build from report step into its own step
1 parent 4effb0f commit 806d54f

File tree

4 files changed

+134
-67
lines changed

4 files changed

+134
-67
lines changed

.github/workflows/moodle.yml

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,11 @@ jobs:
121121
run: |
122122
export BUILDKIT_PROGRESS=plain
123123
124-
# Build image but don't push yet
125-
# Cache reads from both registry and GHA (gracefully skips missing caches)
126-
# Cache writes to GHA only — registry cache is warmed in a separate non-blocking step
124+
# Build image but don't push yet — scan first, then push separately
127125
docker buildx build \
128126
--cache-from type=registry,ref=${{ secrets.ARTIFACTORY_URL }}/${{ env.MOODLE_DEPLOYMENT_NAME }}:buildcache \
129127
--cache-from type=gha,scope=moodle-${{ github.ref_name }} \
128+
--cache-to type=registry,ref=${{ secrets.ARTIFACTORY_URL }}/${{ env.MOODLE_DEPLOYMENT_NAME }}:buildcache,mode=max \
130129
--cache-to type=gha,scope=moodle-${{ github.ref_name }},mode=min \
131130
--build-arg MOODLE_URL=${{ inputs.MOODLE_URL }} \
132131
--build-arg MOODLE_BRANCH_VERSION=${{ inputs.MOODLE_BRANCH_VERSION }} \
@@ -187,20 +186,8 @@ jobs:
187186
"${{ secrets.ARTIFACTORY_URL }}/${{ env.MOODLE_DEPLOYMENT_NAME }}:${{ github.ref_name }}" \
188187
"$SEVERITY" \
189188
"$EXIT_ON_CRITICAL" # Only push if security scan passed
190-
- name: 📤 Push to Artifactory
191-
run: |
192-
echo "Pushing validated image to Artifactory..."
193-
docker push ${{ secrets.ARTIFACTORY_URL }}/${{ env.MOODLE_DEPLOYMENT_NAME }}:${{ github.ref_name }}
194189
195-
# Warm registry buildcache separately so a cache push failure never blocks the build
196-
- name: 🗄️ Warm Registry Build Cache
197-
continue-on-error: true
190+
- name: 📤 Push to Artifactory
198191
run: |
199-
echo "Pushing build cache to Artifactory registry (non-blocking)..."
200-
docker buildx build \
201-
--cache-from type=gha,scope=moodle-${{ github.ref_name }} \
202-
--cache-to type=registry,ref=${{ secrets.ARTIFACTORY_URL }}/${{ env.MOODLE_DEPLOYMENT_NAME }}:buildcache,mode=max \
203-
--build-arg DOCKER_FROM_IMAGE=${{ secrets.ARTIFACTORY_URL }}/${{ env.PHP_DEPLOYMENT_NAME }}:${{ github.ref_name }} \
204-
--output=type=cacheonly \
205-
-f ${{ env.MOODLE_DOCKER_FILE_PATH }} \
206-
. || echo "Warning: Registry build cache push failed — GHA cache is still available for next run"
192+
source openshift/scripts/utils/docker.sh
193+
push_to_artifactory "${{ secrets.ARTIFACTORY_URL }}/${{ env.MOODLE_DEPLOYMENT_NAME }}:${{ github.ref_name }}"

.github/workflows/php.yml

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -93,30 +93,17 @@ jobs:
9393
fi
9494
echo "======================================"
9595
96-
# Build PHP Image
97-
- name: 🛠️ Build PHP Image
96+
# Build and push PHP Image to Artifactory (with retry)
97+
- name: 🛠️ Build and Push PHP Image
9898
run: |
99+
source openshift/scripts/utils/docker.sh
99100
export BUILDKIT_PROGRESS=plain
100-
docker buildx build \
101-
--cache-from type=gha,scope=php \
102-
--cache-to type=gha,scope=php,mode=max \
103-
--build-arg DOCKER_FROM_IMAGE=${{ secrets.ARTIFACTORY_URL }}/${{ env.PHP_IMAGE }} \
104-
--tag ${{ secrets.ARTIFACTORY_URL }}/${{ env.PHP_DEPLOYMENT_NAME }}:${{ github.ref_name }} \
105-
--load \
106-
-f ${{ env.PHP_DOCKER_FILE_PATH }} .
107-
108-
# Push PHP Image to Artifactory (with retry for transient registry errors)
109-
- name: 📤 Push PHP Image to Artifactory
110-
run: |
111-
IMAGE="${{ secrets.ARTIFACTORY_URL }}/${{ env.PHP_DEPLOYMENT_NAME }}:${{ github.ref_name }}"
112-
for attempt in 1 2 3; do
113-
echo "🔄 Push attempt $attempt/3"
114-
if docker push "$IMAGE"; then
115-
echo "✅ Push succeeded on attempt $attempt"
116-
exit 0
117-
fi
118-
echo "⚠️ Push failed on attempt $attempt"
119-
[ "$attempt" -lt 3 ] && echo "⏳ Waiting 15s before retry..." && sleep 15
120-
done
121-
echo "❌ Push failed after 3 attempts"
122-
exit 1
101+
build_and_push "php:${{ github.ref_name }}" \
102+
docker buildx build \
103+
--cache-from type=gha,scope=php \
104+
--cache-to type=gha,scope=php,mode=max \
105+
--build-arg DOCKER_FROM_IMAGE=${{ secrets.ARTIFACTORY_URL }}/${{ env.PHP_IMAGE }} \
106+
--tag ${{ secrets.ARTIFACTORY_URL }}/${{ env.PHP_DEPLOYMENT_NAME }}:${{ github.ref_name }} \
107+
--output=type=image,push=true \
108+
--push \
109+
-f ${{ env.PHP_DOCKER_FILE_PATH }} .

.github/workflows/redis-proxy.yml

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -132,29 +132,16 @@ jobs:
132132
fi
133133
echo "=============================================="
134134
135-
# Build Redis Proxy Image
136-
- name: 🛠️ Build Redis Proxy Image
135+
# Build and push Redis Proxy Image to Artifactory (with retry)
136+
- name: 🛠️ Build and Push Redis Proxy Image
137137
run: |
138+
source openshift/scripts/utils/docker.sh
138139
export BUILDKIT_PROGRESS=plain
139-
docker buildx build \
140-
--build-arg GOLANG_FROM_IMAGE=${{ secrets.ARTIFACTORY_URL }}/${{ env.GOLANG_IMAGE }} \
141-
--build-arg UBUNTU_FROM_IMAGE=${{ secrets.ARTIFACTORY_URL }}/${{ env.UBUNTU_IMAGE }} \
142-
--tag ${{ secrets.ARTIFACTORY_URL }}/${{ env.REDIS_PROXY_NAME }}:${{ github.ref_name }} \
143-
--load \
144-
-f ./Redis.Proxy.Dockerfile .
145-
146-
# Push Redis Proxy Image to Artifactory (with retry for transient registry errors)
147-
- name: 📤 Push Redis Proxy Image to Artifactory
148-
run: |
149-
IMAGE="${{ secrets.ARTIFACTORY_URL }}/${{ env.REDIS_PROXY_NAME }}:${{ github.ref_name }}"
150-
for attempt in 1 2 3; do
151-
echo "🔄 Push attempt $attempt/3"
152-
if docker push "$IMAGE"; then
153-
echo "✅ Push succeeded on attempt $attempt"
154-
exit 0
155-
fi
156-
echo "⚠️ Push failed on attempt $attempt"
157-
[ "$attempt" -lt 3 ] && echo "⏳ Waiting 15s before retry..." && sleep 15
158-
done
159-
echo "❌ Push failed after 3 attempts"
160-
exit 1
140+
build_and_push "redis-proxy:${{ github.ref_name }}" \
141+
docker buildx build \
142+
--build-arg GOLANG_FROM_IMAGE=${{ secrets.ARTIFACTORY_URL }}/${{ env.GOLANG_IMAGE }} \
143+
--build-arg UBUNTU_FROM_IMAGE=${{ secrets.ARTIFACTORY_URL }}/${{ env.UBUNTU_IMAGE }} \
144+
--tag ${{ secrets.ARTIFACTORY_URL }}/${{ env.REDIS_PROXY_NAME }}:${{ github.ref_name }} \
145+
--output=type=image,push=true \
146+
--push \
147+
-f ./Redis.Proxy.Dockerfile .

openshift/scripts/utils/docker.sh

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#!/bin/bash
2+
# =============================================================================
3+
# Docker Build & Push Utilities
4+
# Standardized functions for building and pushing Docker images to Artifactory
5+
#
6+
# Usage:
7+
# source openshift/scripts/utils/docker.sh
8+
# push_to_artifactory "registry.example.com/my-image:tag"
9+
# build_and_push "docker buildx build ... --push" "registry.example.com/my-image:tag"
10+
#
11+
# See also:
12+
# - optimize-image-push.sh — base image mirroring (Docker Hub → Artifactory)
13+
# - docker-security.sh — vulnerability scanning before push
14+
# =============================================================================
15+
16+
_DOCKER_UTILS_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
17+
18+
if [[ -f "$_DOCKER_UTILS_SCRIPT_DIR/openshift.sh" ]]; then
19+
source "$_DOCKER_UTILS_SCRIPT_DIR/openshift.sh"
20+
else
21+
log_info() { echo "ℹ️ $*" >&2; }
22+
log_warn() { echo "⚠️ $*" >&2; }
23+
log_error() { echo "$*" >&2; }
24+
log_success() { echo "$*" >&2; }
25+
fi
26+
27+
# =============================================================================
28+
# push_to_artifactory — Push a local Docker image to Artifactory with retry
29+
#
30+
# For images built with --output=type=docker (loaded into Docker daemon).
31+
# Retries handle transient Artifactory errors like "upload must be restarted".
32+
#
33+
# Arguments:
34+
# $1 — Full image reference (e.g., registry.example.com/project/image:tag)
35+
# $2 — Max retry attempts (default: 3)
36+
# $3 — Backoff delay in seconds (default: 15)
37+
#
38+
# Returns:
39+
# 0 on success, 1 after all retries exhausted
40+
# =============================================================================
41+
push_to_artifactory() {
42+
local image="${1:?push_to_artifactory: image reference required}"
43+
local max_attempts="${2:-3}"
44+
local backoff="${3:-15}"
45+
46+
log_info "Pushing image to Artifactory: ${image}"
47+
48+
for attempt in $(seq 1 "$max_attempts"); do
49+
log_info "Push attempt ${attempt}/${max_attempts}"
50+
51+
if docker push "$image" 2>&1; then
52+
log_success "Push succeeded on attempt ${attempt}"
53+
return 0
54+
fi
55+
56+
log_warn "Push failed on attempt ${attempt}"
57+
if [[ "$attempt" -lt "$max_attempts" ]]; then
58+
log_info "Waiting ${backoff}s before retry..."
59+
sleep "$backoff"
60+
fi
61+
done
62+
63+
log_error "Push failed after ${max_attempts} attempts: ${image}"
64+
return 1
65+
}
66+
67+
# =============================================================================
68+
# build_and_push — Run a docker buildx build command with inline --push, with retry
69+
#
70+
# For builds that use --output=type=image,push=true (BuildKit pushes directly).
71+
# On transient push failure, retries the full build+push command.
72+
# BuildKit layer caching ensures rebuilds are near-instant.
73+
#
74+
# Arguments:
75+
# $1 — Image name (for logging only)
76+
# $2+ — The full docker buildx build command and arguments
77+
#
78+
# Returns:
79+
# 0 on success, 1 after all retries exhausted
80+
# =============================================================================
81+
build_and_push() {
82+
local image_name="${1:?build_and_push: image name required}"
83+
shift
84+
local max_attempts=3
85+
local backoff=15
86+
87+
log_info "Building and pushing: ${image_name}"
88+
89+
for attempt in $(seq 1 "$max_attempts"); do
90+
log_info "Build+push attempt ${attempt}/${max_attempts}"
91+
92+
if "$@" 2>&1; then
93+
log_success "Build+push succeeded on attempt ${attempt}"
94+
return 0
95+
fi
96+
97+
log_warn "Build+push failed on attempt ${attempt}"
98+
if [[ "$attempt" -lt "$max_attempts" ]]; then
99+
log_info "Waiting ${backoff}s before retry (cached layers make rebuild fast)..."
100+
sleep "$backoff"
101+
fi
102+
done
103+
104+
log_error "Build+push failed after ${max_attempts} attempts: ${image_name}"
105+
return 1
106+
}

0 commit comments

Comments
 (0)