Build: Add NX check #678
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: nx | |
| on: | |
| push: | |
| branches: | |
| - next | |
| pull_request: | |
| types: [opened, synchronize, labeled, reopened] | |
| permissions: | |
| actions: read | |
| contents: read | |
| statuses: write | |
| env: | |
| NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} | |
| jobs: | |
| nx: | |
| if: > | |
| (github.event_name == 'pull_request' && | |
| (contains(github.event.pull_request.labels.*.name, 'ci:normal') || | |
| contains(github.event.pull_request.labels.*.name, 'ci:merged') || | |
| contains(github.event.pull_request.labels.*.name, 'ci:daily')) | |
| ) || (github.event_name == 'push' && github.ref == 'refs/heads/next') | |
| runs-on: ubuntu-latest | |
| env: | |
| 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 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| filter: tree:0 | |
| fetch-depth: 0 | |
| - run: npx nx@latest start-ci-run --distribute-on="./.nx/workflows/distribution-config.yaml" --stop-agents-after="$ALL_TASKS" | |
| - name: Create Nx Cloud Status (pending) | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const pr = context.payload.pull_request; | |
| const isPr = !!pr; | |
| const sha = isPr ? pr.head.sha : context.sha; | |
| const branchParam = isPr | |
| ? pr.number | |
| : (context.ref.startsWith('refs/heads/') | |
| ? context.ref.slice('refs/heads/'.length) | |
| : context.ref); | |
| await github.rest.repos.createCommitStatus({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| sha: context.payload.pull_request.head.sha, | |
| state: 'pending', | |
| target_url: `https://cloud.nx.app/orgs/606dcb5cdc2a2b00059cc0e9/workspaces/6929fbef73e98d8094d2a343/overview?branch=${prNumber}`, | |
| description: 'NX is running your tests', | |
| context: 'Nx Cloud' | |
| }); | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| cache: 'yarn' | |
| - run: yarn install --immutable | |
| - uses: nrwl/nx-set-shas@v4 | |
| # --- PRs --- | |
| - if: github.event_name == 'pull_request' && | |
| contains(github.event.pull_request.labels.*.name, 'ci:normal') | |
| id: tag | |
| name: 'Set tag to ci:normal' | |
| run: echo "tag=tag:ci:normal" >> $GITHUB_OUTPUT | |
| # - if: github.event_name == 'pull_request' && | |
| # contains(github.event.pull_request.labels.*.name, 'ci:merged') | |
| # id: tag | |
| # name: 'Set tag to ci:merged' | |
| # run: echo "tag=tag:ci:normal,tag:ci:merged" >> $GITHUB_OUTPUT | |
| # - if: github.event_name == 'pull_request' && | |
| # contains(github.event.pull_request.labels.*.name, 'ci:daily') | |
| # id: tag | |
| # name: 'Set tag to ci:daily' | |
| # run: echo "tag=tag:ci:normal,tag:ci:merged,tag:ci:daily" >> $GITHUB_OUTPUT | |
| # - if: github.event_name == 'push' && github.ref == 'refs/heads/next' | |
| # id: tag | |
| # name: 'Set tag to ci:merged' | |
| # run: echo "tag=tag:ci:normal,tag:ci:merged" >> $GITHUB_OUTPUT | |
| - id: nx | |
| name: 'Run nx' | |
| run: | | |
| echo 'nx_output<<EOF' >> "$GITHUB_OUTPUT" | |
| yarn nx run-many -t $ALL_TASKS -c production -p="${{ steps.tag.outputs.tag }}" | tee -a "$GITHUB_OUTPUT" | |
| status=${PIPESTATUS[0]} | |
| echo 'EOF' >> "$GITHUB_OUTPUT" | |
| exit $status | |
| - name: Create per-task Nx statuses | |
| if: always() | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const raw = ${{ toJson(steps.nx.outputs.nx_output) }}; | |
| if (!raw) { | |
| core.info('No nx_output found, skipping per-task statuses.'); | |
| return; | |
| } | |
| const lines = raw.split('\n'); | |
| const failures = []; | |
| for (let i = 0; i < lines.length; i++) { | |
| const line = lines[i]; | |
| if (!line.includes('✖')) continue; | |
| // Example: | |
| // │ ✖ vue3-vite/default-ts:test-runner:production 58s Cache Miss │ | |
| const taskMatch = line.match(/✖\s+([^│]+?)\s{2,}/); | |
| const task = taskMatch ? taskMatch[1].trim() : 'Unknown Nx task'; | |
| // Look ahead for "> Task logs: https://cloud.nx.app/logs/..." | |
| let url = null; | |
| for (let j = i + 1; j < Math.min(i + 6, lines.length); j++) { | |
| const logLine = lines[j]; | |
| const urlMatch = logLine.match(/Task logs:\s*(https:\/\/cloud\.nx\.app\/logs\/\S+)/); | |
| if (urlMatch) { | |
| url = urlMatch[1]; | |
| break; | |
| } | |
| } | |
| failures.push({ task, url }); | |
| } | |
| if (!failures.length) { | |
| core.info('No failing Nx tasks detected in output.'); | |
| return; | |
| } | |
| const owner = context.repo.owner; | |
| const repo = context.repo.repo; | |
| const sha = | |
| context.payload.pull_request?.head?.sha || | |
| context.sha; | |
| core.info(`Creating statuses for ${failures.length} failing Nx tasks...`); | |
| for (const { task, url } of failures.slice(0, 5)) { | |
| const contextName = `nx run ${task}`; | |
| const description = ` Your tests failed on NX Cloud`; | |
| core.info(`Status for "${task}" -> ${url || 'no URL'}`); | |
| await github.rest.repos.createCommitStatus({ | |
| owner, | |
| repo, | |
| sha, | |
| state: 'failure', | |
| target_url: url || undefined, | |
| context: contextName, | |
| description, | |
| }); | |
| } | |
| - name: Finalize Nx Cloud Status | |
| if: always() | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const raw = ${{ toJson(steps.nx.outputs.nx_output) }} || ''; | |
| // Try to find a direct run URL in the Nx output | |
| let runUrl = null; | |
| const runMatches = raw.match(/https:\/\/cloud\.nx\.app\/runs\/\S+/g); | |
| if (runMatches && runMatches.length > 0) { | |
| // Use the last run URL in the output | |
| runUrl = runMatches[runMatches.length - 1]; | |
| } | |
| const prNumber = context.payload.pull_request.number; | |
| const overviewUrl = | |
| prNumber ? `https://cloud.nx.app/orgs/606dcb5cdc2a2b00059cc0e9/workspaces/6929fbef73e98d8094d2a343/overview?branch=${prNumber}` : ''; | |
| const nxCloudUrl = runUrl || overviewUrl; | |
| const jobStatus = '${{ job.status }}'; // 'success', 'failure', 'cancelled', 'skipped' | |
| /** Map job.status -> GitHub Statuses API state */ | |
| const stateMap = { | |
| success: 'success', | |
| failure: 'failure', | |
| cancelled: 'error', | |
| skipped: 'error' | |
| }; | |
| const state = stateMap[jobStatus] ?? 'error'; | |
| await github.rest.repos.createCommitStatus({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| sha: context.payload.pull_request.head.sha, | |
| state, | |
| target_url: nxCloudUrl, | |
| description: state === 'success' | |
| ? 'Nx Cloud run finished successfully' | |
| : `Nx Cloud run ended with status: ${jobStatus}`, | |
| context: 'Nx Cloud' | |
| }); |