@@ -74,57 +74,45 @@ jobs:
7474 app_id : ${{ env.GH_APP_ID_DISTRO_CI }}
7575 private_key : ${{ env.GH_APP_PRIVATE_KEY_DISTRO_CI }}
7676
77+ # "Parse dev tag" runs before the commit SHA is known, so build release-tools
78+ # here. The binary in /tmp survives the later checkout-at-SHA.
79+ - name : Checkout for tooling
80+ uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
81+
82+ - name : Install tools for tooling
83+ uses : ./.github/actions/install-tool-versions
84+ with :
85+ tools : |
86+ golang
87+
88+ - name : Build release-tools
89+ run : cd scripts/release-tools && go build -o /tmp/release-tools .
90+
7791 - name : Parse dev tag
7892 id : parse
7993 env :
8094 HARBOR_API : " https://${{ env.HARBOR_REGISTRY }}/api/v2.0"
8195 HARBOR_REPO : " projects/${{ env.HARBOR_PROJECT }}/repositories/camunda-platform"
8296 run : |
8397 input_tag="${{ inputs.dev-tag }}"
84-
85- # Check if it's a rolling tag format: {major}-dev-latest
98+
99+ # A rolling tag ({major}-dev-latest) needs the artifact's tag list to
100+ # resolve to a concrete {version}-dev-{sha}; fetch it for resolve-tag.
101+ tags_args=()
86102 if [[ "$input_tag" =~ ^[0-9]+-dev-latest$ ]]; then
87103 echo "::notice::Rolling tag detected: ${input_tag}, resolving to actual dev tag..."
88-
89- # Query Harbor API to get all tags for this artifact
90- tags_response=$(curl -sf "${HARBOR_API}/${HARBOR_REPO}/artifacts/${input_tag}/tags" \
104+ curl -sf "${HARBOR_API}/${HARBOR_REPO}/artifacts/${input_tag}/tags" \
91105 -u "${HARBOR_REGISTRY_USER}:${HARBOR_REGISTRY_PASSWORD}" \
92- --retry 3 --retry-delay 5 --retry-all-errors)
93-
94- if [[ -z "$tags_response" || "$tags_response" == "null" ]]; then
95- echo "::error::Rolling tag ${input_tag} not found in Harbor"
96- exit 1
97- fi
98-
99- # Find the actual dev tag (format: {version}-dev-{sha})
100- dev_tag=$(echo "$tags_response" | jq -r '.[].name | select(test("^[0-9]+\\.[0-9]+\\.[0-9]+(-[a-zA-Z0-9]+)?-dev-[a-f0-9]+$"))' | head -1)
101-
102- if [[ -z "$dev_tag" ]]; then
103- echo "::error::Could not find actual dev tag for rolling tag ${input_tag}"
104- exit 1
105- fi
106-
107- echo "::notice::Resolved rolling tag ${input_tag} to: ${dev_tag}"
108- else
109- dev_tag="$input_tag"
106+ --retry 3 --retry-delay 5 --retry-all-errors > /tmp/dev-tags.json
107+ tags_args=(--tags-file /tmp/dev-tags.json)
110108 fi
111-
112- echo "resolved_tag=${dev_tag}" | tee -a $GITHUB_OUTPUT
113-
114- # Validate format: {version}-dev-{sha}
115- if [[ ! "$dev_tag" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?-dev-[a-f0-9]+$ ]]; then
116- echo "::error::Invalid dev tag format. Expected: {version}-dev-{sha} (e.g., 13.4.0-dev-abc1234)"
117- exit 1
118- fi
119-
120- # Extract version (everything before -dev-)
121- version="${dev_tag%-dev-*}"
122- echo "version=${version}" | tee -a $GITHUB_OUTPUT
123-
124- # Extract SHA (everything after -dev-)
125- sha="${dev_tag##*-dev-}"
126109
127- # Resolve short SHA to full 40-char commit SHA.
110+ # Resolve + validate the dev tag; emit resolved_tag/version/chart_major/
111+ # rc_tag/rc_latest_tag to $GITHUB_OUTPUT. The short commit SHA is printed
112+ # to stdout and expanded to the full SHA below.
113+ sha=$(/tmp/release-tools resolve-tag --kind dev --input-tag "$input_tag" "${tags_args[@]}")
114+
115+ # Resolve short SHA to full 40-char commit SHA (GitHub API stays glue).
128116 # actions/checkout treats short SHAs as branch/tag names and fails.
129117 if [[ ${#sha} -lt 40 ]]; then
130118 echo "::notice::Short SHA detected (${sha}); resolving via GitHub API..."
@@ -142,14 +130,6 @@ jobs:
142130 fi
143131
144132 echo "sha=${sha}" | tee -a $GITHUB_OUTPUT
145-
146- # Extract chart major (first number of version)
147- chart_major="${version%%.*}"
148- echo "chart_major=${chart_major}" | tee -a $GITHUB_OUTPUT
149-
150- # RC tag format
151- echo "rc_tag=${version}-rc" | tee -a $GITHUB_OUTPUT
152- echo "rc_latest_tag=${chart_major}-rc-latest" | tee -a $GITHUB_OUTPUT
153133
154134 - name : Checkout at commit SHA
155135 uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
@@ -165,18 +145,16 @@ jobs:
165145 fi
166146
167147 - name : Login to Harbor
168- uses : docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
169- with :
170- registry : ${{ env.HARBOR_REGISTRY }}
171- username : ${{ env.HARBOR_REGISTRY_USER }}
172- password : ${{ env.HARBOR_REGISTRY_PASSWORD }}
148+ run : |
149+ # Retrying registry login; harbor_helm_pull below also re-auths on 401.
150+ source "${GITHUB_WORKSPACE}/scripts/harbor-retry.sh"
151+ harbor_login
173152
174153 - name : Install tools
175154 uses : ./.github/actions/install-tool-versions
176155 with :
177156 tools : |
178157 helm
179- yq
180158
181159 - name : Pull and validate dev package
182160 id : package
@@ -200,28 +178,11 @@ jobs:
200178 echo "✅ Dev package exists: ${package_file}"
201179 echo "package_file=${package_file}" | tee -a $GITHUB_OUTPUT
202180
203- # Extract Camunda version from Chart.yaml inside the package
181+ # Extract camunda_version (appVersion, .x-stripped), chart_dir_id, the
182+ # component-image-versions annotation and has_image_overrides from the
183+ # package's Chart.yaml → $GITHUB_OUTPUT.
204184 tar -xzf "${package_file}" camunda-platform/Chart.yaml -O > /tmp/Chart.yaml
205- camunda_version=$(yq '.appVersion' /tmp/Chart.yaml | sed 's/.x//')
206-
207- echo "camunda_version=${camunda_version}" | tee -a $GITHUB_OUTPUT
208- echo "chart_dir_id=${camunda_version}" | tee -a $GITHUB_OUTPUT
209-
210- # Extract component image versions from annotation (added by dev build)
211- image_versions=$(yq '.annotations."camunda.io/component-image-versions" // ""' /tmp/Chart.yaml)
212- if [[ -n "${image_versions}" ]]; then
213- echo "image_versions<<EOF" >> $GITHUB_OUTPUT
214- echo "${image_versions}" >> $GITHUB_OUTPUT
215- echo "EOF" >> $GITHUB_OUTPUT
216- fi
217-
218- # Check if image overrides were used during dev build
219- image_overrides=$(yq '.annotations."camunda.io/imageOverrides" // ""' /tmp/Chart.yaml)
220- if [[ -n "${image_overrides}" ]]; then
221- echo "has_image_overrides=true" >> $GITHUB_OUTPUT
222- else
223- echo "has_image_overrides=false" >> $GITHUB_OUTPUT
224- fi
185+ /tmp/release-tools chart-metadata --chart-yaml /tmp/Chart.yaml
225186
226187 - name : Install release-please
227188 run : npm i release-please -g
@@ -276,58 +237,25 @@ jobs:
276237 GH_TOKEN : ${{ steps.generate-github-token.outputs.token }}
277238 run : |
278239 set -euo pipefail
279-
240+
280241 chart_version="${{ steps.parse.outputs.version }}"
281242 app_version="${{ steps.package.outputs.camunda_version }}"
282243 head_ref="${{ steps.release-please.outputs.head_ref }}"
283- package_file="${{ steps.package.outputs.package_file }}"
284- chart_path="charts/camunda-platform-${app_version}"
285-
286- echo "Extracting images from dev package for version-matrix.json"
287-
288- # Extract the chart from the package
289- mkdir -p /tmp/rc-chart
290- tar -xzf "${package_file}" -C /tmp/rc-chart
291-
292- # Build layered values args (same approach as generate-version-matrix.sh)
293- scenario_dir="${chart_path}/test/integration/scenarios/chart-full-setup"
294- helm_values_args=""
295- for layer_file in \
296- "${scenario_dir}/values/base.yaml" \
297- "${scenario_dir}/values/identity/keycloak.yaml" \
298- "${scenario_dir}/values/persistence/elasticsearch.yaml"; do
299- if [[ -f "${layer_file}" ]]; then
300- helm_values_args="${helm_values_args} --values ${layer_file}"
301- fi
302- done
303-
304- # Get images using helm template on the extracted package
305- chart_images=$(
306- helm template --skip-tests camunda /tmp/rc-chart/camunda-platform \
307- ${helm_values_args} 2>/dev/null |
308- tr -d "\"'" | awk '/image:/{gsub(/^(camunda|bitnami)/, "docker.io/&", $2); printf "%s\n", $2}' |
309- sort | uniq
310- )
311- chart_images_json=$(echo -e "$chart_images" | jq -R | jq -sc)
312-
313- # Checkout release-please branch to update version-matrix.json
244+
245+ # Update version-matrix.json on the release-please branch.
314246 git fetch origin "${head_ref}"
315247 git checkout "${head_ref}"
316-
317- # Update version-matrix.json
248+
318249 version_matrix_file="version-matrix/camunda-${app_version}/version-matrix.json"
319- mkdir -p "$(dirname "${version_matrix_file}")"
320- test -f "${version_matrix_file}" || echo '[]' > "${version_matrix_file}"
321-
322- # Remove existing entry for this version if present, then add new one
323- updated_json=$(cat "${version_matrix_file}" | \
324- jq --arg ver "${chart_version}" 'map(select(.chart_version != $ver))' | \
325- jq --arg ver "${chart_version}" --argjson imgs "${chart_images_json}" \
326- '. + [{"chart_version": $ver, "chart_images": $imgs}]')
327-
328- echo "${updated_json}" > "${version_matrix_file}"
329-
330- # Check if there are changes to commit
250+
251+ # Write the matrix entry from the camunda.io/chart-images annotation in
252+ # the package's Chart.yaml (extracted to /tmp/Chart.yaml above).
253+ /tmp/release-tools update-matrix \
254+ --chart-yaml /tmp/Chart.yaml \
255+ --chart-version "${chart_version}" \
256+ --matrix-file "${version_matrix_file}"
257+
258+ # Commit + push if changed.
331259 if git diff --quiet "${version_matrix_file}"; then
332260 echo "::notice::No changes to version-matrix.json"
333261 else
@@ -338,90 +266,36 @@ jobs:
338266 git push origin "${head_ref}"
339267 echo "::notice::Updated version-matrix.json on release-please branch"
340268 fi
341-
342- # Switch back to original checkout
269+
270+ # Switch back to original checkout.
343271 git checkout "${{ steps.parse.outputs.sha }}"
344- rm -rf /tmp/rc-chart
345272
346273 - name : Add RC tags to Harbor package
347274 env :
348275 HARBOR_API : " https://${{ env.HARBOR_REGISTRY }}/api/v2.0"
349276 HARBOR_REPO : " projects/${{ env.HARBOR_PROJECT }}/repositories/camunda-platform"
350277 run : |
351278 set -euo pipefail
352- # harbor_curl bakes in auth + --retry 3 --retry-delay 5 --retry-all-errors
353- # with -sf, so HTTP 4xx/5xx (incl. transient 401) trigger retries.
354- source "${GITHUB_WORKSPACE}/scripts/harbor-retry.sh"
355-
356- # Get artifact digest from dev tag
357- echo "Getting digest for dev tag: ${{ steps.parse.outputs.resolved_tag }}"
358- digest=$(harbor_curl "${HARBOR_API}/${HARBOR_REPO}/artifacts/${{ steps.parse.outputs.resolved_tag }}" \
359- | jq -r '.digest')
360-
361- if [[ -z "$digest" || "$digest" == "null" ]]; then
362- echo "::error::Failed to get digest for dev tag"
363- exit 1
364- fi
279+ # Point {version}-rc and {major}-rc-latest at the dev artifact's digest,
280+ # idempotently (no-op if already there, else move). Untagging uses the
281+ # deleteTag endpoint (DELETE .../tags/{tag}) so only the tag is removed,
282+ # never the shared artifact. Auth is HARBOR_REGISTRY_USER/PASSWORD (env).
283+ api="${HARBOR_API}"
284+ repo="${HARBOR_REPO}"
285+
286+ # Digest of the dev artifact the RC tags should point at.
287+ digest=$(/tmp/release-tools harbor-tag digest --api "$api" --repo "$repo" \
288+ --ref "${{ steps.parse.outputs.resolved_tag }}")
365289 echo "Digest: ${digest}"
366290
367- # Helpers for idempotent tagging.
368- tag_digest() {
369- local tag_name="$1"
370- harbor_curl "${HARBOR_API}/${HARBOR_REPO}/artifacts/${tag_name}" \
371- | jq -r '.digest // empty' || true
372- }
373-
374- delete_tag_if_exists() {
375- local tag_name="$1"
376- echo "Removing existing tag if present: ${tag_name}"
377- harbor_curl -X DELETE "${HARBOR_API}/${HARBOR_REPO}/artifacts/${tag_name}" || true
378- }
379-
380- add_or_update_tag() {
381- local tag_name="$1"
382- local must_move="$2" # true for rolling tags
383-
384- existing_digest="$(tag_digest "${tag_name}")"
385- if [[ -n "${existing_digest}" ]]; then
386- if [[ "${existing_digest}" == "${digest}" ]]; then
387- echo "✅ Tag already present and up-to-date: ${tag_name} -> ${digest}"
388- return 0
389- fi
390-
391- if [[ "${must_move}" == "true" ]]; then
392- echo "ℹ️ Tag exists but points elsewhere; will move: ${tag_name} (${existing_digest} -> ${digest})"
393- else
394- echo "ℹ️ Tag exists but points elsewhere; will overwrite: ${tag_name} (${existing_digest} -> ${digest})"
395- fi
396- delete_tag_if_exists "${tag_name}"
397- fi
291+ # RC version tag — reruns safe (overwrite if it points elsewhere).
292+ /tmp/release-tools harbor-tag ensure --api "$api" --repo "$repo" \
293+ --digest "${digest}" --tag "${{ steps.parse.outputs.rc_tag }}"
398294
399- echo "Adding tag: ${tag_name}"
400- http_code="$(harbor_curl -o /tmp/harbor-tag.json -w "%{http_code}" \
401- -X POST "${HARBOR_API}/${HARBOR_REPO}/artifacts/${digest}/tags" \
402- -H "Content-Type: application/json" \
403- -d "{\"name\": \"${tag_name}\"}" || true)"
295+ # RC rolling latest tag — always move to the selected digest.
296+ /tmp/release-tools harbor-tag ensure --api "$api" --repo "$repo" \
297+ --digest "${digest}" --tag "${{ steps.parse.outputs.rc_latest_tag }}" --move
404298
405- if [[ "${http_code}" != "201" && "${http_code}" != "200" && "${http_code}" != "409" ]]; then
406- echo "::error::Failed to add tag: ${tag_name} (HTTP ${http_code})"
407- exit 1
408- fi
409-
410- # If Harbor returned conflict (409), verify whether the tag now exists and points to the desired digest.
411- final_digest="$(tag_digest "${tag_name}")"
412- if [[ "${final_digest}" != "${digest}" ]]; then
413- echo "::error::Tag '${tag_name}' does not point to expected digest after update."
414- echo "::error::Expected: ${digest}; got: ${final_digest:-<missing>}"
415- exit 1
416- fi
417- }
418-
419- # Add RC version tag (make reruns safe: overwrite if already exists)
420- add_or_update_tag "${{ steps.parse.outputs.rc_tag }}" "false"
421-
422- # Add RC rolling latest tag (always move to the selected digest)
423- add_or_update_tag "${{ steps.parse.outputs.rc_latest_tag }}" "true"
424-
425299 echo "✅ RC tags added successfully"
426300
427301 - name : Summary
0 commit comments