1- name : E2E matrix
1+ name : Nightly E2E Matrix
22
33on :
4+ schedule :
5+ # Weekdays at 05:00 UTC
6+ - cron : 0 5 * * 1-5
7+ push :
8+ branches :
9+ - main
10+ workflow_dispatch :
411 pull_request :
512 types :
13+ - labeled
614 - opened
715 - reopened
816 - synchronize
9- merge_group :
10- push :
11- branches :
12- - main
13- workflow_dispatch :
1417
1518# Environment details:
16- # - renovate branches should never use Nx Cloud
19+ # - Nx Cloud disabled unless NX_E2E_USE_CLOUD repo variable is 'true' and branch is not renovate
20+ # - Full matrix runs nightly, on main pushes, manual triggers, or PRs labeled "full-e2e"
1721env :
1822 CDWR_DEBUG_LOGGING : true
1923 IS_RENOVATE : ${{ startsWith(github.head_ref || github.ref_name, 'renovate') }}
2024 NX_CLOUD_ACCESS_TOKEN : ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
21- NX_NO_CLOUD : ${{ startsWith(github.head_ref || github.ref_name, 'renovate') || vars.NX_NO_CLOUD }}
25+ NX_NO_CLOUD : ${{ startsWith(github.head_ref || github.ref_name, 'renovate') || vars.NX_E2E_USE_CLOUD != 'true' }}
2226 NX_DAEMON : ${{ vars.NX_DAEMON }}
2327 NX_PARALLEL : ${{ vars.NX_PARALLEL }}
2428 NX_VERBOSE_LOGGING : ${{ vars.NX_VERBOSE_LOGGING }}
@@ -31,21 +35,98 @@ jobs:
3135 init :
3236 runs-on : ubuntu-latest
3337 outputs :
34- e2e-enabled : ${{ steps.e2e-enabled.outputs.status }}
38+ e2e-enabled : ${{ steps.gate.outputs.run_status == 'true' && (steps.gate.outputs.reason == 'manual' || steps.affected.outputs.affected == 'true') }}
39+ projects : ${{ steps.affected.outputs.projects }}
40+ reason : ${{ steps.gate.outputs.reason }}
3541 steps :
36- # Disable e2e for
37- # - merge groups unless it's renovate
38- # - renovate pull requests
39- - name : Whether e2e is enabled
40- id : e2e-enabled
42+ - name : Determine matrix request
43+ id : gate
44+ env :
45+ EVENT_NAME : ${{ github.event_name }}
46+ GITHUB_REF : ${{ github.ref }}
47+ IS_RENOVATE : ${{ env.IS_RENOVATE }}
48+ PR_LABELS : ${{ toJson(github.event.pull_request.labels.*.name) }}
49+ run : |
50+ set -euo pipefail
51+
52+ run_status=false
53+ reason=not-requested
54+
55+ if [ "$IS_RENOVATE" = "true" ]; then
56+ run_status=false
57+ reason=renovate
58+ elif [ "$EVENT_NAME" = "schedule" ]; then
59+ run_status=true
60+ reason=schedule
61+ elif [ "$EVENT_NAME" = "workflow_dispatch" ]; then
62+ run_status=true
63+ reason=manual
64+ elif [ "$EVENT_NAME" = "push" ] && [ "$GITHUB_REF" = "refs/heads/main" ]; then
65+ run_status=true
66+ reason=push-main
67+ elif [ "$EVENT_NAME" = "pull_request" ]; then
68+ labels=${PR_LABELS:-[]}
69+ if printf '%s\n' "$labels" | grep -Fq '"full-e2e"'; then
70+ run_status=true
71+ reason=pr-label
72+ else
73+ run_status=false
74+ reason=pr-no-label
75+ fi
76+ fi
77+ echo "run_status = $run_status"
78+ echo "reason = $reason"
79+ echo "run_status=$run_status" >> "$GITHUB_OUTPUT"
80+ echo "reason=$reason" >> "$GITHUB_OUTPUT"
81+
82+ - uses : actions/checkout@v4
83+ if : steps.gate.outputs.run_status == 'true'
84+ with :
85+ fetch-depth : 0
86+
87+ - name : Install pnpm package manager
88+ if : steps.gate.outputs.run_status == 'true'
89+ uses : pnpm/action-setup@v4
90+
91+ - name : Setup Node.js
92+ if : steps.gate.outputs.run_status == 'true'
93+ uses : actions/setup-node@v4
94+ with :
95+ node-version : 20
96+ cache : pnpm
97+
98+ - name : Install dependencies
99+ if : steps.gate.outputs.run_status == 'true'
100+ run : pnpm install --frozen-lockfile --prefer-offline
101+
102+ - name : Resolve Nx base and head
103+ if : steps.gate.outputs.run_status == 'true'
104+ uses : nrwl/nx-set-shas@v4
105+ with :
106+ # For schedule: compare against last successful nightly run
107+ # For PR/push: normal base/head comparison
108+ last-successful-event : ${{ github.event_name == 'schedule' && 'schedule' || '' }}
109+ set-environment-variables-for-job : true
110+
111+ - name : Determine affected e2e targets
112+ if : steps.gate.outputs.run_status == 'true'
113+ id : affected
41114 run : |
42- if [[ "${{ github.event_name }}" == "merge_group" && "${{ env.IS_RENOVATE }}" == "false" ]]; then
43- echo "status=false" >> $GITHUB_OUTPUT
44- elif [[ "${{ github.event_name }}" == "pull_request" && "${{ env.IS_RENOVATE }}" == "true" ]]; then
45- echo "status=false" >> $GITHUB_OUTPUT
115+ set -euo pipefail
116+
117+ cmd=(pnpm nx show projects --affected --with-target=e2e)
118+ projects="$("${cmd[@]}")"
119+ projects_list="$(printf '%s' "$projects" | tr -d '\r')"
120+
121+ if [ -n "$projects" ]; then
122+ affected=true
46123 else
47- echo "status=true" >> $GITHUB_OUTPUT
124+ affected=false
48125 fi
126+ echo Affected projects: "$projects_list"
127+
128+ echo "affected=$affected" >> "$GITHUB_OUTPUT"
129+ echo "projects=$projects_list" >> "$GITHUB_OUTPUT"
49130
50131 preinstall :
51132 needs : init
@@ -74,15 +155,34 @@ jobs:
74155 id : cache-modules
75156 with :
76157 lookup-only : true
77- path : ' **/node_modules'
158+ path : " **/node_modules"
78159 key : ${{ matrix.os }}-modules-${{ matrix.node }}-${{ github.run_id }}
79160
80161 - if : steps.cache-modules.outputs.cache-hit != 'true'
81162 run : pnpm install --frozen-lockfile
82163
164+ - name : Cache Playwright browsers
165+ uses : actions/cache@v4
166+ id : cache-playwright
167+ with :
168+ path : ~/.cache/ms-playwright
169+ key : playwright-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
170+ restore-keys : |
171+ playwright-${{ runner.os }}-
172+
173+ - name : Install Playwright browsers
174+ if : steps.cache-playwright.outputs.cache-hit != 'true'
175+ run : pnpm exec playwright install
176+
177+ - name : Install Playwright system dependencies
178+ if : matrix.os == 'ubuntu-latest' && steps.cache-playwright.outputs.cache-hit != 'true'
179+ run : pnpm exec playwright install-deps
180+
83181 e2e :
84182 needs : [init, preinstall]
85- if : always()
183+ if : needs.init.outputs.e2e-enabled == 'true'
184+ env :
185+ projects : ${{ needs.init.outputs.projects }}
86186 permissions :
87187 contents : read
88188 runs-on : ${{ matrix.os }}
92192 os : [ubuntu-latest, macos-latest, windows-latest]
93193 node : [20]
94194 pm : [npm, pnpm, yarn]
95- project : [nx-payload-e2e]
96195 include :
97196 - os : ubuntu-latest
98197 name : Linux
@@ -118,46 +217,39 @@ jobs:
118217 pm : yarn
119218 fail-fast : false
120219
121- name : E2E ${{ matrix.os }}/${{ matrix.pm }}/${{ matrix.node }} ${{ join(matrix.project) }}
220+ name : E2E ${{ matrix.os }}/${{ matrix.pm }}/${{ matrix.node }}
122221 steps :
123222 - uses : actions/checkout@v4
124- if : needs.init.outputs.e2e-enabled == 'true'
125223 with :
126224 fetch-depth : 0
127225
128226 - name : Install pnpm package manager
129- if : needs.init.outputs.e2e-enabled == 'true'
130227 uses : pnpm/action-setup@v4
131228
132229 - uses : actions/setup-node@v4
133- if : needs.init.outputs.e2e-enabled == 'true'
134230 with :
135231 node-version : ${{ matrix.node }}
136232 cache : pnpm
137233
138234 - uses : actions/cache@v4
139- if : needs.init.outputs.e2e-enabled == 'true'
140235 id : cache-modules
141236 with :
142- path : ' **/node_modules'
237+ path : " **/node_modules"
143238 key : ${{ matrix.os }}-modules-${{ matrix.node }}-${{ github.run_id }}
144239
145240 - name : Install dependencies
146- if : needs.init.outputs.e2e-enabled == 'true'
147241 run : pnpm install --frozen-lockfile
148242
149243 - name : Install Playwright browser dependencies
150- if : needs.init.outputs.e2e-enabled == 'true' && matrix.name == 'Linux'
151- run : npx playwright install-deps
244+ if : matrix.name == 'Linux'
245+ run : pnpm exec playwright install-deps
152246
153247 - name : Analyze affected projects
154- if : needs.init.outputs.e2e-enabled == 'true'
155248 uses : nrwl/nx-set-shas@v4
156249
157- - name : Run e2e tests
158- if : needs.init.outputs.e2e-enabled == 'true'
250+ - name : Run affected e2e tests
159251 id : e2e-run
160- run : pnpm nx e2e ${{ matrix.project }} -c ${{ matrix.configuration }}
252+ run : pnpm nx run-many -t e2e --projects ${{ env.projects }} -c ${{ matrix.configuration }}
161253 timeout-minutes : ${{ matrix.timeout }}
162254 env :
163255 CDWR_E2E_PACKAGE_MANAGER : ${{ matrix.pm }}
@@ -175,61 +267,79 @@ jobs:
175267 else
176268 status="skipped"
177269 fi
178- echo '{"project": "${{ matrix.project }}", " os": "${{ matrix.os }}", "pm": "${{ matrix.pm }}", "node": "${{ matrix.node }}", "configuration": "${{ matrix.configuration }}", "status": "'"$status"'"}' > results-${{ matrix.project }} -${{ matrix.os }}-${{ matrix.pm }}-${{ matrix.node }}.json
270+ echo '{"os": "${{ matrix.os }}", "pm": "${{ matrix.pm }}", "node": "${{ matrix.node }}", "configuration": "${{ matrix.configuration }}", "status": "'"$status"'"}' > results-${{ matrix.os }}-${{ matrix.pm }}-${{ matrix.node }}.json
179271 shell : bash
180272
181273 - name : Upload test results
182274 if : always()
183275 uses : actions/upload-artifact@v4
184276 with :
185- name : results-${{ matrix.project }}-${{ matrix. os }}-${{ matrix.pm }}-${{ matrix.node }}
186- path : results-${{ matrix.project }}-${{ matrix. os }}-${{ matrix.pm }}-${{ matrix.node }}.json
277+ name : results-${{ matrix.os }}-${{ matrix.pm }}-${{ matrix.node }}
278+ path : results-${{ matrix.os }}-${{ matrix.pm }}-${{ matrix.node }}.json
187279
188280 summary :
189281 name : E2E Test Summary
190- needs : e2e
282+ needs : [init, e2e]
191283 runs-on : ubuntu-latest
192- if : always()
284+ if : always() && needs.init.result != 'cancelled'
193285 steps :
194286 - name : Download all workflow run artifacts
287+ if : needs.e2e.result != 'skipped'
195288 uses : actions/download-artifact@v4
196289 with :
197290 path : artifacts
198291 pattern : results-*
199292 merge-multiple : true
200293 - name : Create summary
294+ env :
295+ REASON : ${{ needs.init.outputs.reason }}
296+ PROJECTS : ${{ needs.init.outputs.projects }}
297+ E2E_ENABLED : ${{ needs.init.outputs.e2e-enabled }}
298+ E2E_RESULT : ${{ needs.e2e.result }}
201299 run : |
202300 echo "# E2E Test Results Summary" > summary.md
203301 echo "" >> summary.md
204- echo "| Project | OS | Package Manager | Node Version | Configuration | Status |" >> summary.md
205- echo "| ------- | -- | --------------- | ------------ | ------------- | ------ |" >> summary.md
206- for artifact in artifacts/*.json; do
207- if [ -f "$artifact" ]; then
208- project=$(jq -r '.project' "$artifact")
209- os=$(jq -r '.os' "$artifact")
210- pm=$(jq -r '.pm' "$artifact")
211- configuration=$(jq -r '.configuration' "$artifact")
212- node=$(jq -r '.node' "$artifact")
213- status=$(jq -r '.status' "$artifact")
214- echo "| $project | $os | $pm | $node | $configuration | $status |" >> summary.md
215- fi
216- done
302+ echo "**Trigger:** \`$REASON\`" >> summary.md
217303
218- echo "" >> summary.md
219- echo "## Detailed Results" >> summary.md
220- echo "" >> summary.md
221- echo "<details><summary>Click to expand</summary>" >> summary.md
222- for artifact in artifacts/*.json; do
223- echo "$(basename $artifact)" >> summary.md
304+ if [ "$E2E_ENABLED" != "true" ]; then
224305 echo "" >> summary.md
225- echo '```json' >> summary.md
306+ echo "⏭️ **E2E tests skipped** - no affected projects with e2e targets" >> summary.md
307+ elif [ "$E2E_RESULT" = "skipped" ]; then
226308 echo "" >> summary.md
227- jq -s '.' $artifact >> summary.md
309+ echo "⏭️ **E2E tests skipped**" >> summary.md
310+ else
311+ if [ -n "$PROJECTS" ]; then
312+ echo "**Affected projects:** \`$PROJECTS\`" >> summary.md
313+ fi
228314 echo "" >> summary.md
229- echo '```' >> summary.md
315+ echo "| OS | Package Manager | Node Version | Configuration | Status |" >> summary.md
316+ echo "| -- | --------------- | ------------ | ------------- | ------ |" >> summary.md
317+ for artifact in artifacts/*.json; do
318+ if [ -f "$artifact" ]; then
319+ os=$(jq -r '.os' "$artifact")
320+ pm=$(jq -r '.pm' "$artifact")
321+ configuration=$(jq -r '.configuration' "$artifact")
322+ node=$(jq -r '.node' "$artifact")
323+ status=$(jq -r '.status' "$artifact")
324+ echo "| $os | $pm | $node | $configuration | $status |" >> summary.md
325+ fi
326+ done
230327 echo "" >> summary.md
231- done
232- echo "</details>" >> summary.md
328+ echo "## Detailed Results" >> summary.md
329+ echo "" >> summary.md
330+ echo "<details><summary>Click to expand</summary>" >> summary.md
331+ for artifact in artifacts/*.json; do
332+ echo "$(basename $artifact)" >> summary.md
333+ echo "" >> summary.md
334+ echo '```json' >> summary.md
335+ echo "" >> summary.md
336+ jq -s '.' $artifact >> summary.md
337+ echo "" >> summary.md
338+ echo '```' >> summary.md
339+ echo "" >> summary.md
340+ done
341+ echo "</details>" >> summary.md
342+ fi
233343
234344 - name : Upload summary artifact
235345 uses : actions/upload-artifact@v4
0 commit comments