@@ -22,6 +22,8 @@ JENKINS_K8S_DNS_NAME=${JENKINS_K8S_DNS_NAME:-${JENKINS_NETWORK_ALIAS}.default.sv
2222KEEP_JENKINS=${KEEP_JENKINS:- true}
2323
2424CI_K8_FILE=${CI_K8_FILE:- .ci/ job_matrix_gha_k8.yaml}
25+ # Accept one or more config files (space/comma separated). Falls back to CI_K8_FILE.
26+ CI_K8_FILES=${CI_K8_FILES:- ${CI_K8_FILE} }
2527TARGET_ARCHES=${TARGET_ARCHES:- ${TARGET_ARCH:- } }
2628SKIP_REGEX=${SKIP_REGEX:- }
2729AGENT_EXECUTORS=${AGENT_EXECUTORS:- 8}
119121
120122mkdir -p " ${LOG_DIR} "
121123
122- echo " [1/9] Using static workflow config ${CI_K8_FILE} "
123- if [[ ! -f " ${CI_K8_FILE} " ]]; then
124- echo " ERROR: Config file not found: ${CI_K8_FILE} " >&2
124+ conf_files=()
125+ while IFS= read -r conf; do
126+ [[ -n " ${conf} " ]] && conf_files+=(" ${conf} " )
127+ done < <( echo " ${CI_K8_FILES} " | tr ' , ' ' \n' | awk ' NF' )
128+ if [[ " ${# conf_files[@]} " -eq 0 ]]; then
129+ echo " ERROR: No config files specified (set CI_K8_FILES or CI_K8_FILE)" >&2
125130 exit 1
126131fi
127- conf_files=(" ${CI_K8_FILE} " )
132+
133+ echo " [1/9] Using static workflow config(s): ${conf_files[*]} "
134+ for conf in " ${conf_files[@]} " ; do
135+ if [[ ! -f " ${conf} " ]]; then
136+ echo " ERROR: Config file not found: ${conf} " >&2
137+ exit 1
138+ fi
139+ done
128140
129141target_arch_list=()
130142while IFS= read -r arch; do
@@ -297,6 +309,55 @@ save_jenkins_artifacts() {
297309 docker logs " ${JENKINS_NAME} " > " ${LOG_DIR} /${prefix} .jenkins-container.log" 2>&1 || true
298310}
299311
312+ # Extract declared stage names from a matrix YAML in first-appearance order (deduped).
313+ extract_declared_stages () {
314+ local file=" $1 "
315+ awk '
316+ /^[[:space:]]*stage:[[:space:]]*/ {
317+ line=$0
318+ sub(/^[[:space:]]*stage:[[:space:]]*/, "", line)
319+ sub(/[[:space:]]*(#.*)?$/, "", line)
320+ gsub(/["' \' ' ]/, "", line)
321+ if (line != "" && !(line in seen)) { seen[line]=1; order[++n]=line }
322+ }
323+ END { for (i=1;i<=n;i++) print order[i] }
324+ ' " ${file} "
325+ }
326+
327+ # Extract stage names in the order they were entered from a Jenkins console log (deduped).
328+ extract_console_stages () {
329+ local logf=" $1 "
330+ grep -a ' Starting stage:' " ${logf} " 2> /dev/null \
331+ | sed -E ' s/.*Starting stage:[[:space:]]*//' \
332+ | sed -E ' s/[^A-Za-z0-9_.-].*$//' \
333+ | awk ' NF && !seen[$0]++'
334+ }
335+
336+ # Assert stages ran sequentially in declared order. Self-activating: no-op unless
337+ # the config declares >= 2 stages. Returns non-zero on mismatch.
338+ assert_stage_order () {
339+ local conf_file=" $1 " console_log=" $2 " label=" $3 "
340+ local expected actual nstages
341+ expected=$( extract_declared_stages " ${conf_file} " )
342+ nstages=$( printf ' %s\n' " ${expected} " | awk ' NF' | wc -l | tr -d ' ' )
343+ if [[ " ${nstages} " -lt 2 ]]; then
344+ return 0
345+ fi
346+ if [[ ! -f " ${console_log} " ]]; then
347+ echo " STAGE-ORDER FAIL ${label} : console log not found (${console_log} )"
348+ return 1
349+ fi
350+ actual=$( extract_console_stages " ${console_log} " )
351+ if [[ " ${expected} " == " ${actual} " ]]; then
352+ echo " STAGE-ORDER PASS ${label} : $( echo ${expected} | tr ' \n' ' ' ) "
353+ return 0
354+ fi
355+ echo " STAGE-ORDER FAIL ${label} "
356+ echo " expected: $( echo ${expected} | tr ' \n' ' ' ) "
357+ echo " actual: $( echo ${actual} | tr ' \n' ' ' ) "
358+ return 1
359+ }
360+
300361echo " Waiting for Jenkins CLI readiness"
301362cli_ready=false
302363for i in $( seq 1 90) ; do
@@ -575,6 +636,9 @@ for conf in "${conf_files[@]}"; do
575636 fail_count=$(( fail_count + 1 ))
576637 else
577638 echo " PASS ${conf_rel} TARGET_ARCH=${target_arch} "
639+ if ! assert_stage_order " ${conf} " " ${LOG_DIR} /${output_prefix} .jenkins-console.log" " ${conf_base} TARGET_ARCH=${target_arch} " ; then
640+ fail_count=$(( fail_count + 1 ))
641+ fi
578642 fi
579643 done
580644done
0 commit comments