diff --git a/.github/workflows/loadtest.yml b/.github/workflows/loadtest.yml new file mode 100644 index 000000000..cb2cf3b34 --- /dev/null +++ b/.github/workflows/loadtest.yml @@ -0,0 +1,113 @@ +name: Load Test Tooling Workflow + +on: + push: + branches: + - main + pull_request: + branches: + - "*" + +permissions: + contents: read + +# The load-test tooling of src/loadtest (see documentation/load-testing.md). +# swarm and canary are standalone TypeScript packages, each with its own +# package.json and yarn.lock, like src/yhub-server; k6 runs plain scripts and +# the dashboards are generated by a python script. +jobs: + lint-and-test-loadtest: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + package: + - swarm + - canary + defaults: + run: + working-directory: src/loadtest/${{ matrix.package }} + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + + - name: Setup Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + with: + node-version: "22.x" + cache: yarn + cache-dependency-path: src/loadtest/${{ matrix.package }}/yarn.lock + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Lint + run: yarn lint + + - name: Typecheck + run: yarn typecheck + + # the image compiles with tsconfig.build.json (rootDir ./src, emits), + # which can fail where the typecheck passes + - name: Build + run: yarn build + + # the canary specs drive a real Chromium against a page served + # in-process: install the one matching the playwright package + - name: Install Chromium + if: matrix.package == 'canary' + run: yarn playwright install --with-deps --only-shell chromium + + - name: Test + run: yarn test + + check-k6: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + + # `k6 inspect` runs the init context of each scenario (imports, options, + # the manifest checks) without sending a request. The scripts refuse to + # load without a manifest holding a session: give them a dummy one. + - name: Inspect the scenarios + run: | + echo '{"cookie_name": "sessionid", "sessions": [{"session_key": "x", "editable_documents": [], "readonly_documents": []}]}' \ + > "$RUNNER_TEMP/manifest.json" + for scenario in src/loadtest/k6/scenarios/*.js; do + echo "::group::$scenario" + docker run --rm \ + -v "$PWD/src/loadtest/k6:/k6:ro" \ + -v "$RUNNER_TEMP/manifest.json:/manifest.json:ro" \ + -w /k6 grafana/k6:2.3.0 inspect "scenarios/$(basename "$scenario")" + echo "::endgroup::" + done + + check-dashboards: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + + # the docs-loadtest-* boards are generated: the committed JSON must be + # what generate.py writes (django.json is a community board, not + # generated, and is left out of the comparison) + - name: Check the dashboards are up to date + run: | + mkdir -p "$RUNNER_TEMP/dashboards" + python3 src/loadtest/dashboards/generate.py "$RUNNER_TEMP/dashboards" + status=0 + for generated in "$RUNNER_TEMP"/dashboards/*.json; do + committed="src/loadtest/dashboards/$(basename "$generated")" + if ! diff -u "$committed" "$generated"; then + echo "::error file=$committed::out of date, run: python3 src/loadtest/dashboards/generate.py src/loadtest/dashboards" + status=1 + fi + done + exit $status diff --git a/CHANGELOG.md b/CHANGELOG.md index 3174bcb82..f624f0b4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to ### Added - 👷(ci) lint, typecheck, build and test the collaboration server (yhub) +- 👷(ci) check the load-test tooling: swarm, canary, k6 and dashboards - ✨(backend) add mention endpoint with cooldown-limited email notification #2447 - 🚩(setting) add feature flag on Duplicate with Children #2721