11# --- Global Script Variables (Defaults) ---
22CLEANUP=" true"
33NO_CVE=" true" # Default to true
4+ # GitHub API curl retries (override in CI/local: export GITHUB_API_CURL_RETRY=5)
5+ GITHUB_API_CURL_RETRY=" ${GITHUB_API_CURL_RETRY:- 3} "
46
57# Variables that will be set by functions and used globally:
68# component_branch, component_base_branch, component_repo_name (from test.env or similar)
@@ -15,6 +17,110 @@ NO_CVE="true" # Default to true
1517# component_push_plr_name (set by wait_for_plr_to_appear)
1618# RELEASE_NAME, RELEASE_NAMESPACE (set and exported by wait_for_release)
1719
20+ patch_component_source_before_merge () {
21+ # CI often runs scripts under xtrace (bash -x). Disable tracing only while handling tokens.
22+ local xtrace_was_on=0
23+ case $- in
24+ * x* ) xtrace_was_on=1 ;;
25+ esac
26+ if [ " ${xtrace_was_on} " -eq 1 ]; then
27+ set +x
28+ fi
29+
30+ echo " Patching component source BEFORE MERGE to ensure multi-arch build..."
31+
32+ # PaC GitHub token: do not export — scoped env only for the helper that requires GH_TOKEN.
33+ # Vault file uses ${component_name} placeholders; envsubst runs at kubectl apply, not on disk.
34+ local secret_value
35+ secret_value=$( yq ' . | select(.metadata.name | contains("pipelines-as-code-secret-")) | .stringData.password' \
36+ " ${SUITE_DIR} /resources/tenant/secrets/tenant-secrets.yaml" | head -n 1)
37+
38+ if [ -z " ${secret_value} " ] || [ " ${secret_value} " = " null" ]; then
39+ log_error " PaC token not found in tenant secrets (pipelines-as-code-secret-*)"
40+ fi
41+
42+ local file_names=(
43+ " .tekton/${component_name} -pull-request.yaml"
44+ " .tekton/${component_name} -push.yaml"
45+ )
46+ local head_sha api_msg pr_response
47+ if ! pr_response=$( curl --retry " ${GITHUB_API_CURL_RETRY} " -s --fail-with-body \
48+ -H " Authorization: token ${secret_value} " \
49+ " https://api.github.com/repos/${component_repo_name} /pulls/${pr_number} " ) ; then
50+ if api_msg=$( jq -r ' .message // empty' <<< " ${pr_response}" 2> /dev/null) && [ -n " ${api_msg} " ]; then
51+ log_error " GitHub API error fetching PR ${pr_number} : ${api_msg} "
52+ else
53+ log_error " failed to fetch PR ${pr_number} from ${component_repo_name} (check PaC token and repo access)"
54+ fi
55+ fi
56+ head_sha=$( jq -r -e ' .head.sha' <<< " ${pr_response}" ) || {
57+ log_error " missing or invalid .head.sha in PR ${pr_number} response"
58+ }
59+
60+ for file_name in " ${file_names[@]} " ; do
61+ local decoded_contents encoded_contents
62+ local contents_response encoded_content_field
63+ echo " Patching ${file_name} ..."
64+
65+ if ! contents_response=$( curl --retry " ${GITHUB_API_CURL_RETRY} " -s --fail-with-body \
66+ -H " Authorization: token ${secret_value} " \
67+ " https://api.github.com/repos/${component_repo_name} /contents/${file_name} ?ref=${head_sha} " ) ; then
68+ if api_msg=$( jq -r ' .message // empty' <<< " ${contents_response}" 2> /dev/null) && [ -n " ${api_msg} " ]; then
69+ log_error " GitHub API error fetching ${file_name} : ${api_msg} "
70+ else
71+ log_error " failed to fetch ${file_name} at ref ${head_sha} from ${component_repo_name} "
72+ fi
73+ else
74+ encoded_content_field=$( jq -r -e ' .content' <<< " ${contents_response}" ) || {
75+ log_error " missing or invalid .content for ${file_name} in PR ${pr_number} "
76+ }
77+ if ! decoded_contents=$( printf ' %s' " ${encoded_content_field} " | base64 -d) ; then
78+ log_error " failed to base64-decode ${file_name} from PR ${pr_number} "
79+ fi
80+ if [ -z " ${decoded_contents} " ]; then
81+ log_error " decoded contents for ${file_name} are empty"
82+ fi
83+ fi
84+
85+ encoded_contents=$(
86+ set -eo pipefail
87+ work_dir=$( mktemp -d)
88+ trap ' rm -rf "${work_dir}"' EXIT
89+ nopath_file_name=$( basename " ${file_name} " )
90+ echo " ${decoded_contents} " > " ${work_dir} /${nopath_file_name} "
91+
92+ # Ensure linux/amd64 + linux/arm64 are present.
93+ if yq -e ' (.spec.params[]? | select(.name == "build-platforms") | .value | type) == "!!seq"' \
94+ " ${work_dir} /${nopath_file_name} " > /dev/null 2>&1 ; then
95+ yq -i ' (.spec.params[] | select(.name == "build-platforms") | .value) |= ([.[] | select(. != "linux/arm64")] + ["linux/arm64"])' \
96+ " ${work_dir} /${nopath_file_name} "
97+ yq -i ' (.spec.params[] | select(.name == "build-platforms") | .value) |= ([.[] | select(. != "linux/amd64")] + ["linux/amd64"])' \
98+ " ${work_dir} /${nopath_file_name} "
99+ else
100+ yq -i ' .spec.params += [{"name": "build-platforms", "value": ["linux/amd64", "linux/arm64"]}]' \
101+ " ${work_dir} /${nopath_file_name} "
102+ fi
103+
104+ base64 -w 0 < " ${work_dir} /${nopath_file_name} "
105+ ) || {
106+ log_error " failed to patch ${file_name} for multi-arch build"
107+ }
108+
109+ GH_TOKEN=" ${secret_value} " " ${SCRIPT_DIR} /scripts/update-file-in-pull-request.sh" \
110+ " ${component_repo_name} " \
111+ " ${pr_number} " \
112+ " ${file_name} " \
113+ " Update PaC templates for multi-arch build" \
114+ " ${encoded_contents} "
115+ done
116+
117+ unset secret_value
118+ if [ " ${xtrace_was_on} " -eq 1 ]; then
119+ set -x
120+ fi
121+ echo " ✅️ Successfully patched component PaC templates for multi-arch."
122+ }
123+
18124# Function to verify Release contents
19125# Relies on global variables: RELEASE_NAMES, RELEASE_NAMESPACE, SCRIPT_DIR, managed_namespace, managed_sa_name, NO_CVE
20126verify_release_contents () {
@@ -29,13 +135,34 @@ verify_release_contents() {
29135 log_error " Could not retrieve Release JSON for ${RELEASE_NAME} "
30136 fi
31137
32- echo " Release JSON: ${release_json} "
33-
34138 local failures=0
35- local image_url mergerequest_url
139+ local image_url mergerequest_url image_arches image_shasum released_status
36140
37141 image_url=$( jq -r ' .status.artifacts.images[0].urls[0] // ""' <<< " ${release_json}" )
38142 mergerequest_url=$( jq -r ' .status.artifacts.merge_requests[0].url // ""' <<< " ${release_json}" )
143+ # Release may list one arch per manifest/index entry (e.g. duplicate amd64); compare distinct sets.
144+ # Strip optional linux/ prefix (e.g. linux/amd64 -> amd64). Default null/missing .arches to [].
145+ image_arches=$( jq -r ' (.status.artifacts.images[0].arches // [])
146+ | map((tostring | split("/") | .[-1]))
147+ | unique
148+ | join(" ")' <<< " ${release_json}" )
149+ image_shasum=$( jq -r ' .status.artifacts.images[0].shasum // ""' <<< " ${release_json}" )
150+ released_status=$( jq -r ' .status.conditions[]? | select(.type=="Released") | .status // ""' <<< " ${release_json}" )
151+
152+ echo " Release fields under validation:"
153+ echo " Released: ${released_status} "
154+ echo " image_url: ${image_url} "
155+ echo " mergerequest_url: ${mergerequest_url} "
156+ echo " image_arches: ${image_arches} "
157+ echo " image_shasum: ${image_shasum} "
158+
159+ echo " Checking Released=True..."
160+ if [ " ${released_status} " = " True" ]; then
161+ echo " ✅️ Released=True"
162+ else
163+ echo " 🔴 Released was not True (found: '${released_status} ')"
164+ failures=$(( failures+ 1 ))
165+ fi
39166
40167 echo " Checking image_url..."
41168 if [ -n " ${image_url} " ]; then
@@ -52,6 +179,39 @@ verify_release_contents() {
52179 failures=$(( failures+ 1 ))
53180 fi
54181
182+ echo " Checking image arches include amd64 and arm64..."
183+ if [[ " ${image_arches} " == * " amd64 " * && " ${image_arches} " == * " arm64 " * ]]; then
184+ echo " ✅️ Found required arches: ${image_arches} "
185+ else
186+ echo " 🔴 Missing required arches (need: amd64 and arm64), found: '${image_arches} '"
187+ failures=$(( failures+ 1 ))
188+ fi
189+
190+ echo " Checking image shasum (manifest list digest) is present..."
191+ if [[ " ${image_shasum} " == sha256:* ]]; then
192+ echo " ✅️ image_shasum: ${image_shasum} "
193+ else
194+ echo " 🔴 image_shasum missing or invalid: '${image_shasum} '"
195+ failures=$(( failures+ 1 ))
196+ fi
197+
198+ echo " Checking skopeo inspect succeeds for both arches (digest pull + registry auth)..."
199+ if [ -n " ${image_url} " ] && [[ " ${image_shasum} " == sha256:* ]]; then
200+ set +e
201+ " ${SCRIPT_DIR} /scripts/skopeo-verify-image.sh" \
202+ " ${image_url} " " ${image_shasum} " \
203+ " ${SUITE_DIR} /resources/managed/secrets/managed-secrets.yaml" \
204+ " amd64 arm64"
205+ skopeo_rc=$?
206+ set -e
207+ if [ " ${skopeo_rc} " -ne 0 ]; then
208+ failures=$(( failures+ 1 ))
209+ fi
210+ elif [ -n " ${image_url} " ]; then
211+ echo " 🔴 Skipping skopeo multi-arch check: image_shasum missing or not sha256:*"
212+ failures=$(( failures+ 1 ))
213+ fi
214+
55215 if [ " ${failures} " -gt 0 ]; then
56216 echo " 🔴 Test has FAILED with ${failures} failure(s)!"
57217 failed_releases=" ${RELEASE_NAME} ${failed_releases} "
0 commit comments