#!/usr/bin/env bash # Emit per-service + aggregate change flags for the CI / build workflows. # # Reads: # BASE_SHA — diff base. Empty / unreachable / diff-failure → emit everything as true. # HEAD_SHA — diff target. Defaults to HEAD. # # Writes key=value lines to $GITHUB_OUTPUT (defaults to /dev/stdout for local runs). # # ROBUSTNESS CONTRACT: this script must ALWAYS emit a full set of outputs. A # missing/empty output makes the job's `outputs:` mapping evaluate to a Go format # error (`%!t(string=)`) which fails detect-changes AND every job that `needs` it # (cascade). So we do NOT use `set -e` (an aborting git/grep must not kill the # script before it emits), we treat any base/diff failure as "rebuild all", and an # EXIT trap emits rebuild-all + forces exit 0 if we ever exit early/unexpectedly. # # Keys emitted: # admin, backend, sdk, portal, tts, crawler, dsms_gateway, dsms_node # any_python, any_node, any set -uo pipefail BASE_SHA="${BASE_SHA:-}" HEAD_SHA="${HEAD_SHA:-HEAD}" OUT="${GITHUB_OUTPUT:-/dev/stdout}" ALL_KEYS=(admin backend sdk portal tts crawler dsms_gateway dsms_node any_python any_node any) emit() { echo "$1=$2" >> "$OUT" } emit_all_true() { reason=$1 echo "→ rebuild all: $reason" for k in "${ALL_KEYS[@]}"; do emit "$k" true done } # Safety net: never let the job end with undefined outputs. If we exit before # DONE=1 (any error / early termination), emit rebuild-all and exit 0 so the # step still succeeds — rebuild-all is the safe over-approximation. DONE=0 trap '[ "$DONE" = 1 ] || emit_all_true "safety-net (unexpected exit)"; exit 0' EXIT if [ -z "$BASE_SHA" ]; then emit_all_true "no BASE_SHA provided" DONE=1; exit 0 fi if ! git rev-parse --verify "${BASE_SHA}^{commit}" >/dev/null 2>&1; then emit_all_true "BASE_SHA ${BASE_SHA} unreachable" DONE=1; exit 0 fi if ! changed=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA" 2>/dev/null); then emit_all_true "git diff against ${BASE_SHA} failed" DONE=1; exit 0 fi echo "Changed files since ${BASE_SHA}:" echo "${changed:-(none)}" echo "---" check() { key=$1 path=$2 if echo "$changed" | grep -q "^${path}/"; then emit "$key" true echo " ${key} (${path}/): true" else emit "$key" false echo " ${key} (${path}/): false" fi } check admin admin-compliance check backend backend-compliance check sdk ai-compliance-sdk check portal developer-portal check tts compliance-tts-service check crawler document-crawler check dsms_gateway dsms-gateway check dsms_node dsms-node # Aggregate flags if echo "$changed" | grep -qE "^(backend-compliance|document-crawler|dsms-gateway|compliance-tts-service)/"; then emit any_python true echo " any_python: true" else emit any_python false echo " any_python: false" fi if echo "$changed" | grep -qE "^(admin-compliance|developer-portal)/"; then emit any_node true echo " any_node: true" else emit any_node false echo " any_node: false" fi if [ -n "$changed" ]; then emit any true echo " any: true" else emit any false echo " any: false" fi DONE=1