Skip to content

Commit d63c76a

Browse files
chore: optimizations (#78)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 27c988f commit d63c76a

File tree

3 files changed

+93
-74
lines changed

3 files changed

+93
-74
lines changed

.github/workflows/pr-open.yml

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,13 @@ jobs:
1515
name: Build
1616
runs-on: ubuntu-24.04
1717
steps:
18-
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
18+
- uses: actions/checkout@v4
1919
- id: build
2020
uses: ./
2121
with:
2222
package: backend
2323
keep_versions: 10
2424
repository: bcgov/quickstart-openshift
25-
tags: |
26-
${{ github.event.number }}
27-
${{ github.event.number }}-multiline
28-
pr-latest
2925

3026
- if: steps.build.outputs.triggered != 'false'
3127
run: |
@@ -44,7 +40,7 @@ jobs:
4440
name: Retag
4541
runs-on: ubuntu-24.04
4642
steps:
47-
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
43+
- uses: actions/checkout@v4
4844
- id: retag
4945
uses: ./
5046
with:
@@ -71,7 +67,7 @@ jobs:
7167
name: Advanced
7268
runs-on: ubuntu-24.04
7369
steps:
74-
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
70+
- uses: actions/checkout@v4
7571
- id: advanced
7672
uses: ./
7773
with:
@@ -80,7 +76,10 @@ jobs:
8076
build_file: api/Dockerfile
8177
keep_versions: 10
8278
repository: bcgov/nr-fom
83-
tags: ${{ github.event.number }}
79+
tags:
80+
${{ github.event.number }}
81+
${{ github.event.number }}-multiline
82+
pr-latest
8483

8584
- if: steps.advanced.outputs.triggered != 'false'
8685
run: |

README.md

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ Only GitHub Container Registry (ghcr.io) is supported so far.
4646

4747
# Tags to apply to the image
4848
# Optional, defaults to pull request number
49+
# Note: All tags are normalized to lowercase and stripped of spaces before use.
4950
tags: |
5051
pr123
5152
demo
@@ -87,7 +88,7 @@ Only GitHub Container Registry (ghcr.io) is supported so far.
8788

8889
Build a single subfolder with a Dockerfile in it. Deletes old packages, keeping the last 50. Runs on pull requests (PRs).
8990

90-
Create or modify a GitHub workflow, like below. E.g. `./github/workflows/pr-open.yml`
91+
Create or modify a GitHub workflow, like below. E.g. `.github/workflows/pr-open.yml`
9192

9293
```yaml
9394
name: Pull Request
@@ -105,7 +106,7 @@ jobs:
105106
packages: write
106107
runs-on: ubuntu-24.04
107108
steps:
108-
- uses: actions/checkout@v3
109+
- uses: actions/checkout@v4
109110
- name: Builds
110111
uses: bcgov/action-builder-ghcr@vX.Y.Z
111112
with:
@@ -118,7 +119,7 @@ jobs:
118119
119120
Same as previous, but specifying build folder and Dockerfile.
120121
121-
Create or modify a GitHub workflow, like below. E.g. `./github/workflows/pr-open.yml`
122+
Create or modify a GitHub workflow, like below. E.g. `.github/workflows/pr-open.yml`
122123

123124
```yaml
124125
name: Pull Request
@@ -136,7 +137,7 @@ jobs:
136137
packages: write
137138
runs-on: ubuntu-24.04
138139
steps:
139-
- uses: actions/checkout@v3
140+
- uses: actions/checkout@v4
140141
- name: Builds
141142
uses: bcgov/action-builder-ghcr@vX.Y.Z
142143
with:
@@ -156,7 +157,7 @@ jobs:
156157

157158
Build from multiple subfolders with Dockerfile in them. This time an outside repository is used. Runs on pull requests (PRs).
158159

159-
Create or modify a GitHub workflow, like below. E.g. `./github/workflows/pr-open.yml`
160+
Create or modify a GitHub workflow, like below. E.g. `.github/workflows/pr-open.yml`
160161

161162
```yaml
162163
name: Pull Request
@@ -182,7 +183,7 @@ jobs:
182183
- package: frontend
183184
triggers: ('frontend/')
184185
steps:
185-
- uses: actions/checkout@v3
186+
- uses: actions/checkout@v4
186187
- name: Test Builds
187188
uses: bcgov/action-builder-ghcr@vX.Y.Z
188189
with:
@@ -197,6 +198,11 @@ jobs:
197198

198199
# Outputs
199200

201+
| Output | Description |
202+
|------------|---------------------------------------------|
203+
| `digest` | Digest of the built or retagged image |
204+
| `triggered`| Whether a build was triggered (`true/false`)|
205+
200206
New image digest (SHA). This applies to build and retags.
201207

202208
```yaml
@@ -234,7 +240,8 @@ permissions:
234240

235241
# Deprecations
236242

237-
- The `tag` input has been deprecated in favor of `tags`, a multiline string that can handle multiple values.
243+
> ⚠️ **Deprecated:** The `tag` input has been deprecated in favor of `tags`, a multiline string that can handle multiple values. The `tag` input will be removed in a future release.
244+
238245
- The `digest_old` output has been deprecated due to non-use.
239246
- The `digest_new` output has been renamed to `digest`.
240247

action.yml

Lines changed: 72 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,15 @@ inputs:
4646
default: ${{ github.repository }}
4747
tag:
4848
description: Deprecated input; use tags instead
49+
deprecationMessage: "Input 'tag' is deprecated and will be removed in a future release. Please use 'tags' instead."
4950
token:
5051
description: Specify token (GH or PAT), instead of inheriting one from the calling workflow
5152
default: ${{ github.token }}
5253

5354
outputs:
5455
digest:
5556
description: 'Digest of the built image; e.g. sha256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'
56-
value: ${{ steps.digest.outputs.digest }}
57+
value: ${{ steps.build_and_push.outputs.digest }}
5758

5859
triggered:
5960
description: Did a deployment trigger? [true|false]
@@ -66,55 +67,46 @@ runs:
6667
- name: GHCR Cleanup
6768
if: inputs.keep_versions
6869
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
7071
with:
7172
package-name: "${{ github.event.repository.name }}/${{ inputs.package }}"
7273
package-type: "container"
7374
min-versions-to-keep: ${{ inputs.keep_versions }}
7475
ignore-versions: "${{ inputs.keep_regex }}"
7576

76-
# Process variables and inputs
7777
- id: vars
7878
shell: bash
79+
env:
80+
INPUT_BUILD_CONTEXT: ${{ inputs.build_context }}
81+
INPUT_BUILD_FILE: ${{ inputs.build_file }}
82+
INPUT_PACKAGE: ${{ inputs.package }}
7983
run: |
80-
# Inputs and variables
84+
# Process inputs and set variables
85+
set -euo pipefail
8186
82-
# If inputs.tag is provided say it has been deprecated and exit 1
87+
# Early exit for deprecated input
8388
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."
8590
exit 1
8691
fi
8792
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}"
9596
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
115107
fi
116-
done <<< "${{ inputs.tags }}"
117-
echo "tags=$TAGS" >> $GITHUB_OUTPUT
108+
echo "$v=${!v}" >> "$GITHUB_OUTPUT"
109+
done
118110
119111
# Send triggers to diff action
120112
- id: diff
@@ -131,19 +123,20 @@ runs:
131123
shell: bash
132124
run: |
133125
# Check for builds
134-
echo "triggered=true" >> $GITHUB_OUTPUT
126+
triggered=true
135127
if [ "${{ steps.diff.outputs.triggered }}" == "true" ]; then
136128
echo "Build triggered. Used bcgov/action-diff-triggers."
137129
elif [ "${{ inputs.repository }}" != "${{ github.repository }}" ]; then
138130
echo "Build triggered. Override repository provided."
139131
elif [ -z "${{ inputs.tag_fallback }}" ]; then
140132
echo "Build triggered. No tag_fallback provided."
141-
elif [[ ! $(docker manifest inspect ${URL_FALLBACK}) ]]; then
133+
elif [[ ! $(docker manifest inspect "${URL_FALLBACK}") ]]; then
142134
echo "Build triggered. Fallback tag (tag_fallback) not usable."
143135
else
144136
echo "Container build not required"
145-
echo "triggered=false" >> $GITHUB_OUTPUT
137+
triggered=false
146138
fi
139+
echo "triggered=$triggered" >> $GITHUB_OUTPUT
147140
148141
# If a build is not required, reuse a previous image
149142
- name: Recycle/retag Previous Images
@@ -156,25 +149,37 @@ runs:
156149
tags: |
157150
${{ inputs.tags }}
158151
159-
# If a build is required, then checkout, login, build and push!
160-
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
152+
- uses: actions/checkout@v4
161153
with:
162154
repository: ${{ inputs.repository }}
163155

