fleet/.github/scripts/detect-new-fmas-in-pr.sh
Allen Houchins b688fe3636
Improve changed app detection in CI (#37838)
This pull request improves the robustness and reliability of the script
and workflows that detect changed or new maintained apps in pull
requests. The main focus is on making the detection script pass
validation when the test is triggered but no new FMAs are detected.

**Script robustness and error handling:**

* The `.github/scripts/detect-new-fmas-in-pr.sh` script is updated to
always exit successfully (status 0) when no changes are detected, and
only exit with error (status 1) for critical failures like missing `jq`.
A new `safe_exit` function is introduced to standardize output and
ensure graceful exits.
[[1]](diffhunk://#diff-f9bbb0340f504713c99d610f3c64bf281fc13ed3cb8a1c06a5366272c9828a8dR7-R11)
[[2]](diffhunk://#diff-f9bbb0340f504713c99d610f3c64bf281fc13ed3cb8a1c06a5366272c9828a8dL21-R39)
* Improved error handling for missing files, empty variables, and failed
commands throughout the script, including handling cases where
`merge-base`, `git show`, or `jq` fail, and ensuring empty or missing
data does not cause the script to error out.
[[1]](diffhunk://#diff-f9bbb0340f504713c99d610f3c64bf281fc13ed3cb8a1c06a5366272c9828a8dL32-R66)
[[2]](diffhunk://#diff-f9bbb0340f504713c99d610f3c64bf281fc13ed3cb8a1c06a5366272c9828a8dR87-R108)
[[3]](diffhunk://#diff-f9bbb0340f504713c99d610f3c64bf281fc13ed3cb8a1c06a5366272c9828a8dL75-R155)

**Workflow improvements:**

* The `test-fma-darwin-pr-only.yml` and `test-fma-windows-pr-only.yml`
workflows are updated to default to "no changes" if the detection step
fails or does not set the expected output, preventing false positives or
workflow failures.
[[1]](diffhunk://#diff-28b30c8601cb7662d59efbfbbcf800cae91455fd3d875627659dced8c1257a24L70-R72)
[[2]](diffhunk://#diff-51641fd1d2cc19348b81fd8310b62ad270ca5082ceddff2d49064e78f126a1eaL76-R78)
2026-01-05 15:01:47 -06:00

157 lines
5.2 KiB
Bash
Executable file

#!/bin/bash
# Script to detect changed/new maintained apps in a PR
# This script compares the PR branch with the base branch to find:
# 1. New apps added to apps.json
# 2. Apps with changed manifest files
#
# This script always exits successfully (0) when no changes are detected.
# It only exits with error (1) for critical failures like missing jq.
set -uo pipefail
# Get repository root
REPO_ROOT="${GITHUB_WORKSPACE:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
APPS_JSON="${REPO_ROOT}/ee/maintained-apps/outputs/apps.json"
OUTPUTS_DIR="${REPO_ROOT}/ee/maintained-apps/outputs"
# Base branch (usually main or the PR's base branch)
# In GitHub Actions, GITHUB_BASE_REF is set for pull_request events
BASE_BRANCH="${GITHUB_BASE_REF:-main}"
# Use origin/ prefix for remote branch reference
BASE_BRANCH_REF="origin/${BASE_BRANCH}"
# Ensure GITHUB_OUTPUT exists
GITHUB_OUTPUT="${GITHUB_OUTPUT:-${REPO_ROOT}/.github_output}"
# Function to safely set outputs and exit
safe_exit() {
local has_changes="${1:-false}"
local changed_apps="${2:-[]}"
echo "CHANGED_APPS=${changed_apps}" >> "$GITHUB_OUTPUT"
echo "HAS_CHANGES=${has_changes}" >> "$GITHUB_OUTPUT"
exit 0
}
# Check if jq is available - this is a critical failure
if ! command -v jq &> /dev/null; then
echo "Error: jq is required but not installed" >&2
safe_exit "false" "[]"
exit 1
fi
# Function to extract app slugs from apps.json
extract_slugs() {
local apps_file="$1"
if [ ! -f "$apps_file" ]; then
echo ""
return 0
fi
jq -r '.apps[].slug' "$apps_file" 2>/dev/null | sort || echo ""
}
# Function to extract app slugs from changed manifest files
extract_slugs_from_changed_manifests() {
local changed_files="$1"
local slugs=()
if [ -z "$changed_files" ]; then
echo ""
return 0
fi
while IFS= read -r file; do
# Skip empty lines
[ -z "$file" ] && continue
# Extract slug from path like: outputs/app-name/darwin.json or outputs/app-name/windows.json
if [[ "$file" =~ outputs/([^/]+)/(darwin|windows)\.json$ ]]; then
app_name="${BASH_REMATCH[1]}"
platform="${BASH_REMATCH[2]}"
slug="${app_name}/${platform}"
slugs+=("$slug")
fi
done <<< "$changed_files"
# Remove duplicates and sort
if [ ${#slugs[@]} -eq 0 ]; then
echo ""
else
printf '%s\n' "${slugs[@]}" | sort -u
fi
}
# Get changed files in outputs directory
echo "Detecting changed files in outputs directory..."
echo "Comparing HEAD with ${BASE_BRANCH_REF}..."
# Use merge-base to find the common ancestor for comparison
# If merge-base fails, try using the base branch ref directly
MERGE_BASE=""
if git merge-base "${BASE_BRANCH_REF}" HEAD &>/dev/null; then
MERGE_BASE=$(git merge-base "${BASE_BRANCH_REF}" HEAD 2>/dev/null || echo "")
fi
# If merge-base still failed, try the base branch ref directly
if [ -z "$MERGE_BASE" ]; then
echo "Warning: Could not find merge-base, using ${BASE_BRANCH_REF} directly"
MERGE_BASE="${BASE_BRANCH_REF}"
fi
# Get changed files, handling errors gracefully
CHANGED_FILES=""
if git diff --name-only "$MERGE_BASE" HEAD -- "ee/maintained-apps/outputs/" &>/dev/null; then
CHANGED_FILES=$(git diff --name-only "$MERGE_BASE" HEAD -- "ee/maintained-apps/outputs/" 2>/dev/null || echo "")
else
echo "Warning: Could not get changed files, assuming no changes"
CHANGED_FILES=""
fi
# Extract slugs from changed manifest files
CHANGED_MANIFEST_SLUGS=$(extract_slugs_from_changed_manifests "$CHANGED_FILES")
# Get current apps.json slugs
CURRENT_SLUGS=$(extract_slugs "$APPS_JSON")
# Get base branch apps.json slugs
echo "Fetching base branch apps.json from ${MERGE_BASE}..."
BASE_APPS_JSON=""
BASE_SLUGS=""
if git show "${MERGE_BASE}:ee/maintained-apps/outputs/apps.json" &>/dev/null; then
BASE_APPS_JSON=$(git show "${MERGE_BASE}:ee/maintained-apps/outputs/apps.json" 2>/dev/null || echo "")
if [ -n "$BASE_APPS_JSON" ]; then
BASE_SLUGS=$(echo "$BASE_APPS_JSON" | jq -r '.apps[].slug' 2>/dev/null | sort || echo "")
fi
fi
if [ -z "$BASE_SLUGS" ]; then
echo "Warning: Could not find apps.json in base branch, only checking manifest file changes"
# If we can't get base slugs, only use manifest changes (don't assume all apps are new)
NEW_SLUGS=""
else
# Find new slugs in apps.json
NEW_SLUGS=$(comm -13 <(echo "$BASE_SLUGS" || echo "") <(echo "$CURRENT_SLUGS" || echo "") 2>/dev/null || echo "")
fi
# Combine all changed slugs (from manifest changes and new apps)
ALL_CHANGED_SLUGS=$(printf '%s\n' "$CHANGED_MANIFEST_SLUGS" "$NEW_SLUGS" | grep -v '^$' | sort -u || echo "")
# Output results - always exit successfully
if [ -z "$ALL_CHANGED_SLUGS" ]; then
echo "No changed apps detected."
safe_exit "false" "[]"
fi
echo "Detected changed apps:"
echo "$ALL_CHANGED_SLUGS" | while read -r slug; do
[ -n "$slug" ] && echo " - $slug"
done
# Output as JSON array for GitHub Actions
CHANGED_APPS_JSON=$(echo "$ALL_CHANGED_SLUGS" | jq -R -s -c 'split("\n") | map(select(length > 0))' 2>/dev/null || echo "[]")
echo "CHANGED_APPS=$CHANGED_APPS_JSON" >> "$GITHUB_OUTPUT"
echo "HAS_CHANGES=true" >> "$GITHUB_OUTPUT"
exit 0