diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 19e3514aed..61bfc86e20 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,11 +48,14 @@ jobs: HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} run: node tools/ci/classifyToolchains.mjs + # Always call Node so required `Node / *` checks are reported. Jobs inside + # skip when Node is unaffected; GitHub treats skipped jobs as passing. node: name: Node needs: changes - if: needs.changes.outputs.node == 'true' uses: ./.github/workflows/node.yml + with: + affected: ${{ needs.changes.outputs.node == 'true' }} android: name: Android diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml index f882ba22e3..a0ce78f05d 100644 --- a/.github/workflows/node.yml +++ b/.github/workflows/node.yml @@ -2,7 +2,17 @@ name: Node on: workflow_call: + inputs: + affected: + description: When false, jobs skip so required Node checks still report a passing status + type: boolean + default: true workflow_dispatch: + inputs: + affected: + description: When false, jobs skip so required Node checks still report a passing status + type: boolean + default: true concurrency: group: Node-${{github.ref_name}}-${{github.event_name == 'pull_request' && github.event.pull_request.number || github.sha}} @@ -20,6 +30,7 @@ env: jobs: validate: name: Validate + if: ${{ inputs.affected == true }} runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) @@ -43,6 +54,7 @@ jobs: lint: name: Lint + if: ${{ inputs.affected == true }} runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) @@ -63,6 +75,7 @@ jobs: format: name: Format + if: ${{ inputs.affected == true }} runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) @@ -78,6 +91,7 @@ jobs: test: name: Test + if: ${{ inputs.affected == true }} runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) @@ -93,6 +107,7 @@ jobs: typecheck: name: Typecheck + if: ${{ inputs.affected == true }} runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) @@ -108,6 +123,7 @@ jobs: build: name: Build + if: ${{ inputs.affected == true }} runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) @@ -125,7 +141,7 @@ jobs: name: Depcheck runs-on: ubuntu-latest # Only run on master pushes (to cache baseline) or PRs targeting master (to compare) - if: github.ref_name == 'master' || (github.event_name == 'pull_request' && github.base_ref == 'master') + if: ${{ inputs.affected == true && (github.ref_name == 'master' || (github.event_name == 'pull_request' && github.base_ref == 'master')) }} steps: - name: Harden the runner (Audit all outbound calls) uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 @@ -171,7 +187,7 @@ jobs: name: Bundle Stats runs-on: ubuntu-latest # Only run on master pushes (to cache baseline) or PRs targeting master (to compare) - if: github.ref_name == 'master' || (github.event_name == 'pull_request' && github.base_ref == 'master') + if: ${{ inputs.affected == true && (github.ref_name == 'master' || (github.event_name == 'pull_request' && github.base_ref == 'master')) }} steps: - name: Harden the runner (Audit all outbound calls) uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 @@ -220,6 +236,7 @@ jobs: lint-styles: name: Lint Styles + if: ${{ inputs.affected == true }} runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) diff --git a/docs/ci.md b/docs/ci.md index 3e70964c2a..fc7fad69e7 100644 --- a/docs/ci.md +++ b/docs/ci.md @@ -1,7 +1,8 @@ # CI architecture [`.github/workflows/ci.yml`](../.github/workflows/ci.yml) is the pull-request orchestrator. It -classifies changed paths by toolchain, then starts only the required lanes. +classifies changed paths by toolchain, then starts the native lanes that need to run. The Node +lane is always started so required checks are reported; its jobs skip when Node is unaffected. ## Toolchain tags @@ -18,8 +19,10 @@ product surface, but CI scheduling is based on the toolchain classification. The orchestrator determines whether Node, Gradle, or Xcode paths changed: -- Node changes call the reusable [Node workflow](../.github/workflows/node.yml). Its Linux jobs use - `nx affected` plus `toolchain:node`, so only affected Node projects with the requested target run. +- The [Node workflow](../.github/workflows/node.yml) is always called so required `Node / *` + checks are reported. When Node paths changed, its Linux jobs use `nx affected` plus + `toolchain:node`. When they did not, each Node job is skipped; GitHub treats skipped jobs as + passing, so native-only PRs are not blocked waiting for Node. - Gradle changes call the reusable [Android workflow](../.github/workflows/android.yml), which builds and tests the Android library and demo app with JDK 21 and the Android SDK. - Xcode changes call the reusable [iOS workflow](../.github/workflows/ios.yml), which builds @@ -30,8 +33,8 @@ This keeps toolchains isolated while preserving dependency-aware validation: - A web-only change does not run React Native, Android, or iOS work. - A change to a shared Node dependency may affect multiple dependent Node projects, such as both web and React Native packages. -- An Android-only change runs the Gradle lane without Node or Xcode jobs. -- An iOS-only change runs the Xcode lane without Node or Gradle jobs. +- An Android-only change runs the Gradle and skipped-Node lanes without Xcode jobs. +- An iOS-only change runs the Xcode and skipped-Node lanes without Gradle jobs. - Changes to centralized CI or Nx classification files safely enable all toolchains. Manually dispatching `ci.yml` enables every toolchain. The reusable Android and iOS workflows can diff --git a/docs/nx.md b/docs/nx.md index e941a443d8..f9b3970640 100644 --- a/docs/nx.md +++ b/docs/nx.md @@ -13,8 +13,8 @@ executor does not solve the problem because both Node and native projects primar Nx 20 cannot use the tags to select `targetDefaults`, but CI already depends on them: -1. The change classifier maps a changed project root to its `toolchain:*` tag and selects the Node, - Gradle, or Xcode workflow. +1. The change classifier maps a changed project root to its `toolchain:*` tag and decides which + of the Node, Gradle, or Xcode workflows should execute work. 2. The Node workflow positively filters `nx affected` to `toolchain:node`, preventing native `build` and `test` targets from running on Node-only Linux jobs. 3. The validator rejects missing or conflicting tags so a new project cannot silently enter the