Skip to content

Commit cd62a01

Browse files
pdabelf5vepatel
andauthored
Add build-push-retry action to Docker build workflows (#10491)
Add build-push-retry action to Docker build workflows (#10486) * Add build-push-retry action to Docker build workflows * Update action.yaml to clarify max-attempts input and handle error messages for build/push attempts * Enhance retry logic in build-push-retry action to use environment variables for wait times and validate max-attempts input --------- Co-authored-by: Venktesh Patel <ve.patel@f5.com>
1 parent b5f5b42 commit cd62a01

9 files changed

Lines changed: 192 additions & 15 deletions

File tree

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
name: Build and Push with Retry
2+
description: Wraps docker/build-push-action with retry logic for transient push/auth failures
3+
4+
inputs:
5+
file:
6+
description: Path to the Dockerfile
7+
required: true
8+
context:
9+
description: Build context
10+
default: "."
11+
tags:
12+
description: Image tags (newline-separated)
13+
required: true
14+
labels:
15+
description: Image labels
16+
required: false
17+
annotations:
18+
description: Image annotations
19+
required: false
20+
platforms:
21+
description: Target platforms (e.g. linux/amd64,linux/arm64)
22+
required: false
23+
target:
24+
description: Dockerfile build target
25+
required: false
26+
build-args:
27+
description: Build arguments (newline-separated)
28+
required: false
29+
secret-files:
30+
description: Secret files (newline-separated key=path pairs)
31+
required: false
32+
cache-from:
33+
description: Cache sources
34+
required: false
35+
cache-to:
36+
description: Cache destinations
37+
required: false
38+
push:
39+
description: Push image to registry
40+
default: "true"
41+
load:
42+
description: Load image into local Docker daemon
43+
default: "false"
44+
pull:
45+
description: Always pull base images
46+
default: "true"
47+
no-cache:
48+
description: Disable build cache
49+
default: "false"
50+
sbom:
51+
description: Generate SBOM attestation
52+
required: false
53+
provenance:
54+
description: Generate provenance attestation
55+
required: false
56+
max-attempts:
57+
description: Maximum number of push attempts (1-3)
58+
default: "3"
59+
retry-wait-seconds:
60+
description: Seconds to wait between retries
61+
default: "15"
62+
63+
outputs:
64+
digest:
65+
description: Image digest from successful push
66+
value: ${{ steps.attempt1.outputs.digest || steps.attempt2.outputs.digest || steps.attempt3.outputs.digest }}
67+
metadata:
68+
description: Build result metadata
69+
value: ${{ steps.attempt1.outputs.metadata || steps.attempt2.outputs.metadata || steps.attempt3.outputs.metadata }}
70+
imageid:
71+
description: Image ID
72+
value: ${{ steps.attempt1.outputs.imageid || steps.attempt2.outputs.imageid || steps.attempt3.outputs.imageid }}
73+
74+
runs:
75+
using: composite
76+
steps:
77+
- name: Build and Push (attempt 1)
78+
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
79+
id: attempt1
80+
continue-on-error: true
81+
with:
82+
file: ${{ inputs.file }}
83+
context: ${{ inputs.context }}
84+
tags: ${{ inputs.tags }}
85+
labels: ${{ inputs.labels }}
86+
annotations: ${{ inputs.annotations }}
87+
platforms: ${{ inputs.platforms }}
88+
target: ${{ inputs.target }}
89+
build-args: ${{ inputs.build-args }}
90+
secret-files: ${{ inputs.secret-files }}
91+
cache-from: ${{ inputs.cache-from }}
92+
cache-to: ${{ inputs.cache-to }}
93+
push: ${{ inputs.push }}
94+
load: ${{ inputs.load }}
95+
pull: ${{ inputs.pull }}
96+
no-cache: ${{ inputs.no-cache }}
97+
sbom: ${{ inputs.sbom }}
98+
provenance: ${{ inputs.provenance }}
99+
100+
- name: Wait before retry 1
101+
if: steps.attempt1.outcome == 'failure' && inputs.max-attempts >= 2
102+
shell: bash
103+
env:
104+
RETRY_WAIT: ${{ inputs.retry-wait-seconds }}
105+
run: |
106+
echo "::warning::Build/push attempt 1 failed. Retrying in ${RETRY_WAIT}s..."
107+
sleep "${RETRY_WAIT}"
108+
109+
- name: Build and Push (attempt 2)
110+
if: steps.attempt1.outcome == 'failure' && inputs.max-attempts >= 2
111+
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
112+
id: attempt2
113+
continue-on-error: true
114+
with:
115+
file: ${{ inputs.file }}
116+
context: ${{ inputs.context }}
117+
tags: ${{ inputs.tags }}
118+
labels: ${{ inputs.labels }}
119+
annotations: ${{ inputs.annotations }}
120+
platforms: ${{ inputs.platforms }}
121+
target: ${{ inputs.target }}
122+
build-args: ${{ inputs.build-args }}
123+
secret-files: ${{ inputs.secret-files }}
124+
cache-from: ${{ inputs.cache-from }}
125+
push: ${{ inputs.push }}
126+
load: ${{ inputs.load }}
127+
pull: ${{ inputs.pull }}
128+
no-cache: ${{ inputs.no-cache }}
129+
sbom: ${{ inputs.sbom }}
130+
provenance: ${{ inputs.provenance }}
131+
132+
- name: Wait before retry 2
133+
if: steps.attempt1.outcome == 'failure' && steps.attempt2.outcome == 'failure' && inputs.max-attempts >= 3
134+
shell: bash
135+
env:
136+
RETRY_WAIT: ${{ inputs.retry-wait-seconds }}
137+
run: |
138+
echo "::warning::Build/push attempt 2 failed. Retrying in ${RETRY_WAIT}s..."
139+
sleep "${RETRY_WAIT}"
140+
141+
- name: Build and Push (attempt 3)
142+
if: steps.attempt1.outcome == 'failure' && steps.attempt2.outcome == 'failure' && inputs.max-attempts >= 3
143+
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
144+
id: attempt3
145+
continue-on-error: true
146+
with:
147+
file: ${{ inputs.file }}
148+
context: ${{ inputs.context }}
149+
tags: ${{ inputs.tags }}
150+
labels: ${{ inputs.labels }}
151+
annotations: ${{ inputs.annotations }}
152+
platforms: ${{ inputs.platforms }}
153+
target: ${{ inputs.target }}
154+
build-args: ${{ inputs.build-args }}
155+
secret-files: ${{ inputs.secret-files }}
156+
cache-from: ${{ inputs.cache-from }}
157+
push: ${{ inputs.push }}
158+
load: ${{ inputs.load }}
159+
pull: ${{ inputs.pull }}
160+
no-cache: ${{ inputs.no-cache }}
161+
sbom: ${{ inputs.sbom }}
162+
provenance: ${{ inputs.provenance }}
163+
164+
- name: Fail if all attempts failed
165+
if: steps.attempt1.outcome == 'failure' && (inputs.max-attempts < 2 || steps.attempt2.outcome == 'failure') && (inputs.max-attempts < 3 || steps.attempt3.outcome == 'failure')
166+
shell: bash
167+
env:
168+
MAX_ATTEMPTS: ${{ inputs.max-attempts }}
169+
run: |
170+
if ! [[ "${MAX_ATTEMPTS}" =~ ^[0-9]+$ ]]; then
171+
echo "::error::max-attempts must be a positive integer, got: ${MAX_ATTEMPTS}"
172+
exit 1
173+
fi
174+
attempts="${MAX_ATTEMPTS}"
175+
if [ "${attempts}" -gt 3 ]; then attempts=3; fi
176+
echo "::error::All ${attempts} build/push attempts failed"
177+
exit 1

.github/workflows/build-base-images.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ jobs:
112112
type=raw,value=${{ needs.checks.outputs.docker_md5 }},enable=${{ needs.checks.outputs.docker_md5 != '' }}
113113
114114
- name: Build Base Container
115-
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
115+
uses: ./.github/actions/build-push-retry
116116
with:
117117
file: build/Dockerfile
118118
context: "."
@@ -208,7 +208,7 @@ jobs:
208208
type=raw,value=${{ needs.checks.outputs.docker_md5 }},enable=${{ needs.checks.outputs.docker_md5 != '' }}
209209
210210
- name: Build Base Container
211-
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
211+
uses: ./.github/actions/build-push-retry
212212
with:
213213
file: build/Dockerfile
214214
context: "."
@@ -319,7 +319,7 @@ jobs:
319319
type=raw,value=${{ needs.checks.outputs.docker_md5 }},enable=${{ needs.checks.outputs.docker_md5 != '' }}
320320
321321
- name: Build Base Container
322-
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
322+
uses: ./.github/actions/build-push-retry
323323
with:
324324
file: build/Dockerfile
325325
context: "."

.github/workflows/build-oss.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ jobs:
183183
if: ${{ steps.images_exist.outputs.base_exists != 'true' || steps.images_exist.outputs.target_exists != 'true' }}
184184

185185
- name: Build Base Container
186-
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
186+
uses: ./.github/actions/build-push-retry
187187
with:
188188
file: build/Dockerfile
189189
context: "."
@@ -215,7 +215,7 @@ jobs:
215215
if: ${{ steps.images_exist.outputs.base_exists != 'true' || steps.images_exist.outputs.target_exists != 'true' }}
216216

217217
- name: Build Docker image
218-
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
218+
uses: ./.github/actions/build-push-retry
219219
id: build-push
220220
with:
221221
file: build/Dockerfile

.github/workflows/build-plus.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ jobs:
206206
if: ${{ steps.images_exist.outputs.base_exists != 'true' || steps.images_exist.outputs.target_exists != 'true' }}
207207

208208
- name: Build Base Container
209-
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
209+
uses: ./.github/actions/build-push-retry
210210
with:
211211
file: build/Dockerfile
212212
context: "."
@@ -243,7 +243,7 @@ jobs:
243243
if: ${{ steps.images_exist.outputs.base_exists != 'true' || steps.images_exist.outputs.target_exists != 'true' }}
244244

245245
- name: Build Docker image
246-
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
246+
uses: ./.github/actions/build-push-retry
247247
id: build-push
248248
with:
249249
file: build/Dockerfile

.github/workflows/build-test-image.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ jobs:
6868
password: ${{ steps.auth.outputs.access_token }}
6969

7070
- name: Build Test-Runner Container
71-
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
71+
uses: ./.github/actions/build-push-retry
7272
with:
7373
file: tests/Dockerfile
7474
context: "."

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,7 @@ jobs:
617617
if: ${{ needs.checks.outputs.forked_workflow == 'true' }}
618618

619619
- name: Build Docker Image ${{ matrix.base-os }}
620-
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
620+
uses: ./.github/actions/build-push-retry
621621
with:
622622
file: build/Dockerfile
623623
context: "."
@@ -772,7 +772,7 @@ jobs:
772772
if: ${{ needs.checks.outputs.forked_workflow == 'false' }}
773773

774774
- name: Build Test-Runner Container
775-
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
775+
uses: ./.github/actions/build-push-retry
776776
with:
777777
file: tests/Dockerfile
778778
context: "."

.github/workflows/patch-image.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ jobs:
8989
password: ${{ steps.auth.outputs.access_token }}
9090

9191
- name: Apply OS patches to Container
92-
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
92+
uses: ./.github/actions/build-push-retry
9393
with:
9494
file: build/Dockerfile
9595
context: "."

.github/workflows/setup-smoke.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ permissions:
4747
jobs:
4848
setup-smoke:
4949
permissions:
50-
contents: read # for docker/build-push-action to read repo content
50+
contents: read # for ./.github/actions/build-push-retry
5151
id-token: write # for OIDC login to GCR
5252
if: github.repository == 'nginx/kubernetes-ingress'
5353
runs-on: ubuntu-24.04
@@ -163,7 +163,7 @@ jobs:
163163
if: ${{ inputs.authenticated }}
164164

165165
- name: Build Pytest-Runner Container
166-
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
166+
uses: ./.github/actions/build-push-retry
167167
with:
168168
file: tests/Dockerfile
169169
context: "."
@@ -175,7 +175,7 @@ jobs:
175175
if: ${{ ( !inputs.authenticated || steps.check-image.outcome == 'failure' ) }}
176176

177177
- name: Build ${{ inputs.image }} Container
178-
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
178+
uses: ./.github/actions/build-push-retry
179179
with:
180180
file: build/Dockerfile
181181
context: "."

.github/workflows/single-image-regression.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ jobs:
124124
continue-on-error: true
125125

126126
- name: Build Test-Runner Container
127-
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
127+
uses: ./.github/actions/build-push-retry
128128
with:
129129
file: tests/Dockerfile
130130
context: "."

0 commit comments

Comments
 (0)