Skip to content

fix(ci): path-aware find-projects#161

Merged
ksroda-sa merged 3 commits into
mainfrom
fix/path-aware-find-projects
May 14, 2026
Merged

fix(ci): path-aware find-projects#161
ksroda-sa merged 3 commits into
mainfrom
fix/path-aware-find-projects

Conversation

@ksroda-sa

Copy link
Copy Markdown
Collaborator

Summary

Verified via test PR #160 that required status checks (without "Require workflows to succeed") block any PR that doesn't trigger every required workflow — paths-filtered workflows leave their check name in "Expected — waiting for status to be reported" forever.

This PR fixes that by:

  1. Removing the paths: filter from pull_request: on every test workflow plus extract.yml. The workflows now always trigger, so the aggregator (<Framework> tests passed, validate) always reports.
  2. Making find-projects path-aware in each test workflow. It computes the changed paths between the PR base and head with git diff, then emits only the project directories whose subtree was touched. Empty matrix → test/build job skipped → aggregator passes via result == 'skipped'.
  3. Hardening test-js's find with -not -path "*/.angular/*" so Angular's Vite dep cache can't leak into the matrix if workflow ordering ever changes.

Behavior matrix

PR scope test-js matrix test-android matrix test-ios matrix
Docs-only (README.md) [] → skipped [] → skipped [] → skipped all skipped → all aggregators green
samples/react/login-pkce/** ["samples/react/login-pkce"] [] → skipped [] → skipped only test-js runs work
samples/android/** [] → skipped ["samples/android/<flow>"] [] → skipped only test-android runs work
Workflow file itself changes full matrix (exercises the workflow against every sample) (others skipped unless they touched too)
Push to main / schedule / manual dispatch full matrix (no filter, catches anything that slipped through) full full safety net intact

Why this approach

GitHub's "Require status checks to pass" rule gates by check name. A workflow filtered out by paths: doesn't trigger → no check appears → required check stays "Expected" forever. The fix has to make every workflow trigger on every PR; then a cheap find-projects step decides whether there's any real work.

🤖 Generated with Claude Code

…ocs-only PRs

Required status checks gate on each named check actually reporting a
pass. A workflow that's been paths-filtered out doesn't trigger, so its
aggregator check never appears and the PR stays in "Expected — waiting
for status to be reported" forever. Drop the `paths:` filter from
`pull_request:` on every test workflow plus extract.yml so they always
trigger.

To keep CI cost flat (no full matrix on irrelevant PRs), make each
find-projects step compute the changed paths between PR base and head
via `git diff` and emit only the projects whose subtree was touched.
Docs-only and other-framework PRs produce an empty matrix → test job
skipped → aggregator passes via `result == 'skipped'` → required check
satisfied.

Workflow-file changes still trigger a full matrix for the affected
framework, so a PR editing test-js.yml exercises every JS sample.

push/schedule/workflow_dispatch events are unchanged — they run the
full matrix.

Also harden test-js's find with `-not -path "*/.angular/*"` to defend
against Angular's Vite dep cache leaking into the matrix if workflow
ordering ever changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ksroda-sa
ksroda-sa marked this pull request as ready for review May 13, 2026 12:11
@ksroda-sa
ksroda-sa requested a review from a team as a code owner May 13, 2026 12:11
Copilot AI review requested due to automatic review settings May 13, 2026 12:11
@ksroda-sa
ksroda-sa requested a review from a team as a code owner May 13, 2026 12:11

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Switches the JS / Java / iOS / .NET / Android test workflows and extract.yml from paths:-filtered triggers to always-trigger triggers, then narrows each workflow's matrix at runtime by diffing the PR's changed files against the discovered project list. The motivation is that GitHub's required-status-checks rule waits forever on a check name produced by a workflow that paths:-filtered itself out; making every required workflow always trigger (and producing an empty matrix → skipped job → green aggregator) avoids that stuck state.

Changes:

  • Drop paths: filters from pull_request (and the matching push filters) on five test-* workflows and extract.yml.
  • In each test-* workflow's find-projects step, checkout with fetch-depth: 0, compute changed paths via git diff BASE..HEAD, and emit a project only if some changed file is under it; the workflow file itself acts as an "escape hatch" that re-enables the full matrix.
  • Harden test-js's find with -not -path "*/.angular/*" to keep Angular's Vite dep cache out of the matrix.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
.github/workflows/test-js.yml Removes paths filter, adds path-aware find-projects with .angular exclude.
.github/workflows/test-java.yml Removes paths filter, adds path-aware find-projects.
.github/workflows/test-ios.yml Removes paths filter, adds path-aware find-projects.
.github/workflows/test-dotnet.yml Removes paths filter, adds path-aware find-projects.
.github/workflows/test-android.yml Removes paths filter, adds path-aware find-projects.
.github/workflows/extract.yml Removes pull_request paths filter so validate always reports.
Comments suppressed due to low confidence (4)

.github/workflows/test-js.yml:55

  • The matching uses grep -q "^${proj}/" where $proj is interpolated as a regex pattern. Project paths can legitimately contain regex metacharacters (e.g. ., +), and any future sample directory named like samples/node/express.js would have . match any character — causing both false positives (matching unrelated paths) and unanchored sub-string matches. Use a fixed-string match anchored with a leading separator, e.g. by piping CHANGED and using awk with a literal prefix check, or by escaping regex metacharacters in $proj before passing to grep. Same issue exists in test-java.yml, test-ios.yml, test-dotnet.yml, test-android.yml.
              FILTERED=$(while IFS= read -r proj; do
                [ -z "$proj" ] && continue
                if printf '%s\n' "$CHANGED" | grep -q "^${proj}/"; then
                  echo "$proj"
                fi
              done <<< "$ALL")

.github/workflows/test-js.yml:44

  • git diff --name-only "$BASE_SHA" "$HEAD_SHA" 2>/dev/null || echo "" silently swallows any failure (e.g. one of the SHAs not fetched, transient git error). The result is CHANGED="", which yields an empty matrix and the test job is skipped — the aggregator then reports green even though no test ran and no error surfaced. Failing closed (running the full matrix, or failing the find step) would be safer than silently passing. Same concern applies to test-java.yml, test-ios.yml, test-dotnet.yml, and test-android.yml.
          if [ "$EVENT_NAME" = "pull_request" ]; then
            CHANGED=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA" 2>/dev/null || echo "")

.github/workflows/test-js.yml:48

  • The WORKFLOW_FILE "escape hatch" only triggers a full matrix when the workflow file itself changes. PRs that modify shared dependencies the workflow relies on — for example a composite action under .github/actions/**, a reusable scripts/** helper invoked by the test job, root-level config like package.json / lockfiles that affect every sample, or shared workflow includes — will not retrigger any project's matrix and the test job will be skipped. Consider broadening this trigger (e.g. include .github/actions/**, top-level config files, or any non-samples/** path that the test job consumes) or documenting the assumption that no such shared inputs exist. Same applies to all five test-*.yml files.
            # If the workflow file itself changed, run the full matrix so we
            # exercise the workflow against every sample.
            if printf '%s\n' "$CHANGED" | grep -qFx "$WORKFLOW_FILE"; then
              FILTERED="$ALL"

.github/workflows/test-js.yml:62

  • The same ~20-line find-projects logic (checkout with fetch-depth: 0, env block, set -euo pipefail, find + diff + filter + jq matrix emit) is duplicated across all five test-*.yml workflows. Any fix to the regex-escaping issue, the silent-failure behavior, or the workflow-file escape hatch must be applied in five places and will drift over time. Consider extracting this into a composite action under .github/actions/find-changed-projects that takes the find pattern (and any extra excludes) as inputs and emits the matrix output. Each workflow then becomes ~5 lines.
      - id: find
        env:
          BASE_SHA: ${{ github.event.pull_request.base.sha }}
          HEAD_SHA: ${{ github.event.pull_request.head.sha }}
          EVENT_NAME: ${{ github.event_name }}
          WORKFLOW_FILE: .github/workflows/test-js.yml
        run: |
          set -euo pipefail
          # Exclude `.angular/cache/**/package.json` — Angular's Vite dep cache
          # writes nested package.json files inside .angular/cache after a build.
          # On a fresh CI checkout the cache doesn't exist yet, but the exclude
          # hardens us against any future ordering change that runs a build first.
          ALL=$(find samples -name "package.json" -not -path "*/node_modules/*" -not -path "*/.angular/*" -exec dirname {} \; 2>/dev/null | sort)

          if [ "$EVENT_NAME" = "pull_request" ]; then
            CHANGED=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA" 2>/dev/null || echo "")
            # If the workflow file itself changed, run the full matrix so we
            # exercise the workflow against every sample.
            if printf '%s\n' "$CHANGED" | grep -qFx "$WORKFLOW_FILE"; then
              FILTERED="$ALL"
            else
              FILTERED=$(while IFS= read -r proj; do
                [ -z "$proj" ] && continue
                if printf '%s\n' "$CHANGED" | grep -q "^${proj}/"; then
                  echo "$proj"
                fi
              done <<< "$ALL")
            fi
          else
            FILTERED="$ALL"
          fi

          MATRIX=$(printf '%s\n' "$FILTERED" | jq -R -s -c 'split("\n") | map(select(. != ""))')
          echo "matrix=$MATRIX" >> "$GITHUB_OUTPUT"

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .github/workflows/test-js.yml Outdated
ksroda-sa and others added 2 commits May 13, 2026 14:27
`fetch-depth: 0` clones the entire repo history on every workflow run
(5 test workflows × every PR + every push + every schedule). For
samples-heavy mono-repos this is real I/O wasted on metadata we don't
need — we only care about the PR's changed file list, not history.

Use `gh api /repos/.../pulls/N/files` instead. Single HTTP call against
the compare API, default shallow checkout suffices, no git history
fetched at all.

Also add `pull-requests: read` to the permissions block in each test
workflow so the auto-issued GITHUB_TOKEN has the scope `gh api` needs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After the path-aware filter in 58638e9, an RN dependabot bump touching
`samples/react-native/<flow>/package.json` no longer triggered the
RN Android `assembleDebug` build — the filter checked `^${proj}/` where
$proj is the gradlew dir (`samples/react-native/<flow>/android`), and
the package.json bump lives one directory up.

Compute a watch_root per project by walking up from gradlew until we
find a package.json:
  - For RN samples, watch_root becomes the RN sample root → JS-side
    changes that affect Android autolinking now correctly trigger
    the Android build.
  - For native Android samples, no package.json ancestor exists, so
    watch_root stays as the gradlew dir → behavior unchanged.

This mirrors the same walk-up the workflow's "Install JS dependencies
(RN samples)" step already does, keeping discovery and dependency-
installation in sync.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ksroda-sa
ksroda-sa merged commit f100770 into main May 14, 2026
36 checks passed
@ksroda-sa
ksroda-sa deleted the fix/path-aware-find-projects branch May 14, 2026 07:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants