From 9501263ec71ed67f5279f4087e8f96a9804cb803 Mon Sep 17 00:00:00 2001 From: Marcel Menk Date: Mon, 15 Jun 2026 19:59:18 +0200 Subject: [PATCH] fix: ci silent fails --- .github/actions/detect-affected/action.yml | 32 ++++++ .github/workflows/pull-request-checks.yml | 120 +++++++++++++++------ .github/workflows/release.yml | 83 +++++++++++--- tools/ci/build-windows-installer.ps1 | 6 ++ tools/ci/detect-affected.sh | 46 ++++++++ 5 files changed, 240 insertions(+), 47 deletions(-) create mode 100644 .github/actions/detect-affected/action.yml create mode 100755 tools/ci/detect-affected.sh diff --git a/.github/actions/detect-affected/action.yml b/.github/actions/detect-affected/action.yml new file mode 100644 index 000000000..f01554f2a --- /dev/null +++ b/.github/actions/detect-affected/action.yml @@ -0,0 +1,32 @@ +name: Detect affected Nx projects +description: List affected projects and expose outputs for skipping nx affected when nothing changed. + +outputs: + has_affected: + description: True when at least one project is affected + value: ${{ steps.detect.outputs.has_affected }} + projects: + description: Newline-separated list of affected project names + value: ${{ steps.detect.outputs.projects }} + has_test: + description: True when at least one affected project has a test target + value: ${{ steps.detect.outputs.has_test }} + has_e2e: + description: True when at least one affected project has an e2e target + value: ${{ steps.detect.outputs.has_e2e }} + has_lint: + description: True when at least one affected project has a lint target + value: ${{ steps.detect.outputs.has_lint }} + has_build: + description: True when at least one affected project has a build target + value: ${{ steps.detect.outputs.has_build }} + +runs: + using: composite + steps: + - name: Detect affected projects + id: detect + shell: bash + run: | + chmod +x tools/ci/detect-affected.sh + tools/ci/detect-affected.sh diff --git a/.github/workflows/pull-request-checks.yml b/.github/workflows/pull-request-checks.yml index 94433c7ae..11dbf78a6 100644 --- a/.github/workflows/pull-request-checks.yml +++ b/.github/workflows/pull-request-checks.yml @@ -9,6 +9,32 @@ permissions: packages: read jobs: + detect-affected: + name: Detect Affected Projects + runs-on: ubuntu-latest + outputs: + has_affected: ${{ steps.detect-affected.outputs.has_affected }} + projects: ${{ steps.detect-affected.outputs.projects }} + has_test: ${{ steps.detect-affected.outputs.has_test }} + has_e2e: ${{ steps.detect-affected.outputs.has_e2e }} + has_lint: ${{ steps.detect-affected.outputs.has_lint }} + has_build: ${{ steps.detect-affected.outputs.has_build }} + steps: + - name: Checkout code + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Set Nx SHA + uses: nrwl/nx-set-shas@v5 + + - name: Setup environment + uses: ./.github/actions/setup-env + + - name: Detect affected projects + id: detect-affected + uses: ./.github/actions/detect-affected + lint_pull_request: name: Pull Request Lint runs-on: ubuntu-latest @@ -52,6 +78,7 @@ jobs: check: name: Format Check runs-on: ubuntu-latest + needs: detect-affected steps: - name: Checkout code uses: actions/checkout@v5 @@ -68,11 +95,18 @@ jobs: run: npx nx format:check - name: Nx Lint - Check - run: npx nx affected --target=lint --parallel=3 || echo "No affected projects for lint" + if: needs.detect-affected.outputs.has_lint == 'true' + run: npx nx affected --target=lint --parallel=3 + + - name: Skip lint (no affected projects) + if: needs.detect-affected.outputs.has_lint != 'true' + run: echo "No affected projects with lint target; skipping nx affected lint." test: name: Test runs-on: ubuntu-latest + needs: detect-affected + if: needs.detect-affected.outputs.has_affected == 'true' permissions: contents: read actions: read @@ -91,20 +125,8 @@ jobs: - name: Setup environment uses: ./.github/actions/setup-env - - name: Detect affected projects with test target - id: affected - shell: bash - run: | - set -euo pipefail - RAW=$(npx nx show projects --affected --with-target=test 2>/dev/null | sed '/^\s*$/d' || true) - if [ -n "$RAW" ]; then - echo "has=true" >> "$GITHUB_OUTPUT" - else - echo "has=false" >> "$GITHUB_OUTPUT" - fi - - name: Collect base-branch coverage (for PR diff) - if: github.event_name == 'pull_request' && steps.affected.outputs.has == 'true' + if: github.event_name == 'pull_request' && needs.detect-affected.outputs.has_test == 'true' continue-on-error: true shell: bash run: | @@ -113,7 +135,7 @@ jobs: git fetch origin "${{ github.base_ref }}" --depth=1 git checkout "${{ github.event.pull_request.base.sha }}" rm -rf coverage coverage-base - npx nx affected --target=test --parallel=3 --configuration=ci --skip-nx-cache || true + npx nx affected --target=test --parallel=3 --configuration=ci --skip-nx-cache if [ -d coverage ]; then mv coverage coverage-base else @@ -122,18 +144,23 @@ jobs: git checkout "$PR_SHA" - name: Ensure coverage-base exists for diff comment - if: github.event_name == 'pull_request' && steps.affected.outputs.has != 'true' + if: github.event_name == 'pull_request' && needs.detect-affected.outputs.has_test != 'true' run: mkdir -p coverage-base - name: Run unit tests - run: npx nx affected --target=test --parallel=3 --configuration=ci --skip-nx-cache || echo "No affected projects for test" + if: needs.detect-affected.outputs.has_test == 'true' + run: npx nx affected --target=test --parallel=3 --configuration=ci --skip-nx-cache + + - name: Skip unit tests (no affected projects with test target) + if: needs.detect-affected.outputs.has_test != 'true' + run: echo "No affected projects with test target; skipping unit tests." - name: Report Nx coverage on PR if: always() && github.event_name == 'pull_request' uses: dkhunt27/action-nx-code-coverage@v3 with: github-token: ${{ secrets.GITHUB_TOKEN }} - no-coverage-ran: ${{ steps.affected.outputs.has == 'false' }} + no-coverage-ran: ${{ needs.detect-affected.outputs.has_test != 'true' }} coverage-folder: coverage coverage-base-folder: coverage-base gist-processing: 'false' @@ -141,7 +168,12 @@ jobs: hide-unchanged: 'false' - name: Run end-to-end tests - run: npx nx affected --target=e2e --parallel=3 || echo "No affected projects for e2e" + if: needs.detect-affected.outputs.has_e2e == 'true' + run: npx nx affected --target=e2e --parallel=3 + + - name: Skip e2e tests (no affected projects with e2e target) + if: needs.detect-affected.outputs.has_e2e != 'true' + run: echo "No affected projects with e2e target; skipping e2e tests." trivy-filesystem: name: Trivy Filesystem Scan @@ -180,6 +212,8 @@ jobs: build: name: Build runs-on: ubuntu-latest + needs: detect-affected + if: needs.detect-affected.outputs.has_affected == 'true' steps: - name: Checkout code uses: actions/checkout@v5 @@ -193,12 +227,12 @@ jobs: uses: ./.github/actions/setup-env - name: Run build - run: npx nx affected --target=build --parallel=3 || echo "No affected projects for build" + run: npx nx affected --target=build --parallel=3 - name: Run SBOM generation env: VERSION: 0.0.0-SNAPSHOT - run: npx nx affected --target=sbom --parallel=1 || echo "No affected projects for sbom generation" + run: npx nx affected --target=sbom --parallel=1 - name: Upload build artifacts uses: actions/upload-artifact@v7 @@ -240,7 +274,7 @@ jobs: - name: Build API container images run: | - npx nx affected --target=api-container-image --configuration=test --parallel=3 || echo "No projects for api container image build" + npx nx affected --target=api-container-image --configuration=test --parallel=3 - name: Scan API container images with Trivy uses: ./.github/actions/trivy-scan-local-images @@ -282,7 +316,7 @@ jobs: - name: Build Worker container images run: | - npx nx affected --target=worker-container-image --configuration=test --parallel=3 || echo "No projects for worker container image build" + npx nx affected --target=worker-container-image --configuration=test --parallel=3 - name: Scan Worker container images with Trivy uses: ./.github/actions/trivy-scan-local-images @@ -324,7 +358,7 @@ jobs: - name: Build VNC container images run: | - npx nx affected --target=vnc-container-image --configuration=test --parallel=3 || echo "No projects for vnc container image build" + npx nx affected --target=vnc-container-image --configuration=test --parallel=3 - name: Scan VNC container images with Trivy uses: ./.github/actions/trivy-scan-local-images @@ -366,7 +400,7 @@ jobs: - name: Build SSH container images run: | - npx nx affected --target=ssh-container-image --configuration=test --parallel=3 || echo "No projects for ssh container image build" + npx nx affected --target=ssh-container-image --configuration=test --parallel=3 - name: Scan SSH container images with Trivy uses: ./.github/actions/trivy-scan-local-images @@ -408,7 +442,7 @@ jobs: - name: Build AGI container images run: | - npx nx affected --target=agi-container-image --configuration=test --parallel=3 || echo "No projects for agi container image build" + npx nx affected --target=agi-container-image --configuration=test --parallel=3 - name: Scan AGI container images with Trivy uses: ./.github/actions/trivy-scan-local-images @@ -450,7 +484,7 @@ jobs: - name: Build Server container images run: | - npx nx affected --target=server-container-image --configuration=test --parallel=3 || echo "No projects for server container image build" + npx nx affected --target=server-container-image --configuration=test --parallel=3 - name: Scan Server container images with Trivy uses: ./.github/actions/trivy-scan-local-images @@ -467,6 +501,7 @@ jobs: name: Collect SBOM artifacts runs-on: ubuntu-latest needs: + - detect-affected - build - build-api-container-images - build-worker-container-images @@ -474,7 +509,7 @@ jobs: - build-ssh-container-images - build-agi-container-images - build-server-container-images - if: always() + if: always() && needs.detect-affected.outputs.has_affected == 'true' steps: - name: Download service SBOMs from build uses: actions/download-artifact@v8 @@ -508,6 +543,24 @@ jobs: compression-level: 6 if-no-files-found: ignore + - name: Fail if required upstream jobs failed + if: always() + env: + BUILD: ${{ needs.build.result }} + API: ${{ needs.build-api-container-images.result }} + WORKER: ${{ needs.build-worker-container-images.result }} + VNC: ${{ needs.build-vnc-container-images.result }} + SSH: ${{ needs.build-ssh-container-images.result }} + AGI: ${{ needs.build-agi-container-images.result }} + SERVER: ${{ needs.build-server-container-images.result }} + run: | + for result in "$BUILD" "$API" "$WORKER" "$VNC" "$SSH" "$AGI" "$SERVER"; do + if [ "$result" = "failure" ]; then + echo "::error::An upstream SBOM build job failed" + exit 1 + fi + done + build-scripts-binary: name: Build Scripts Binaries runs-on: ubuntu-latest @@ -531,7 +584,7 @@ jobs: uses: ./.github/actions/setup-env - name: Build binaries - run: npx nx affected --target=scripts-binary --parallel=3 || echo "No projects for scripts binary build" + run: npx nx affected --target=scripts-binary --parallel=3 build-openapi-clients-js: name: Build OpenAPI JavaScript/TypeScript Clients @@ -562,7 +615,7 @@ jobs: - name: Build JavaScript/TypeScript clients run: | - npx nx affected --target=openapi-client-js --configuration=test --parallel=3 || echo "No projects for openapi js client build" + npx nx affected --target=openapi-client-js --configuration=test --parallel=3 build-native-applications-linux: name: Build Linux Applications @@ -590,7 +643,7 @@ jobs: env: ELECTRON_FORGE_PLATFORM: linux run: | - xvfb-run -a npx nx affected --target=package --configuration=development-linux --parallel=3 || echo "No projects for native applications build" + xvfb-run -a npx nx affected --target=package --configuration=development-linux --parallel=3 - name: Upload build artifacts uses: actions/upload-artifact@v7 @@ -632,7 +685,7 @@ jobs: env: ELECTRON_FORGE_PLATFORM: win32 run: | - xvfb-run -a npx nx affected --target=package --configuration=development-windows --parallel=3 || echo "No projects for native applications build" + xvfb-run -a npx nx affected --target=package --configuration=development-windows --parallel=3 - name: Upload Windows Electron bundle uses: actions/upload-artifact@v7 @@ -667,7 +720,6 @@ jobs: - name: Download Windows Electron bundle uses: actions/download-artifact@v8 - continue-on-error: true with: name: windows-electron-bundle path: windows-electron-bundle @@ -689,9 +741,11 @@ jobs: name: Publish Native Applications runs-on: ubuntu-latest needs: + - detect-affected - build-native-applications-linux - build-native-applications-windows - build-native-applications-windows-installer + if: needs.detect-affected.outputs.has_affected == 'true' steps: - name: Checkout code uses: actions/checkout@v5 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 295420a87..92261eb95 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -48,17 +48,17 @@ jobs: - name: Run build if: steps.semantic-release.outputs.new_release_published == 'true' - run: npx nx run-many --target=build --configuration=production --parallel=3 || echo "No projects for build" + run: npx nx run-many --target=build --configuration=production --parallel=3 - name: Generate binaries if: steps.semantic-release.outputs.new_release_published == 'true' - run: npx nx run-many --target=scripts-binary --parallel=3 || echo "No projects for scripts binary build" + run: npx nx run-many --target=scripts-binary --parallel=3 - name: Run SBOM generation if: steps.semantic-release.outputs.new_release_published == 'true' env: VERSION: ${{ steps.semantic-release.outputs.new_release_version }} - run: npx nx run-many --target=sbom --parallel=1 || echo "No projects for sbom generation" + run: npx nx run-many --target=sbom --parallel=1 - name: Upload build artifacts uses: actions/upload-artifact@v7 @@ -110,7 +110,7 @@ jobs: env: VERSION: ${{ needs.publish.outputs.new_release_version }} run: | - npx nx run-many --target=api-container-image --configuration=release --parallel=3 || echo "No projects for api container image build" + npx nx run-many --target=api-container-image --configuration=release --parallel=3 - name: Generate container image SBOMs uses: ./.github/actions/generate-container-image-sboms @@ -157,7 +157,7 @@ jobs: env: VERSION: ${{ needs.publish.outputs.new_release_version }} run: | - npx nx run-many --target=worker-container-image --configuration=release --parallel=3 || echo "No projects for worker container image build" + npx nx run-many --target=worker-container-image --configuration=release --parallel=3 - name: Generate container image SBOMs uses: ./.github/actions/generate-container-image-sboms @@ -204,7 +204,7 @@ jobs: env: VERSION: ${{ needs.publish.outputs.new_release_version }} run: | - npx nx run-many --target=vnc-container-image --configuration=release --parallel=3 || echo "No projects for vnc container image build" + npx nx run-many --target=vnc-container-image --configuration=release --parallel=3 - name: Generate container image SBOMs uses: ./.github/actions/generate-container-image-sboms @@ -251,7 +251,7 @@ jobs: env: VERSION: ${{ needs.publish.outputs.new_release_version }} run: | - npx nx run-many --target=ssh-container-image --configuration=release --parallel=3 || echo "No projects for ssh container image build" + npx nx run-many --target=ssh-container-image --configuration=release --parallel=3 - name: Generate container image SBOMs uses: ./.github/actions/generate-container-image-sboms @@ -298,7 +298,7 @@ jobs: env: VERSION: ${{ needs.publish.outputs.new_release_version }} run: | - npx nx run-many --target=agi-container-image --configuration=release --parallel=3 || echo "No projects for agi container image build" + npx nx run-many --target=agi-container-image --configuration=release --parallel=3 - name: Generate container image SBOMs uses: ./.github/actions/generate-container-image-sboms @@ -345,7 +345,7 @@ jobs: env: VERSION: ${{ needs.publish.outputs.new_release_version }} run: | - npx nx run-many --target=server-container-image --configuration=release --parallel=3 || echo "No projects for server container image build" + npx nx run-many --target=server-container-image --configuration=release --parallel=3 - name: Generate container image SBOMs uses: ./.github/actions/generate-container-image-sboms @@ -395,7 +395,7 @@ jobs: env: VERSION: ${{ needs.publish.outputs.new_release_version }} run: | - npx nx run-many --target=openapi-client-js --configuration=release --parallel=3 || echo "No projects for openapi js client publish" + npx nx run-many --target=openapi-client-js --configuration=release --parallel=3 build-native-applications-linux: name: Publish Linux Applications @@ -430,7 +430,7 @@ jobs: ELECTRON_FORGE_PLATFORM: linux RELEASE_VERSION: ${{ needs.publish.outputs.new_release_version }} run: | - xvfb-run -a npx nx run-many --target=package --configuration=production-linux --parallel=3 || echo "No projects for native applications build" + xvfb-run -a npx nx run-many --target=package --configuration=production-linux --parallel=3 - name: Upload build artifacts uses: actions/upload-artifact@v7 @@ -479,7 +479,7 @@ jobs: ELECTRON_FORGE_PLATFORM: win32 RELEASE_VERSION: ${{ needs.publish.outputs.new_release_version }} run: | - xvfb-run -a npx nx run-many --target=package --configuration=production-windows --parallel=3 || echo "No projects for native applications build" + xvfb-run -a npx nx run-many --target=package --configuration=production-windows --parallel=3 - name: Upload Windows Electron bundle uses: actions/upload-artifact@v7 @@ -517,7 +517,6 @@ jobs: - name: Download Windows Electron bundle uses: actions/download-artifact@v8 - continue-on-error: true with: name: windows-electron-bundle path: windows-electron-bundle @@ -526,6 +525,7 @@ jobs: shell: pwsh env: RELEASE_VERSION: ${{ needs.publish.outputs.new_release_version }} + REQUIRE_INSTALLER_BUNDLE: '1' run: ./tools/ci/build-windows-installer.ps1 - name: Upload Windows installer artifacts @@ -562,7 +562,6 @@ jobs: - name: Download binaries uses: actions/download-artifact@v8 - continue-on-error: true with: path: artifacts pattern: binary-artifacts-* @@ -599,6 +598,12 @@ jobs: CLOUDFLARE_R2_ENDPOINT: ${{ secrets.CLOUDFLARE_R2_ENDPOINT }} AWS_BUCKET: ${{ secrets.AWS_BUCKET }} run: | + shopt -s nullglob + files=(release/*) + if [ ${#files[@]} -eq 0 ]; then + echo "::error::No desktop binaries in release/ — aborting R2 upload" + exit 1 + fi aws s3 cp release/ s3://${{ secrets.AWS_BUCKET }}/releases/${{ needs.publish.outputs.new_release_version }}/desktop/ --recursive --endpoint-url ${{ secrets.CLOUDFLARE_R2_ENDPOINT }} publish-agentctx: @@ -664,6 +669,7 @@ jobs: fi - name: Upload agent-console SBOM + id: upload-sbom-agent-console uses: forepath/gh-upload-sbom@v2 continue-on-error: true with: @@ -676,6 +682,7 @@ jobs: bomfilename: 'dist/sboms/frontend-agent-console.cdx.json' - name: Upload agent-controller SBOM + id: upload-sbom-agent-controller uses: forepath/gh-upload-sbom@v2 continue-on-error: true with: @@ -688,6 +695,7 @@ jobs: bomfilename: 'dist/sboms/backend-agent-controller.cdx.json' - name: Upload agent-manager SBOM + id: upload-sbom-agent-manager uses: forepath/gh-upload-sbom@v2 continue-on-error: true with: @@ -700,6 +708,7 @@ jobs: bomfilename: 'dist/sboms/backend-agent-manager.cdx.json' - name: Upload billing-console SBOM + id: upload-sbom-billing-console uses: forepath/gh-upload-sbom@v2 continue-on-error: true with: @@ -712,6 +721,7 @@ jobs: bomfilename: 'dist/sboms/frontend-billing-console.cdx.json' - name: Upload billing-manager SBOM + id: upload-sbom-billing-manager uses: forepath/gh-upload-sbom@v2 continue-on-error: true with: @@ -724,6 +734,7 @@ jobs: bomfilename: 'dist/sboms/backend-billing-manager.cdx.json' - name: Upload desktop SBOM + id: upload-sbom-desktop uses: forepath/gh-upload-sbom@v2 continue-on-error: true with: @@ -736,6 +747,7 @@ jobs: bomfilename: 'dist/sboms/native-agent-console.cdx.json' - name: Upload docs SBOM + id: upload-sbom-docs uses: forepath/gh-upload-sbom@v2 continue-on-error: true with: @@ -748,6 +760,7 @@ jobs: bomfilename: 'dist/sboms/frontend-docs.cdx.json' - name: Upload mcp-devkit SBOM + id: upload-sbom-mcp-devkit uses: forepath/gh-upload-sbom@v2 continue-on-error: true with: @@ -760,6 +773,7 @@ jobs: bomfilename: 'dist/sboms/mcp-devkit.cdx.json' - name: Upload mcp-proxy SBOM + id: upload-sbom-mcp-proxy uses: forepath/gh-upload-sbom@v2 continue-on-error: true with: @@ -772,6 +786,7 @@ jobs: bomfilename: 'dist/sboms/mcp-proxy.cdx.json' - name: Upload portal SBOM + id: upload-sbom-portal uses: forepath/gh-upload-sbom@v2 continue-on-error: true with: @@ -783,6 +798,46 @@ jobs: autocreate: true bomfilename: 'dist/sboms/frontend-portal.cdx.json' + - name: Report DependencyTrack upload outcomes + if: always() + run: | + { + echo "## DependencyTrack SBOM uploads" + echo "" + echo "| Project | Outcome |" + echo "|---------|---------|" + echo "| agent-console | ${{ steps.upload-sbom-agent-console.outcome }} |" + echo "| agent-controller | ${{ steps.upload-sbom-agent-controller.outcome }} |" + echo "| agent-manager | ${{ steps.upload-sbom-agent-manager.outcome }} |" + echo "| billing-console | ${{ steps.upload-sbom-billing-console.outcome }} |" + echo "| billing-manager | ${{ steps.upload-sbom-billing-manager.outcome }} |" + echo "| desktop | ${{ steps.upload-sbom-desktop.outcome }} |" + echo "| docs | ${{ steps.upload-sbom-docs.outcome }} |" + echo "| shared-mcp-devkit | ${{ steps.upload-sbom-mcp-devkit.outcome }} |" + echo "| shared-mcp-proxy | ${{ steps.upload-sbom-mcp-proxy.outcome }} |" + echo "| portal | ${{ steps.upload-sbom-portal.outcome }} |" + } >> "$GITHUB_STEP_SUMMARY" + failed=0 + for outcome in \ + "${{ steps.upload-sbom-agent-console.outcome }}" \ + "${{ steps.upload-sbom-agent-controller.outcome }}" \ + "${{ steps.upload-sbom-agent-manager.outcome }}" \ + "${{ steps.upload-sbom-billing-console.outcome }}" \ + "${{ steps.upload-sbom-billing-manager.outcome }}" \ + "${{ steps.upload-sbom-desktop.outcome }}" \ + "${{ steps.upload-sbom-docs.outcome }}" \ + "${{ steps.upload-sbom-mcp-devkit.outcome }}" \ + "${{ steps.upload-sbom-mcp-proxy.outcome }}" \ + "${{ steps.upload-sbom-portal.outcome }}" + do + if [ "$outcome" = "failure" ]; then + failed=1 + fi + done + if [ "$failed" -eq 1 ]; then + echo "::warning::One or more DependencyTrack SBOM uploads failed (see table above)" + fi + - name: Upload SBOMs to Cloudflare R2 bucket env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} diff --git a/tools/ci/build-windows-installer.ps1 b/tools/ci/build-windows-installer.ps1 index 6afdc7abd..b31115ade 100644 --- a/tools/ci/build-windows-installer.ps1 +++ b/tools/ci/build-windows-installer.ps1 @@ -7,6 +7,9 @@ param( $ErrorActionPreference = 'Stop' if (-not (Test-Path -LiteralPath $BundleRoot)) { + if ($env:REQUIRE_INSTALLER_BUNDLE -eq '1') { + throw "Bundle root '$BundleRoot' not found" + } Write-Host "Bundle root '$BundleRoot' not found; skipping Windows installer build." exit 0 } @@ -15,6 +18,9 @@ $exe = Get-ChildItem -Path $BundleRoot -Recurse -Filter 'native-agent-console.ex Select-Object -First 1 if (-not $exe) { + if ($env:REQUIRE_INSTALLER_BUNDLE -eq '1') { + throw "No native-agent-console.exe under '$BundleRoot'" + } Write-Host "No native-agent-console.exe under '$BundleRoot'; skipping Windows installer build." exit 0 } diff --git a/tools/ci/detect-affected.sh b/tools/ci/detect-affected.sh new file mode 100755 index 000000000..9f85d0714 --- /dev/null +++ b/tools/ci/detect-affected.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# Emit GitHub Actions outputs for affected Nx projects and common target subsets. +set -euo pipefail + +list_affected_projects() { + local extra_args=("$@") + npx nx show projects --affected "${extra_args[@]}" | sed '/^\s*$/d' || true +} + +write_bool_output() { + local name="$1" + local value="$2" + echo "${name}=${value}" >> "$GITHUB_OUTPUT" +} + +write_multiline_output() { + local name="$1" + local value="$2" + { + echo "${name}<> "$GITHUB_OUTPUT" +} + +PROJECTS="$(list_affected_projects)" + +if [ -n "$PROJECTS" ]; then + write_bool_output has_affected true + write_multiline_output projects "$PROJECTS" + echo "Affected projects:" + printf '%s\n' "$PROJECTS" +else + write_bool_output has_affected false + write_multiline_output projects "" + echo "No affected projects; nx affected jobs and steps will be skipped." +fi + +for target in test e2e lint build; do + target_projects="$(list_affected_projects --with-target="$target")" + if [ -n "$target_projects" ]; then + write_bool_output "has_${target}" true + else + write_bool_output "has_${target}" false + fi +done