From d549660f4832ca2bd79a89640070e90e8f09f97d Mon Sep 17 00:00:00 2001 From: Faizan Ullah Date: Thu, 29 Oct 2026 13:00:18 +0500 Subject: [PATCH 1/4] ci: scope analyzer to changed plugin families in pull requests --- .../scripts/analyze-changed-packages.sh | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/scripts/analyze-changed-packages.sh diff --git a/.github/workflows/scripts/analyze-changed-packages.sh b/.github/workflows/scripts/analyze-changed-packages.sh new file mode 100644 index 0000000000..462d6f8e24 --- /dev/null +++ b/.github/workflows/scripts/analyze-changed-packages.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# Scope pull-request analysis to the affected plugin families. Pushes to main +# and changes outside packages/ still analyze the entire workspace. +set -euo pipefail + +if [[ "${GITHUB_EVENT_NAME:-}" != "pull_request" ]]; then + exec melos run analyze +fi + +if [[ -z "${GITHUB_BASE_REF:-}" ]]; then + echo 'GITHUB_BASE_REF is required for pull requests' >&2 + exit 1 +fi + +changed_files="$(git diff --name-only "origin/${GITHUB_BASE_REF}...HEAD")" +packages=() +while IFS= read -r file; do + [[ -z "$file" ]] && continue + case "$file" in + packages/*/*) + family="${file#packages/}" + packages+=("${family%%/*}") + ;; + *) + # Workspace configuration or CI changes may affect every package. + echo "Workspace file changed: $file. Analyzing all packages." + exec melos run analyze + ;; + esac +done <<< "$changed_files" + +if (( ${#packages[@]} == 0 )); then + echo 'No package changes to analyze.' + exit 0 +fi + +# Analyze every package in the changed plugin families, including their +# platform interfaces and examples, to catch issues across package boundaries. +while IFS= read -r family; do + [[ -z "$family" ]] && continue + echo "Analyzing ${family} packages" + melos exec -c 5 --scope="*${family}*" -- dart analyze . --fatal-infos +done < <(printf '%s\n' "${packages[@]}" | sort -u) From e2184594d9221cd2f7bf1d14168cbb744a8ab8ed Mon Sep 17 00:00:00 2001 From: Faizan Ullah Date: Thu, 29 Oct 2026 13:00:32 +0500 Subject: [PATCH 2/4] test(ci): cover analyzer scope selection and fail-safe cases --- .../scripts/test-analyze-changed-packages.sh | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 .github/workflows/scripts/test-analyze-changed-packages.sh diff --git a/.github/workflows/scripts/test-analyze-changed-packages.sh b/.github/workflows/scripts/test-analyze-changed-packages.sh new file mode 100644 index 0000000000..add7f8a7f4 --- /dev/null +++ b/.github/workflows/scripts/test-analyze-changed-packages.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +set -euo pipefail +SCRIPT="$(cd "$(dirname "$0")" && pwd)/analyze-changed-packages.sh" +work="$(mktemp -d)" +trap 'rm -rf "$work"' EXIT +mkdir -p "$work/bin" "$work/repo" +cat > "$work/bin/melos" <<'MOCK' +#!/usr/bin/env bash +printf '%s\n' "$*" >> "$MELOS_CALLS" +MOCK +chmod +x "$work/bin/melos" +export PATH="$work/bin:$PATH" +export MELOS_CALLS="$work/calls" +cd "$work/repo" +git init -q -b main +git config user.email test@example.com +git config user.name Test +mkdir -p packages/share_plus/share_plus packages/device_info_plus/device_info_plus +printf 'old\n' > packages/share_plus/share_plus/foo.dart +printf 'old\n' > packages/device_info_plus/device_info_plus/foo.dart +git add . +git commit -qm base +git remote add origin . +git fetch -q origin main +git checkout -q -b feature +export GITHUB_EVENT_NAME=pull_request GITHUB_BASE_REF=main + +assert_calls() { + local expected="$1" + local actual="$(cat "$MELOS_CALLS" 2>/dev/null || true)" + if [[ "$actual" != "$expected" ]]; then + printf 'Expected: %s\nActual: %s\n' "$expected" "$actual" >&2 + exit 1 + fi +} + +# A single plugin family is analyzed; unrelated packages are skipped. +printf 'new\n' > packages/share_plus/share_plus/foo.dart +git add . && git commit -qm single +: > "$MELOS_CALLS" +"$SCRIPT" +assert_calls 'exec -c 5 --scope=*share_plus* -- dart analyze . --fatal-infos' + +# Two plugin families produce exactly two invocations, sorted, without repeats. +printf 'newer\n' > packages/share_plus/share_plus/foo.dart +printf 'new\n' > packages/device_info_plus/device_info_plus/foo.dart +git add . && git commit -qm multiple +: > "$MELOS_CALLS" +"$SCRIPT" +assert_calls $'exec -c 5 --scope=*device_info_plus* -- dart analyze . --fatal-infos\nexec -c 5 --scope=*share_plus* -- dart analyze . --fatal-infos' + +# A workspace config change requires complete analysis. +printf 'config\n' > analysis_options.yaml +git add . && git commit -qm global +: > "$MELOS_CALLS" +"$SCRIPT" +assert_calls 'run analyze' + +# The push path retains complete analysis. +: > "$MELOS_CALLS" +GITHUB_EVENT_NAME=push "$SCRIPT" +assert_calls 'run analyze' + +# Missing base is an error and cannot quietly pass CI. +: > "$MELOS_CALLS" +if GITHUB_BASE_REF='' "$SCRIPT" 2>/dev/null; then + echo 'Missing base incorrectly succeeded' >&2 + exit 1 +fi +assert_calls '' + +echo 'PASS: scoped analysis, multi-family, workspace changes, pushes, and missing base' From 10e837404248e6efcef9338d103aec5cd4e58ecd Mon Sep 17 00:00:00 2001 From: Faizan Ullah Date: Thu, 29 Oct 2026 13:00:45 +0500 Subject: [PATCH 3/4] ci: run scoped analyzer with full-history checkout and selector tests --- .github/workflows/all_plugins.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/all_plugins.yaml b/.github/workflows/all_plugins.yaml index efd2eb66e4..15fafdeda9 100644 --- a/.github/workflows/all_plugins.yaml +++ b/.github/workflows/all_plugins.yaml @@ -121,6 +121,9 @@ jobs: - name: "Bootstrap Workspace" run: melos bootstrap + - name: "Test scoped analysis selection" + run: bash ./.github/workflows/scripts/test-analyze-changed-packages.sh + - name: "Flutter Analyze" - run: melos run analyze + run: bash ./.github/workflows/scripts/analyze-changed-packages.sh From 464a76a4126dfb449713912de11d297fc34b56da Mon Sep 17 00:00:00 2001 From: Faizan Ullah Date: Thu, 29 Oct 2026 14:19:20 +0500 Subject: [PATCH 4/4] test(ci): invoke analysis script through bash without executable bit --- .../workflows/scripts/test-analyze-changed-packages.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/scripts/test-analyze-changed-packages.sh b/.github/workflows/scripts/test-analyze-changed-packages.sh index add7f8a7f4..8a550c6b31 100644 --- a/.github/workflows/scripts/test-analyze-changed-packages.sh +++ b/.github/workflows/scripts/test-analyze-changed-packages.sh @@ -38,7 +38,7 @@ assert_calls() { printf 'new\n' > packages/share_plus/share_plus/foo.dart git add . && git commit -qm single : > "$MELOS_CALLS" -"$SCRIPT" +bash "$SCRIPT" assert_calls 'exec -c 5 --scope=*share_plus* -- dart analyze . --fatal-infos' # Two plugin families produce exactly two invocations, sorted, without repeats. @@ -46,24 +46,24 @@ printf 'newer\n' > packages/share_plus/share_plus/foo.dart printf 'new\n' > packages/device_info_plus/device_info_plus/foo.dart git add . && git commit -qm multiple : > "$MELOS_CALLS" -"$SCRIPT" +bash "$SCRIPT" assert_calls $'exec -c 5 --scope=*device_info_plus* -- dart analyze . --fatal-infos\nexec -c 5 --scope=*share_plus* -- dart analyze . --fatal-infos' # A workspace config change requires complete analysis. printf 'config\n' > analysis_options.yaml git add . && git commit -qm global : > "$MELOS_CALLS" -"$SCRIPT" +bash "$SCRIPT" assert_calls 'run analyze' # The push path retains complete analysis. : > "$MELOS_CALLS" -GITHUB_EVENT_NAME=push "$SCRIPT" +GITHUB_EVENT_NAME=push bash "$SCRIPT" assert_calls 'run analyze' # Missing base is an error and cannot quietly pass CI. : > "$MELOS_CALLS" -if GITHUB_BASE_REF='' "$SCRIPT" 2>/dev/null; then +if GITHUB_BASE_REF='' bash "$SCRIPT" 2>/dev/null; then echo 'Missing base incorrectly succeeded' >&2 exit 1 fi