Repository navigation
fix(e2e): make the list filter suite run again #1576
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: Pull Request Tests | |
| # tools/stb is a standalone project with its own gate (stb_tests.yml). We must | |
| # NOT skip this workflow for stb-only changes via a trigger-level `paths` | |
| # filter: these jobs are REQUIRED status checks, and a required check that is | |
| # path-filtered at the trigger never reports — leaving stb-only PRs stuck on | |
| # "Expected — waiting for status". Instead we always trigger and skip each job | |
| # with a `changes`-gated `if:`; a job skipped by `if:` reports success and so | |
| # satisfies the required check without running the Stratos build. See #5528. | |
| on: | |
| pull_request: | |
| branches: | |
| - main | |
| - develop | |
| - 'angular**' | |
| - 'release/**' | |
| push: | |
| branches: | |
| - develop | |
| - main | |
| - 'release/**' | |
| # One live run per ref: a push to a PR branch (or develop/main) cancels the | |
| # in-flight run for the same ref, so overlapping runs never contend for | |
| # runners and only the newest commit is gated. | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| # pull-requests: read is required by dorny/paths-filter, which lists a | |
| # PR's changed files through the API. | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| env: | |
| NODE_OPTIONS: --max-old-space-size=5500 | |
| NODE_VERSION: '24' | |
| GO_VERSION: '1.27' | |
| BUN_VERSION: '1.3.14' | |
| jobs: | |
| # Decide whether this PR touches anything outside tools/stb. All the real | |
| # jobs below gate on `non_stb` so stb-only PRs skip them (→ success) while | |
| # every other PR runs the full suite. Always runs; cheap. | |
| changes: | |
| name: Detect non-stb changes | |
| runs-on: ubuntu-latest | |
| outputs: | |
| non_stb: ${{ steps.filter.outputs.non_stb }} | |
| docs: ${{ steps.filter.outputs.docs }} | |
| website: ${{ steps.filter.outputs.website }} | |
| booklets: ${{ steps.filter.outputs.booklets }} | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| persist-credentials: false | |
| - uses: dorny/paths-filter@d1c1ffe0248fe513906c8e24db8ea791d46f8590 # v3.0.3 | |
| id: filter | |
| with: | |
| # A file counts only if it matches EVERY pattern — required for the | |
| # '!' exclusions below; the default 'some' lets '**' match anything, | |
| # making the exclusions dead letters. | |
| predicate-quantifier: 'every' | |
| filters: | | |
| # non_stb = the code path: skip the heavy suite when a PR only | |
| # touches stb tooling, docs/, or website/ (each has its own gate). | |
| non_stb: | |
| - '**' | |
| - '!tools/stb/**' | |
| - '!docs/**' | |
| - '!website/**' | |
| - '!README.md' | |
| docs: | |
| - 'docs/**' | |
| website: | |
| - 'website/**' | |
| booklets: | |
| - 'docs/booklets/**' | |
| # Enforce the GFM-intersection markdown subset on docs/ so every page | |
| # renders identically on GitHub and in the website generator. Not a | |
| # required check yet; job-level `if:` keeps it green-when-skipped if it | |
| # ever becomes one (same pattern as the stb gate above). | |
| docs-lint: | |
| name: Docs Lint | |
| runs-on: ubuntu-latest | |
| needs: changes | |
| if: needs.changes.outputs.docs == 'true' | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| persist-credentials: false | |
| - uses: oven-sh/setup-bun@f4d14e03ff726c06358e5557344e1da148b56cf7 # v1.2.2 | |
| with: | |
| bun-version: ${{ env.BUN_VERSION }} | |
| - run: bun scripts/lint-docs.mjs | |
| # The website previously had no PR coverage on develop: the legacy docs | |
| # workflow (since removed) only triggered on master, so a website change | |
| # (e.g. a dependency bump with a stale bun.lock) could merge green | |
| # without ever being built. Frozen-lockfile install + full build | |
| # catches both. | |
| website-build: | |
| name: Website Build | |
| runs-on: ubuntu-latest | |
| needs: changes | |
| if: needs.changes.outputs.website == 'true' | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| persist-credentials: false | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - uses: oven-sh/setup-bun@f4d14e03ff726c06358e5557344e1da148b56cf7 # v1.2.2 | |
| with: | |
| bun-version: ${{ env.BUN_VERSION }} | |
| - name: Install dependencies | |
| run: cd website && bun install --frozen-lockfile | |
| - name: Build website | |
| run: cd website && bun run build | |
| # Booklets are offline epub/PDF renderings of docs/ (see docs/booklets/). | |
| # Rendered on any docs or booklets change; the outputs are uploaded | |
| # as a PR artifact for eyeballing, not published anywhere. | |
| booklets-build: | |
| name: Booklets Build | |
| runs-on: ubuntu-latest | |
| needs: changes | |
| if: needs.changes.outputs.docs == 'true' || needs.changes.outputs.booklets == 'true' | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| persist-credentials: false | |
| - uses: quarto-dev/quarto-actions/setup@8a96df13519ee81fd526f2dfca5962811136661b # v2.2.0 | |
| with: | |
| version: 1.9.38 | |
| # Mermaid diagrams render through chrome-headless-shell; without it | |
| # preinstalled, quarto render blocks on an interactive install | |
| # prompt and the job hangs until the timeout. | |
| - name: Install chrome-headless-shell | |
| run: quarto install chrome-headless-shell --no-prompt | |
| - name: Render booklets | |
| run: docs/booklets/render.sh | |
| - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: booklets | |
| path: | | |
| dist/booklets/ | |
| !dist/booklets/.work/** | |
| retention-days: 14 | |
| # Advisory: names the e2e specs that cover the components a PR changes, | |
| # derived from data-test hooks and element selectors (scripts/ | |
| # e2e-impact.mjs). Never gates — the e2e suite needs a live CF endpoint, | |
| # so it cannot run here; this puts the scoped-run command in the job | |
| # summary so reviewers know exactly which specs the change touches. | |
| e2e-impact: | |
| name: E2E Impact (advisory) | |
| runs-on: ubuntu-latest | |
| needs: changes | |
| if: github.event_name == 'pull_request' && needs.changes.outputs.non_stb == 'true' | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| persist-credentials: false | |
| fetch-depth: 0 | |
| - uses: oven-sh/setup-bun@f4d14e03ff726c06358e5557344e1da148b56cf7 # v1.2.2 | |
| with: | |
| bun-version: ${{ env.BUN_VERSION }} | |
| - name: Report covering specs | |
| env: | |
| BASE: ${{ github.base_ref }} | |
| # Lets the impact script harvest tokens of components DELETED in | |
| # the PR from the base ref, so their stranded specs still surface. | |
| IMPACT_BASE: origin/${{ github.base_ref }} | |
| run: | | |
| # pipefail: a failing git diff must fail the job, not feed bun an | |
| # empty stdin that reads as a green "no frontend changes". | |
| set -o pipefail | |
| { | |
| echo '```' | |
| git diff --name-only "origin/${BASE}...HEAD" | bun scripts/e2e-impact.mjs | |
| echo '```' | |
| } | tee -a "${GITHUB_STEP_SUMMARY}" | |
| lint: | |
| name: Lint Check | |
| runs-on: ubuntu-latest | |
| needs: changes | |
| if: needs.changes.outputs.non_stb == 'true' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| persist-credentials: false | |
| - name: Setup Go | |
| uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| cache-dependency-path: src/jetstream/go.sum | |
| - name: Setup Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@f4d14e03ff726c06358e5557344e1da148b56cf7 # v1.2.2 | |
| with: | |
| bun-version: ${{ env.BUN_VERSION }} | |
| - name: Get Bun cache directory | |
| id: bun-cache-dir | |
| shell: bash | |
| run: echo "dir=$(bun pm cache)" >> ${GITHUB_OUTPUT} | |
| - name: Cache Bun dependencies | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 | |
| with: | |
| path: ${{ steps.bun-cache-dir.outputs.dir }} | |
| key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-bun- | |
| - name: Install dependencies | |
| run: bun install --frozen-lockfile | |
| - name: Run linting | |
| # `make check lint` is what runs locally: ESLint plus go fmt, go vet | |
| # and golangci-lint over every backend module. Running `bun run lint` | |
| # here instead left the Go linters out of CI entirely. | |
| run: make check lint | |
| test-frontend: | |
| name: Frontend Tests | |
| runs-on: ubuntu-latest | |
| needs: changes | |
| # Gate at STEP level, not job level: the per-package names below are | |
| # required status checks, and a job-level skip collapses the matrix so | |
| # those contexts never report ("expected" forever). With step-level | |
| # gating each shard still spins up, no-ops, and reports success. | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| package: [core, store, cloud-foundry, kubernetes, cf-autoscaler, git, shared, extension] | |
| steps: | |
| - name: Checkout code | |
| if: needs.changes.outputs.non_stb == 'true' | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| persist-credentials: false | |
| - name: Setup Node.js | |
| if: needs.changes.outputs.non_stb == 'true' | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Setup Bun | |
| if: needs.changes.outputs.non_stb == 'true' | |
| uses: oven-sh/setup-bun@f4d14e03ff726c06358e5557344e1da148b56cf7 # v1.2.2 | |
| with: | |
| bun-version: ${{ env.BUN_VERSION }} | |
| - name: Get Bun cache directory | |
| if: needs.changes.outputs.non_stb == 'true' | |
| id: bun-cache-dir | |
| shell: bash | |
| run: echo "dir=$(bun pm cache)" >> ${GITHUB_OUTPUT} | |
| - name: Cache Bun dependencies | |
| if: needs.changes.outputs.non_stb == 'true' | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 | |
| with: | |
| path: ${{ steps.bun-cache-dir.outputs.dir }} | |
| key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-bun- | |
| - name: Install dependencies | |
| if: needs.changes.outputs.non_stb == 'true' | |
| run: bun install --frozen-lockfile | |
| - name: Generate build info | |
| if: needs.changes.outputs.non_stb == 'true' | |
| run: make stamp frontend | |
| - name: Run ${{ matrix.package }} tests | |
| if: needs.changes.outputs.non_stb == 'true' | |
| # TODO: Remove --dangerouslyIgnoreUnhandledErrors once test mocks are fixed (FWT-872) | |
| run: bun run vitest run --dangerouslyIgnoreUnhandledErrors --project=${{ matrix.package }} --passWithNoTests | |
| test-backend: | |
| name: Backend Tests | |
| runs-on: ubuntu-latest | |
| needs: changes | |
| if: needs.changes.outputs.non_stb == 'true' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| persist-credentials: false | |
| - name: Setup Go | |
| uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| cache-dependency-path: src/jetstream/go.sum | |
| - name: Setup Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@f4d14e03ff726c06358e5557344e1da148b56cf7 # v1.2.2 | |
| with: | |
| bun-version: ${{ env.BUN_VERSION }} | |
| - name: Install dependencies | |
| run: bun install --frozen-lockfile | |
| - name: Create extra_plugins.go | |
| run: | | |
| cat > src/jetstream/extra_plugins.go << 'EOF' | |
| package main | |
| // This file is auto-generated - DO NOT EDIT | |
| import _ "github.com/cloudfoundry/stratos/src/jetstream/plugins/autoscaler" | |
| import _ "github.com/cloudfoundry/stratos/src/jetstream/plugins/cloudfoundry" | |
| import _ "github.com/cloudfoundry/stratos/src/jetstream/plugins/cfapppush" | |
| import _ "github.com/cloudfoundry/stratos/src/jetstream/plugins/cfappssh" | |
| import _ "github.com/cloudfoundry/stratos/src/jetstream/plugins/userinvite" | |
| import _ "github.com/cloudfoundry/stratos/src/jetstream/plugins/analysis" | |
| import _ "github.com/cloudfoundry/stratos/src/jetstream/plugins/kubernetes" | |
| import _ "github.com/cloudfoundry/stratos/src/jetstream/plugins/monocular" | |
| EOF | |
| - name: Run backend tests | |
| run: make test backend | |
| build-check: | |
| name: Build Check | |
| runs-on: ubuntu-latest | |
| needs: [changes, lint, test-frontend, test-backend] | |
| if: needs.changes.outputs.non_stb == 'true' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| persist-credentials: false | |
| - name: Setup Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@f4d14e03ff726c06358e5557344e1da148b56cf7 # v1.2.2 | |
| with: | |
| bun-version: ${{ env.BUN_VERSION }} | |
| - name: Setup Go | |
| uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| cache-dependency-path: src/jetstream/go.sum | |
| - name: Get Bun cache directory | |
| id: bun-cache-dir | |
| shell: bash | |
| run: echo "dir=$(bun pm cache)" >> ${GITHUB_OUTPUT} | |
| - name: Cache Bun dependencies | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 | |
| with: | |
| path: ${{ steps.bun-cache-dir.outputs.dir }} | |
| key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-bun- | |
| - name: Install dependencies | |
| run: bun install --frozen-lockfile | |
| - name: Create extra_plugins.go | |
| run: | | |
| cat > src/jetstream/extra_plugins.go << 'EOF' | |
| package main | |
| // This file is auto-generated - DO NOT EDIT | |
| import _ "github.com/cloudfoundry/stratos/src/jetstream/plugins/autoscaler" | |
| import _ "github.com/cloudfoundry/stratos/src/jetstream/plugins/cloudfoundry" | |
| import _ "github.com/cloudfoundry/stratos/src/jetstream/plugins/cfapppush" | |
| import _ "github.com/cloudfoundry/stratos/src/jetstream/plugins/cfappssh" | |
| import _ "github.com/cloudfoundry/stratos/src/jetstream/plugins/userinvite" | |
| import _ "github.com/cloudfoundry/stratos/src/jetstream/plugins/analysis" | |
| import _ "github.com/cloudfoundry/stratos/src/jetstream/plugins/kubernetes" | |
| import _ "github.com/cloudfoundry/stratos/src/jetstream/plugins/monocular" | |
| EOF | |
| - name: Build frontend | |
| run: make build frontend | |
| - name: Build backend | |
| run: make build backend | |
| - name: Upload frontend artifact | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: frontend-dist | |
| path: dist/ | |
| retention-days: 1 | |
| - name: Upload backend artifact | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: backend-binary | |
| path: dist/bin/jetstream | |
| retention-days: 1 | |
| # Advisory only, and deliberately NOT in pr-success's needs: a hard failure | |
| # here would fire on pipeline fixes, docs-only changes, reverts and bumps, | |
| # and a gate with that false-positive rate gets routed around rather than | |
| # obeyed. A ::warning:: renders inline on the Files tab; a plain log line | |
| # would be invisible once the check goes green. | |
| # | |
| # Dependabot is exempt because it cannot act on the advice — it never runs | |
| # release-notes.sh. Its bumps are picked up instead by | |
| # `release-notes.sh check` at `make stamp tag` time, off the commit log. | |
| changelog-fragment: | |
| name: Changelog Fragment (advisory) | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' && github.actor != 'dependabot[bot]' | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Check for a release-notes fragment | |
| env: | |
| BASE: ${{ github.event.pull_request.base.sha }} | |
| run: | | |
| # AM, not A: appending to an existing fragment is how a change joins | |
| # a note that is already there — a dependency bump landing after the | |
| # fragment that covers its group, for instance. | |
| if git diff --name-only --diff-filter=AM "$BASE...HEAD" \ | |
| | grep -qE '^changelog\.d/[0-9]+-.*\.md$'; then | |
| echo "✅ Release-notes fragment present." | |
| exit 0 | |
| fi | |
| echo "::warning title=No release-notes fragment::This PR neither adds nor updates a changelog.d fragment, so it will not appear in the release notes. Run ./build/release-notes.sh new — or ignore this if the change needs no note." | |
| pr-success: | |
| name: PR Quality Gate | |
| runs-on: ubuntu-latest | |
| needs: [lint, test-frontend, test-backend, build-check, website-build, booklets-build] | |
| if: always() | |
| steps: | |
| - name: Check all jobs | |
| # A stb-only PR skips every job above; "skipped" is an acceptable | |
| # outcome here (the Stratos suite is genuinely N/A), only "failure" | |
| # or "cancelled" should fail the gate. | |
| run: | | |
| for res in "${{ needs.lint.result }}" \ | |
| "${{ needs.test-frontend.result }}" \ | |
| "${{ needs.test-backend.result }}" \ | |
| "${{ needs.build-check.result }}" \ | |
| "${{ needs.website-build.result }}" \ | |
| "${{ needs.booklets-build.result }}"; do | |
| if [ "$res" != "success" ] && [ "$res" != "skipped" ]; then | |
| echo "❌ One or more required checks failed (result: $res)" | |
| exit 1 | |
| fi | |
| done | |
| echo "✅ All PR checks passed!" |