164156
- name: Add build envars to Dockerfile
165157
if: steps.build.outputs.triggered == 'true'
166158
env:
167-
dockerfile: ${{ steps.vars.outputs.build_file }}
159+
DOCKERFILE: ${{ steps.vars.outputs.build_file }}
168160
shell: bash
169161
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
178183
179184
- name: Set up Docker Buildx
180185
if: steps.build.outputs.triggered == 'true'
@@ -189,34 +194,42 @@ runs:
189194
password: ${{ inputs.token }}
190195

191196
- name: Build and push ${{ inputs.package }} Docker image
197+
id: build_and_push
192198
if: steps.build.outputs.triggered == 'true'
193199
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6
194200
with:
195201
context: ${{ steps.vars.outputs.build_context }}
196202
file: ${{ steps.vars.outputs.build_file }}
197203
push: true
198-
tags: ${{ steps.vars.outputs.tags }}
204+
tags: ${{ steps.vars.outputs.tags_csv }}
199205
cache-from: type=gha
200206
cache-to: type=gha,mode=max
201207
build-args: ${{ inputs.build_args }}
202208

203209
# Action repo needs to be present for cleanup/tests
204210
- name: Checkout local repo to make sure action.yml is present
205211
if: github.repository != inputs.repository
206-
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
212+
uses: actions/checkout@v4
207213

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 }}
210218
id: digest
219+
if: steps.build.outputs.triggered == 'true'
211220
shell: bash
212221
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
217227
218-
- shell: bash
228+
- name: Print summary outputs
229+
shell: bash
219230
run: |
220-
# Summary
221-
echo "digest: ${{ steps.digest.outputs.digest }}"
231+
echo "---- Build Summary ----"
232+
echo "digest: ${{ steps.build_and_push.outputs.digest }}"
222233
echo "triggered: ${{ steps.diff.outputs.triggered }}"
234+
echo "tags_csv: ${{ steps.vars.outputs.tags_csv }}"
235+
echo "-----------------------"

0 commit comments

Comments
 (0)