build: Fallout 10.4 across the repo group, pinned exactly #148
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: UI tests | |
| # Label-gated Playwright runs for PRs (#128). Heavy: a chromium install + | |
| # `--with-deps` apt-get is ~150MB plus host-package churn, so we don't run | |
| # UI tests on every push. Add the `run-ui-tests` label to a PR to opt in. | |
| # | |
| # Trigger fires on label add (so adding the label kicks the workflow), on | |
| # new pushes to the PR (so subsequent commits re-run while the label is | |
| # present), and on opened/reopened (so a PR opened with the label already | |
| # attached runs immediately). | |
| on: | |
| pull_request: | |
| branches: [main] | |
| types: [opened, reopened, synchronize, labeled] | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| checks: write # publish-unit-test-result-action | |
| pull-requests: write # auto-label job needs this (currently disabled) | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| concurrency: | |
| group: ui-tests-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # --------------------------------------------------------------------------- | |
| # Decide whether to run. Two signals: | |
| # 1. `run-ui-tests` label present on the PR → manual opt-in. | |
| # 2. Web paths changed (src/Presentation/Web/**, test/Presentation/Web/**) → auto-detected. | |
| # Either signal flips `run=true`. The path check is wired now even though | |
| # the auto-add-label job below is disabled, so a path-touching PR still | |
| # gets UI coverage if the human forgets the label. | |
| # --------------------------------------------------------------------------- | |
| decide: | |
| name: Decide | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 2 | |
| outputs: | |
| run: ${{ steps.gate.outputs.run }} | |
| label-present: ${{ steps.gate.outputs.label-present }} | |
| paths-changed: ${{ steps.filter.outputs.web }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Detect web paths | |
| id: filter | |
| if: github.event_name == 'pull_request' | |
| uses: dorny/paths-filter@v3 | |
| with: | |
| # Explicit positive patterns ONLY. dorny/paths-filter's `!` | |
| # syntax does NOT subtract from prior patterns the way the | |
| # README implies — under the default `some` quantifier each | |
| # `!` line is an independent positive match for "file does | |
| # NOT match this glob", which OR-combines with the others and | |
| # ends up matching almost everything (e.g. an ADR .md outside | |
| # any src/ path matched the previous `!**/Dockerfile` line | |
| # because it isn't a Dockerfile). Lesson: enumerate the | |
| # extensions that actually move pixels. | |
| filters: | | |
| web: | |
| - 'src/Presentation/Web/Satisfactory.Presentation.Web/**/*.{cs,razor,razor.css,css,js,html,json}' | |
| - 'src/Presentation/Web/Satisfactory.Presentation.Web/wwwroot/**' | |
| - 'src/Presentation/Web/CaptainOfIndustry.Presentation.Web/**/*.{cs,razor,razor.css,css,js,html,json}' | |
| - 'src/Presentation/Web/CaptainOfIndustry.Presentation.Web/wwwroot/**' | |
| - 'src/Presentation/Web/Erp.Presentation.Web.Common/**/*.{cs,razor,razor.css,css,js,html}' | |
| - 'test/Presentation/Web/**' | |
| - name: Compute gate | |
| id: gate | |
| env: | |
| LABELS: ${{ toJson(github.event.pull_request.labels.*.name) }} | |
| PATHS_CHANGED: ${{ steps.filter.outputs.web }} | |
| EVENT: ${{ github.event_name }} | |
| run: | | |
| label_present=false | |
| if echo "$LABELS" | grep -q '"run-ui-tests"'; then | |
| label_present=true | |
| fi | |
| echo "label-present=$label_present" >> "$GITHUB_OUTPUT" | |
| if [ "$EVENT" = "workflow_dispatch" ]; then | |
| echo "run=true" >> "$GITHUB_OUTPUT" | |
| elif [ "$label_present" = "true" ] || [ "$PATHS_CHANGED" = "true" ]; then | |
| echo "run=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "run=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| # --------------------------------------------------------------------------- | |
| # Auto-add the `run-ui-tests` label when web paths change on a PR that | |
| # doesn't already carry it. **Disabled for now** — flip `if: false` to | |
| # `if: needs.decide.outputs.paths-changed == 'true' && needs.decide.outputs.label-present == 'false' && github.event_name == 'pull_request'` | |
| # when ready. The tests still run from the path-changed signal alone, so | |
| # disabling this is purely cosmetic — it just means the label won't | |
| # auto-appear on the PR. | |
| # --------------------------------------------------------------------------- | |
| auto-label: | |
| name: Auto-add run-ui-tests label | |
| needs: decide | |
| if: false | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 2 | |
| steps: | |
| - name: Add label | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| run: gh pr edit "$PR_NUMBER" --add-label run-ui-tests | |
| # --------------------------------------------------------------------------- | |
| # The actual Playwright run. Mirrors the existing `build-and-test` job | |
| # structure (checkout + submodule + NuGet cache + setup-dotnet) and then | |
| # calls the new `TestUi` Nuke target, which depends on | |
| # `InstallPlaywrightBrowsers` (Nuke handles the `playwright.ps1 install | |
| # --with-deps chromium` invocation). | |
| # --------------------------------------------------------------------------- | |
| ui-tests: | |
| name: UI tests (Playwright) | |
| needs: decide | |
| if: needs.decide.outputs.run == 'true' | |
| runs-on: ubuntu-latest | |
| # 30 min was tight on a cold ubuntu-latest runner — NuGet restore + Playwright | |
| # browser install + Aspire-orchestrated test suite was hitting the limit before | |
| # `dotnet test` finished (TRX uploaded with the tests that did run all green, | |
| # job itself killed at the timeout). 45 min gives breathing room without | |
| # being absurd. | |
| timeout-minutes: 45 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 # NB.GV needs full history for version-height calc | |
| - name: Initialize vendored SatisfactorySaveNet submodule | |
| run: git -c submodule.vendor/SatisfactorySaveNet.update=checkout submodule update --init --recursive | |
| - name: Cache NuGet packages | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.nuget/packages | |
| key: ubuntu-latest-nuget-${{ hashFiles('**/*.csproj', '**/global.json') }} | |
| restore-keys: ubuntu-latest-nuget- | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v4 | |
| env: | |
| DOTNET_INSTALL_DIR: /home/runner/.dotnet | |
| with: | |
| dotnet-version: 8.0.x | |
| global-json-file: global.json | |
| # Aspire AppHost's WithHttpHealthCheck("/health") probe hits the | |
| # apiservice / webfrontend over HTTPS. The .NET dev cert isn't in the | |
| # Linux runner's CA bundle by default, so HttpClient inside the | |
| # AppHost rejects the cert with UntrustedRoot, /health is never | |
| # reached, webfrontend stays Unhealthy forever, and the test fixture | |
| # hangs in WaitForResourceHealthyAsync until the job timeout. | |
| # | |
| # `dotnet dev-certs https` generates the cert; `--trust` is a no-op | |
| # for the system trust store on Linux, so we additionally export the | |
| # cert as PEM and drop it into /usr/local/share/ca-certificates/. | |
| # update-ca-certificates then rebuilds the system bundle that | |
| # OpenSSL (and HttpClient via SslStream) consult. | |
| - name: Trust ASP.NET Core HTTPS dev cert | |
| run: | | |
| dotnet dev-certs https | |
| dotnet dev-certs https --export-path /tmp/aspnet-https.pem --format Pem --no-password | |
| sudo cp /tmp/aspnet-https.pem /usr/local/share/ca-certificates/aspnet-https-dev.crt | |
| sudo update-ca-certificates | |
| - name: Run UI tests | |
| run: ./build.sh TestUi | |
| - name: Upload TRX test results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ui-test-results | |
| path: artifacts/test-results/*.trx | |
| if-no-files-found: warn | |
| - name: Upload Playwright traces / videos (on failure) | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: playwright-traces | |
| path: | | |
| test/Presentation/Web/Satisfactory.Presentation.Web.UiTests/bin/**/playwright-traces/** | |
| test/Presentation/Web/Satisfactory.Presentation.Web.UiTests/bin/**/test-results/** | |
| if-no-files-found: ignore | |
| # --------------------------------------------------------------------------- | |
| # Publish — same shape as the main CI workflow's publish-test-results job, | |
| # scoped to the UI run. | |
| # --------------------------------------------------------------------------- | |
| publish-results: | |
| name: Publish UI test results | |
| needs: [decide, ui-tests] | |
| if: always() && needs.decide.outputs.run == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| checks: write | |
| pull-requests: write | |
| steps: | |
| - name: Download TRX artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: test-results | |
| pattern: ui-test-results | |
| merge-multiple: false | |
| - name: Publish results to commit check | |
| uses: EnricoMi/publish-unit-test-result-action/linux@v2 | |
| with: | |
| files: test-results/**/*.trx | |
| check_name: UI test results | |
| comment_mode: changes in failures |