Skip to content

Commit 158888e

Browse files
ci: add reusable workflow to build dynamic plugins container images
Introduce build-container-image.yml that uses rhdh-cli to package and push workspace images to quay.io. Wire it into CI (per-PR images with 2w expiry) and into the release workflow (next + latest tags). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
1 parent f2a4ef9 commit 158888e

3 files changed

Lines changed: 168 additions & 1 deletion

File tree

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
name: Build container image
2+
3+
# Reusable workflow that builds a Red Hat Developer Hub dynamic plugins
4+
# container image for a single workspace using the rhdh-cli.
5+
# Adds a quay expiration label and pushes the image to the container registry.
6+
7+
on:
8+
workflow_call:
9+
inputs:
10+
workspace:
11+
description: Name of the workspace to build an image for
12+
required: true
13+
type: string
14+
tags:
15+
description: >-
16+
Space separated list of image tags (the part after the colon).
17+
The literal `${SHORT_SHA}` placeholder is replaced with the
18+
short commit sha of the checked out ref. The first tag is used to
19+
build the image, additional tags are added afterwards.
20+
All tags should be prefixed with the workspace name.
21+
required: true
22+
type: string
23+
registry:
24+
description: Image repository the tags are pushed to (without the tag part)
25+
required: false
26+
type: string
27+
default: quay.io/rhdh/rhdh-plugins
28+
expires_after:
29+
description: >-
30+
Adds a `quay.expires-after` label with this value (for example `2w`)
31+
so the image is removed automatically.
32+
required: true
33+
type: string
34+
default: ''
35+
ref:
36+
description: Git ref (branch, tag or sha) to checkout and build from
37+
required: false
38+
type: string
39+
default: ''
40+
secrets:
41+
QUAY_USERNAME:
42+
description: Username used to authenticate against quay.io when pushing
43+
required: false
44+
QUAY_PASSWORD:
45+
description: Password or token used to authenticate against quay.io when pushing
46+
required: false
47+
48+
jobs:
49+
build-container-image:
50+
name: Build image for ${{ inputs.workspace }}
51+
if: inputs.workspace != 'noop'
52+
runs-on: ubuntu-latest
53+
env:
54+
CI: true
55+
NODE_OPTIONS: --max-old-space-size=8192
56+
steps:
57+
- name: Checkout
58+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
59+
with:
60+
ref: ${{ inputs.ref }}
61+
62+
- name: Set up Node
63+
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
64+
with:
65+
node-version: 24
66+
registry-url: https://registry.npmjs.org/ # Needed for auth
67+
68+
- name: yarn install
69+
working-directory: ./workspaces/${{ inputs.workspace }}
70+
run: yarn install --immutable
71+
72+
- name: Build dynamic plugins image
73+
working-directory: ./workspaces/${{ inputs.workspace }}
74+
env:
75+
REGISTRY: ${{ inputs.registry }}
76+
TAGS: ${{ inputs.tags }}
77+
EXPIRES_AFTER: ${{ inputs.expires_after }}
78+
run: |
79+
set -euo pipefail
80+
81+
# Resolve the ${SHORT_SHA} placeholder in the requested tags.
82+
SHORT_SHA="$(git rev-parse --short=7 HEAD)"
83+
TAGS_ONE_LINE="$(printf '%s' "${TAGS}" | tr '\n' ' ')"
84+
RESOLVED_TAGS="${TAGS_ONE_LINE//\$\{SHORT_SHA\}/${SHORT_SHA}}"
85+
read -ra TAG_LIST <<< "${RESOLVED_TAGS}"
86+
87+
if [ "${#TAG_LIST[@]}" -eq 0 ]; then
88+
echo "No tags provided" >&2
89+
exit 1
90+
fi
91+
92+
PRIMARY_IMAGE="${REGISTRY}:${TAG_LIST[0]}"
93+
94+
echo "Building ${PRIMARY_IMAGE} with rhdh-cli"
95+
npx @red-hat-developer-hub/cli@latest plugin package \
96+
--tag "${PRIMARY_IMAGE}" \
97+
--container-tool podman \
98+
--label "quay.expires-after=${EXPIRES_AFTER}"
99+
100+
# Add any additional tags to the freshly built image.
101+
for tag in "${TAG_LIST[@]:1}"; do
102+
echo "Tagging ${REGISTRY}:${tag}"
103+
podman tag "${PRIMARY_IMAGE}" "${REGISTRY}:${tag}"
104+
done
105+
106+
# Hand the resolved tags over to the push step.
107+
echo "RESOLVED_TAGS=${RESOLVED_TAGS}" >> "${GITHUB_ENV}"
108+
109+
- name: Push image to registry
110+
env:
111+
REGISTRY: ${{ inputs.registry }}
112+
QUAY_USERNAME: ${{ secrets.QUAY_USERNAME }}
113+
QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }}
114+
run: |
115+
set -euo pipefail
116+
117+
podman login -u "${QUAY_USERNAME}" -p "${QUAY_PASSWORD}" quay.io
118+
119+
read -ra TAG_LIST <<< "${RESOLVED_TAGS}"
120+
for tag in "${TAG_LIST[@]}"; do
121+
[ -z "${tag}" ] && continue
122+
echo "Pushing ${REGISTRY}:${tag}"
123+
podman push "${REGISTRY}:${tag}"
124+
done

