66 - next
77 pull_request :
88 types : [opened, synchronize, labeled, reopened]
9+ schedule :
10+ - cron : ' 0 23 * * *'
911
1012permissions :
1113 actions : read
1214 contents : read
15+ statuses : write
1316
1417env :
1518 NX_CLOUD_ACCESS_TOKEN : ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
2124 (contains(github.event.pull_request.labels.*.name, 'ci:normal') ||
2225 contains(github.event.pull_request.labels.*.name, 'ci:merged') ||
2326 contains(github.event.pull_request.labels.*.name, 'ci:daily'))
24- ) || (github.event_name == 'push' && github.ref == 'refs/heads/next')
27+ ) || (github.event_name == 'push' && github.ref == 'refs/heads/next') ||
28+ (github.event_name == 'schedule')
29+
2530 runs-on : ubuntu-latest
2631 env :
2732 ALL_TASKS : compile,check,knip,test,pretty-docs,lint,sandbox,build,e2e-tests,e2e-tests-dev,test-runner,vitest-integration,check-sandbox,e2e-ui,jest,vitest,playwright-ct,cypress
@@ -30,26 +35,115 @@ jobs:
3035 with :
3136 filter : tree:0
3237 fetch-depth : 0
38+ - name : Set Nx tag(s)
39+ id : tag
40+ run : |
41+ tags="normal"
42+ if [[ "${{ github.event_name }}" == "pull_request" ]]; then
43+ if [[ "${{ contains(github.event.pull_request.labels.*.name, 'ci:merged') }}" == "true" ]]; then
44+ tags="merged"
45+ fi
46+ if [[ "${{ contains(github.event.pull_request.labels.*.name, 'ci:daily') }}" == "true" ]]; then
47+ tags="daily"
48+ fi
49+ fi
50+
51+ if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" == "refs/heads/next" ]]; then
52+ tags="merged"
53+ fi
54+
55+ if [[ "${{ github.event_name }}" == "schedule" ]]; then
56+ tags="daily"
57+ fi
58+
59+ echo "tag=$tags" >> "$GITHUB_OUTPUT"
3360 - run : npx nx@latest start-ci-run --distribute-on="./.nx/workflows/distribution-config.yaml" --stop-agents-after="$ALL_TASKS"
61+ - name : Create Nx Cloud Status (pending)
62+ uses : actions/github-script@v7
63+ with :
64+ script : |
65+ const tag = ${{ toJson(steps.tag.outputs.tag) }} || 'normal';
66+
67+ await github.rest.repos.createCommitStatus({
68+ owner: context.repo.owner,
69+ repo: context.repo.repo,
70+ sha: context.payload.pull_request?.head?.sha ?? context.sha,
71+ state: 'pending',
72+ target_url: `https://cloud.nx.app/orgs/606dcb5cdc2a2b00059cc0e9/workspaces/6929fbef73e98d8094d2a343/overview?branch=${
73+ context.payload.pull_request?.number ?? 'next'
74+ }`,
75+ description: 'NX Cloud is running your tests',
76+ context: `nx: ${tag}`,
77+ });
3478 - uses : actions/setup-node@v4
3579 with :
3680 node-version : 22
3781 cache : ' yarn'
3882 - run : yarn install --immutable
3983 - uses : nrwl/nx-set-shas@v4
84+ - id : nx
85+ name : ' Run nx'
86+ run : |
87+ echo 'nx_output<<EOF' >> "$GITHUB_OUTPUT"
88+ yarn nx run-many -t $ALL_TASKS -c production -p="tag:library,tag:ci:${{ steps.tag.outputs.tag }}" | tee -a "$GITHUB_OUTPUT"
89+ status=${PIPESTATUS[0]}
90+ echo 'EOF' >> "$GITHUB_OUTPUT"
91+ exit $status
92+
93+ - name : Create per-task Nx statuses
94+ if : always()
95+ uses : actions/github-script@v7
96+ with :
97+ github-token : ${{ secrets.GITHUB_TOKEN }}
98+ script : |
99+ const raw = ${{ toJson(steps.nx.outputs.nx_output) }} || '';
100+ const tag = ${{ toJson(steps.tag.outputs.tag) }} || '';
101+ const lines = raw.split('\n');
102+ const failures = [];
103+
104+ for (const [i, line] of lines.entries()) {
105+ if (!line.includes('✖')) continue;
106+
107+ const task =
108+ line.match(/✖\s+([^│]+?)\s{2,}/)?.[1].trim() ||
109+ 'Unknown Nx task';
110+
111+ const url = lines
112+ .slice(i + 1, i + 6)
113+ .find(l => l.includes('Task logs:'))
114+ ?.match(/Task logs:\s*(https:\/\/cloud\.nx\.app\/logs\/\S+)/)?.[1];
115+
116+ failures.push({ task, url });
117+ }
118+
119+ const sha = context.payload.pull_request?.head?.sha ?? context.sha;
120+
121+ // Per-task statuses (max 5)
122+ for (const { task, url } of failures.slice(0, 5)) {
123+ await github.rest.repos.createCommitStatus({
124+ owner: context.repo.owner,
125+ repo: context.repo.repo,
126+ sha,
127+ state: 'failure',
128+ target_url: url ?? undefined,
129+ context: `nx run ${task}`,
130+ description: 'Your test failed on NX Cloud',
131+ });
132+ }
133+
134+ const runMatches = raw.match(/https:\/\/cloud\.nx\.app\/runs\/\S+/g);
135+ const nxCloudUrl = runMatches ? runMatches[runMatches.length - 1] : undefined;
136+
137+ const failedCount = failures.length;
40138
41- # --- PRs ---
42- - if : github.event_name == 'pull_request' &&
43- contains(github.event.pull_request.labels.*.name, 'ci:normal')
44- run : yarn nx run-many -t $ALL_TASKS -c production -p="tag:ci:normal"
45- - if : github.event_name == 'pull_request' &&
46- contains(github.event.pull_request.labels.*.name, 'ci:merged')
47- run : yarn nx run-many -t $ALL_TASKS -c production -p="tag:ci:normal,tag:ci:merged"
48- - if : github.event_name == 'pull_request' &&
49- contains(github.event.pull_request.labels.*.name, 'ci:daily')
50- run : yarn nx run-many -t $ALL_TASKS -c production -p="tag:ci:normal,tag:ci:merged,tag:ci:daily"
51-
52- # --- Pushes ---
53- - if : github.event_name == 'push' &&
54- github.ref == 'refs/heads/next'
55- run : yarn nx run-many -t $ALL_TASKS -c production -p="tag:ci:normal,tag:ci:merged"
139+ await github.rest.repos.createCommitStatus({
140+ owner: context.repo.owner,
141+ repo: context.repo.repo,
142+ sha,
143+ state: failedCount ? 'failure' : 'success',
144+ target_url: nxCloudUrl,
145+ description: failedCount
146+ ? `Nx Cloud run failed (${failedCount} tasks failed)`
147+ : 'Nx Cloud run finished successfully',
148+ context: `nx: ${tag}`,
149+ });
0 commit comments