@@ -50,25 +50,52 @@ jobs:
5050 ignore_pattern=$(grep -v '^#' .github/.ghaignore | grep -v '^$' | tr '\n' '|' | sed 's/|$//')
5151 echo "Ignore pattern: $ignore_pattern"
5252
53+ # Single source of truth for "what is a framework project": a directory
54+ # whose name is exactly "anchor". `find -type d -name anchor` gives us
55+ # that by construction — no substring matching, no path-segment trickery,
56+ # so siblings like "anchor-example/" or nested files such as
57+ # "anchor-example/app/pages/api/foo.ts" can never enter the build list.
5358 function get_projects() {
5459 find . -type d -name "anchor" | grep -vE "$ignore_pattern" | sort
5560 }
5661
62+ # Filter the full project list down to projects touched by the given
63+ # changed files. A file "touches" a project iff it lives inside that
64+ # project directory (prefix match on "<project>/"). This is an
65+ # intersection against get_projects(), so the result is always a subset
66+ # of the authoritative project list.
67+ function filter_by_changes() {
68+ local all_projects="$1"
69+ shift
70+ local changed_files=("$@")
71+ echo "$all_projects" | while read -r project; do
72+ [ -z "$project" ] && continue
73+ # Strip leading ./ so prefix comparison matches git's output
74+ local project_prefix="${project#./}/"
75+ for file in "${changed_files[@]}"; do
76+ if [[ "$file" == "$project_prefix"* ]]; then
77+ echo "$project"
78+ break
79+ fi
80+ done
81+ done | sort -u
82+ }
83+
5784 # Determine which projects to build and test
5885 if [[ "${{ github.event_name }}" == "schedule" || "${{ steps.changes.outputs.workflow }}" == "true" ]]; then
5986 # Workflow file changed or schedule — build everything
6087 projects=$(get_projects)
6188 elif [[ "${{ github.event_name }}" == "push" ]]; then
6289 # On push, only build projects with changes since parent commit
63- changed_files=$ (git diff --name-only HEAD~1 HEAD 2>/dev/null || echo "" )
64- if [ -z "$ changed_files" ]; then
90+ mapfile -t changed_files < < (git diff --name-only HEAD~1 HEAD 2>/dev/null || true )
91+ if [ ${# changed_files[@]} -eq 0 ]; then
6592 projects=$(get_projects)
6693 else
67- projects=$(echo "$changed_files" | while read file; do dirname "$file" | grep anchor | sed 's#/anchor/.*#/anchor#g'; done | grep -vE "$ignore_pattern" | sort -u )
94+ projects=$(filter_by_changes "$(get_projects)" "${changed_files[@]}" )
6895 fi
6996 elif [[ "${{ steps.changes.outputs.anchor }}" == "true" ]]; then
7097 changed_files=(${{ steps.changes.outputs.anchor_files }})
71- projects=$(for file in "${changed_files[@]}"; do dirname "${file}" | grep anchor | sed 's#/anchor/.*#/anchor#g'; done | grep -vE "$ignore_pattern" | sort -u )
98+ projects=$(filter_by_changes "$(get_projects)" "${changed_files[@]}")
7299 else
73100 projects=""
74101 fi
0 commit comments