.github/workflows/ci.yml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,24 @@ jobs:
174174
exit 1
175175
fi
176176
177+
build-image:
178+
name: Workspace ${{ matrix.workspace }}, Build container image
179+
if: github.repository == 'redhat-developer/rhdh-plugins'
180+
needs: find-changed-workspaces
181+
strategy:
182+
matrix:
183+
workspace: ${{ fromJSON(needs.find-changed-workspaces.outputs.workspaces) }}
184+
fail-fast: false
185+
uses: ./.github/workflows/build-container-image.yml
186+
with:
187+
workspace: ${{ matrix.workspace }}
188+
ref: ${{ github.event.pull_request.head.sha }}
189+
tags: |
190+
${{ matrix.workspace }}-pr-${{ github.event.pull_request.number }}
191+
${{ matrix.workspace }}-pr-${{ github.event.pull_request.number }}-${SHORT_SHA}
192+
expires_after: 2w
193+
secrets: inherit
194+
177195
verify:
178196
name: Workspace ${{ matrix.workspace }}, Verify step
179197
runs-on: ubuntu-latest
@@ -201,7 +219,7 @@ jobs:
201219
if: ${{ always() }}
202220
name: check all required jobs
203221
runs-on: ubuntu-latest
204-
needs: [ci, verify]
222+
needs: [ci, build-image, verify]
205223
steps:
206224
- run: exit 1
207225
if: >-

.github/workflows/release_workspace.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,28 @@ jobs:
162162
env:
163163
WORKSPACE_NAME: ${{ inputs.workspace }}
164164
GITHUB_TOKEN: ${{ secrets.RHDH_BOT_TOKEN}}
165+
166+
build-next-image:
167+
name: Build "next" image for ${{ inputs.workspace }}
168+
# Build the rolling "next" image for every merge into main, independent of
169+
# whether the changesets resulted in a release.
170+
if: inputs.branch == 'main'
171+
uses: ./.github/workflows/build-container-image.yml
172+
with:
173+
workspace: ${{ inputs.workspace }}
174+
ref: ${{ inputs.branch }}
175+
tags: ${{ inputs.workspace }}-next
176+
expires_after: 4w
177+
secrets: inherit
178+
179+
build-latest-image:
180+
name: Build "latest" image for ${{ inputs.workspace }}
181+
# Only build the "latest" image once a release of the workspace happened.
182+
needs: release
183+
uses: ./.github/workflows/build-container-image.yml
184+
with:
185+
workspace: ${{ inputs.workspace }}
186+
ref: ${{ inputs.branch }}
187+
tags: ${{ inputs.workspace }}-latest
188+
expires_after: 4w
189+
secrets: inherit

0 commit comments

Comments
 (0)