@@ -46,14 +46,15 @@ inputs:
46
46
default : ${{ github.repository }}
47
47
tag :
48
48
description : Deprecated input; use tags instead
49
+ deprecationMessage : " Input 'tag' is deprecated and will be removed in a future release. Please use 'tags' instead."
49
50
token :
50
51
description : Specify token (GH or PAT), instead of inheriting one from the calling workflow
51
52
default : ${{ github.token }}
52
53
53
54
outputs :
54
55
digest :
55
56
description : ' Digest of the built image; e.g. sha256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'
56
- value : ${{ steps.digest .outputs.digest }}
57
+ value : ${{ steps.build_and_push .outputs.digest }}
57
58
58
59
triggered :
59
60
description : Did a deployment trigger? [true|false]
@@ -66,55 +67,46 @@ runs:
66
67
- name : GHCR Cleanup
67
68
if : inputs.keep_versions
68
69
continue-on-error : true # Stop fail if no versions to delete
69
- uses : actions/delete-package-versions@e5bc658cc4c965c472efe991f8beea3981499c55 # v5.0.0
70
+ uses : actions/delete-package-versions@v5.0.0
70
71
with :
71
72
package-name : " ${{ github.event.repository.name }}/${{ inputs.package }}"
72
73
package-type : " container"
73
74
min-versions-to-keep : ${{ inputs.keep_versions }}
74
75
ignore-versions : " ${{ inputs.keep_regex }}"
75
76
76
- # Process variables and inputs
77
77
- id : vars
78
78
shell : bash
79
+ env :
80
+ INPUT_BUILD_CONTEXT : ${{ inputs.build_context }}
81
+ INPUT_BUILD_FILE : ${{ inputs.build_file }}
82
+ INPUT_PACKAGE : ${{ inputs.package }}
79
83
run : |
80
- # Inputs and variables
84
+ # Process inputs and set variables
85
+ set -euo pipefail
81
86
82
- # If inputs.tag is provided say it has been deprecated and exit 1
87
+ # Early exit for deprecated input
83
88
if [ -n "${{ inputs.tag }}" ]; then
84
- echo "Input 'tag' is deprecated. Please use 'tags' instead."
89
+ echo "Input 'tag' is deprecated. Please use 'tags' instead."
85
90
exit 1
86
91
fi
87
92
88
- # Use package folder as build_context unless an override has been provided
89
- if [ -z ${{ inputs.build_context }} ]; then
90
- BUILD_CONTEXT=${{ inputs.package }}
91
- else
92
- BUILD_CONTEXT=${{ inputs.build_context }}
93
- fi
94
- echo "build_context=${BUILD_CONTEXT}" >> $GITHUB_OUTPUT
93
+ # Set build context and file with defaults
94
+ build_context="${INPUT_BUILD_CONTEXT:-$INPUT_PACKAGE}"
95
+ build_file="${INPUT_BUILD_FILE:-${build_context}/Dockerfile}"
95
96
96
- # Use BUILD_CONTEXT/Dockerfile as build_file unless an override has been provided
97
- if [ -z ${{ inputs.build_file }} ]; then
98
- BUILD_FILE=${BUILD_CONTEXT}/Dockerfile
99
- else
100
- BUILD_FILE=${{ inputs.build_file }}
101
- fi
102
- echo "build_file=${BUILD_FILE}" >> $GITHUB_OUTPUT
103
-
104
- # Bug - Docker build hates images with capital letters
105
- # Convert multiline tags input to a comma-separated list of full image paths (lowercase, no spaces)
106
- TAGS=""
107
- while IFS= read -r tag || [ -n "$tag" ]; do
108
- tag_lowercase=$(echo "$tag" | tr '[:upper:]' '[:lower:]' | tr -d ' ')
109
- [ -z "$tag_lowercase" ] && continue
110
- full_tag="ghcr.io/${{ github.repository }}/${{ inputs.package }}:$tag_lowercase"
111
- if [ -z "$TAGS" ]; then
112
- TAGS="$full_tag"
113
- else
114
- TAGS="$TAGS,$full_tag"
97
+ # Process tags into comma-separated list of full image paths (concise version)
98
+ mapfile -t tags < <(printf '%s\n' "${{ inputs.tags }}" | tr '[:upper:]' '[:lower:]' | tr -d ' ' | grep -v '^$')
99
+ tags=("${tags[@]/#/ghcr.io/${{ github.repository }}/${INPUT_PACKAGE}:}")
100
+ tags_csv=$(IFS=, ; echo "${tags[*]}")
101
+
102
+ # Output all variables at once, and verify they are set
103
+ for v in build_context build_file tags_csv; do
104
+ if [ -z "${!v}" ]; then
105
+ echo "Error: $v is not set!" >&2
106
+ exit 1
115
107
fi
116
- done <<< "${{ inputs.tags }} "
117
- echo "tags=$TAGS" >> $GITHUB_OUTPUT
108
+ echo "$v=${!v}" >> "$GITHUB_OUTPUT "
109
+ done
118
110
119
111
# Send triggers to diff action
120
112
- id : diff
@@ -131,19 +123,20 @@ runs:
131
123
shell : bash
132
124
run : |
133
125
# Check for builds
134
- echo " triggered=true" >> $GITHUB_OUTPUT
126
+ triggered=true
135
127
if [ "${{ steps.diff.outputs.triggered }}" == "true" ]; then
136
128
echo "Build triggered. Used bcgov/action-diff-triggers."
137
129
elif [ "${{ inputs.repository }}" != "${{ github.repository }}" ]; then
138
130
echo "Build triggered. Override repository provided."
139
131
elif [ -z "${{ inputs.tag_fallback }}" ]; then
140
132
echo "Build triggered. No tag_fallback provided."
141
- elif [[ ! $(docker manifest inspect ${URL_FALLBACK}) ]]; then
133
+ elif [[ ! $(docker manifest inspect " ${URL_FALLBACK}" ) ]]; then
142
134
echo "Build triggered. Fallback tag (tag_fallback) not usable."
143
135
else
144
136
echo "Container build not required"
145
- echo " triggered=false" >> $GITHUB_OUTPUT
137
+ triggered=false
146
138
fi
139
+ echo "triggered=$triggered" >> $GITHUB_OUTPUT
147
140
148
141
# If a build is not required, reuse a previous image
149
142
- name : Recycle/retag Previous Images
@@ -156,25 +149,37 @@ runs:
156
149
tags : |
157
150
${{ inputs.tags }}
158
151
159
- # If a build is required, then checkout, login, build and push!
160
- - uses : actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
152
+ - uses : actions/checkout@v4
161
153
with :
162
154
repository : ${{ inputs.repository }}
163
155
164
156
- name : Add build envars to Dockerfile
165
157
if : steps.build.outputs.triggered == 'true'
166
158
env :
167
- dockerfile : ${{ steps.vars.outputs.build_file }}
159
+ DOCKERFILE : ${{ steps.vars.outputs.build_file }}
168
160
shell : bash
169
161
run : |
170
- # Add build envars to Dockerfile (pad for missing line endings!)
171
- echo "" >> ${{ env.dockerfile }}
172
- echo "ENV BUILDER_IMAGE=ghcr.io/${{ github.repository }}/${{ inputs.package }}" >> ${{ env.dockerfile }}
173
- echo "ENV BUILDER_PACKAGE=${{ inputs.package }}" >> ${{ env.dockerfile }}
174
- echo "ENV BUILDER_REPO=${{ github.repository }}" >> ${{ env.dockerfile }}
175
- # Convert multiline tags to a single comma-separated string for BUILDER_TAGS
176
- TAGS_COMMA=$(echo "${{ inputs.tags }}" | tr '\n' ',' | tr -s ',' | sed 's/,$//')
177
- echo "ENV BUILDER_TAGS=\"$TAGS_COMMA\"" >> ${{ env.dockerfile }}
162
+ # Add build envars to DOCKERFILE
163
+ set -euo pipefail
164
+
165
+ # Diagnostics and validation for DOCKERFILE path
166
+ echo "[DIAG] Checking DOCKERFILE path: $DOCKERFILE"
167
+ if [ ! -e "$DOCKERFILE" ]; then
168
+ echo "[DIAG] DOCKERFILE does not exist at $DOCKERFILE" >&2
169
+ ls -l "$(dirname \"$DOCKERFILE\")" || true
170
+ exit 1
171
+ elif [ ! -w "$DOCKERFILE" ]; then
172
+ echo "[DIAG] DOCKERFILE is not writable at $DOCKERFILE" >&2
173
+ ls -l "$DOCKERFILE" || true
174
+ exit 1
175
+ else
176
+ # Add build envars to DOCKERFILE (pad for missing line endings)
177
+ {
178
+ echo ""
179
+ echo "ENV BUILDER_IMAGE=ghcr.io/${{ github.repository }}/${{ inputs.package }}"
180
+ echo "ENV BUILDER_TAGS=${{ steps.vars.outputs.tags_csv }}"
181
+ } >> "$DOCKERFILE"
182
+ fi
178
183
179
184
- name : Set up Docker Buildx
180
185
if : steps.build.outputs.triggered == 'true'
@@ -189,34 +194,42 @@ runs:
189
194
password : ${{ inputs.token }}
190
195
191
196
- name : Build and push ${{ inputs.package }} Docker image
197
+ id : build_and_push
192
198
if : steps.build.outputs.triggered == 'true'
193
199
uses : docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6
194
200
with :
195
201
context : ${{ steps.vars.outputs.build_context }}
196
202
file : ${{ steps.vars.outputs.build_file }}
197
203
push : true
198
- tags : ${{ steps.vars.outputs.tags }}
204
+ tags : ${{ steps.vars.outputs.tags_csv }}
199
205
cache-from : type=gha
200
206
cache-to : type=gha,mode=max
201
207
build-args : ${{ inputs.build_args }}
202
208
203
209
# Action repo needs to be present for cleanup/tests
204
210
- name : Checkout local repo to make sure action.yml is present
205
211
if : github.repository != inputs.repository
206
- uses : actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
212
+ uses : actions/checkout@v4
207
213
208
- # Get the digest of the built image
209
- - name : Return digest of the built image
214
+ # Get the digest of the built image from the build-push-action output
215
+ - name : Validate digest
216
+ env :
217
+ IMAGE : ghcr.io/${{ github.repository }}/${{ inputs.package }}@${{ steps.build_and_push.outputs.digest }}
210
218
id : digest
219
+ if : steps.build.outputs.triggered == 'true'
211
220
shell : bash
212
221
run : |
213
- # Get the digest of the first image in the comma-separated tags list
214
- FIRST_TAG=$(echo "${{ steps.vars.outputs.tags }}" | cut -d, -f1)
215
- DIGEST=$(docker manifest inspect "$FIRST_TAG" | jq -r '.manifests[0].digest')
216
- echo "digest=${DIGEST}" >> $GITHUB_OUTPUT
222
+ # Validate digest using docker manifest inspect (faster than docker pull)
223
+ if ! docker manifest inspect "$IMAGE" > /dev/null; then
224
+ echo "Error: Failed to inspect manifest for $IMAGE" >&2
225
+ exit 1
226
+ fi
217
227
218
- - shell : bash
228
+ - name : Print summary outputs
229
+ shell : bash
219
230
run : |
220
- # Summary
221
- echo "digest: ${{ steps.digest .outputs.digest }}"
231
+ echo "---- Build Summary ----"
232
+ echo "digest: ${{ steps.build_and_push .outputs.digest }}"
222
233
echo "triggered: ${{ steps.diff.outputs.triggered }}"
234
+ echo "tags_csv: ${{ steps.vars.outputs.tags_csv }}"
235
+ echo "-----------------------"
0 commit comments