From 70904060498f1e7ed319a4192b30ca067472041a Mon Sep 17 00:00:00 2001 From: Louis Chmn Date: Fri, 3 Jul 2026 11:42:45 +0200 Subject: [PATCH 1/3] feat(ci): Improve name and error message of gate step Signed-off-by: Louis Chmn --- .github/workflows/cypress.yml | 3 ++- .github/workflows/playwright.yml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml index 6ef8a30d55a96..04a3dbd763557 100644 --- a/.github/workflows/cypress.yml +++ b/.github/workflows/cypress.yml @@ -38,6 +38,7 @@ permissions: jobs: gate: runs-on: ubuntu-latest-low + name: Fail Crypress tests if labels are missing (force-e2e-tests, 3. to review, or 4. to release) steps: - name: Evaluate e2e tests execution conditions id: gate-e2e @@ -61,7 +62,7 @@ jobs: if (hasForceLabel || hasToReviewLabel || hasToReleaseLabel || cypressTouched) { return } else { - core.setFailed('Skipping Cypress: draft state, missing labels or no cypress path changes.') + core.setFailed('Skipping Cypress: missing required labels (force-e2e-tests, 3. to review, or 4. to release), or no cypress path changes.') } init: diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 7f36a406a797c..8aa395cca8c78 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -19,6 +19,7 @@ permissions: jobs: gate: runs-on: ubuntu-latest-low + name: Fail Playwright tests if labels are missing (force-e2e-tests, 3. to review, or 4. to release) steps: - name: Evaluate e2e tests execution conditions id: gate-e2e @@ -42,7 +43,7 @@ jobs: if (hasForceLabel || hasToReviewLabel || hasToReleaseLabel || playwrightTouched) { return } else { - core.setFailed('Skipping Playwright: draft state, missing labels or no playwright path changes.') + core.setFailed('Skipping Playwright: missing required labels (force-e2e-tests, 3. to review, or 4. to release), or no playwright path changes.') } playwright-setup: From 76dee27b6d717bf1bcb358a31652bca6ab0bea66 Mon Sep 17 00:00:00 2001 From: Louis Chmn Date: Fri, 3 Jul 2026 13:46:05 +0200 Subject: [PATCH 2/3] fix(ci): React to unlabeled event for e2e tests Signed-off-by: Louis Chmn --- .github/workflows/cypress.yml | 21 +-- .github/workflows/playwright.yml | 253 ++++++++++++++++--------------- 2 files changed, 138 insertions(+), 136 deletions(-) diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml index 04a3dbd763557..f297e8a3461f3 100644 --- a/.github/workflows/cypress.yml +++ b/.github/workflows/cypress.yml @@ -16,6 +16,7 @@ on: - reopened - ready_for_review - labeled + - unlabeled concurrency: group: cypress-${{ github.head_ref || github.run_id }} @@ -89,7 +90,7 @@ jobs: id: check_composer uses: andstor/file-existence-action@558493d6c74bf472d87c84eab196434afc2fa029 # v3.1.0 with: - files: 'composer.json' + files: "composer.json" - name: Install composer dependencies if: steps.check_composer.outputs.files_exists == 'true' @@ -99,8 +100,8 @@ jobs: uses: skjnldsv/read-package-engines-version-actions@06d6baf7d8f41934ab630e97d9e6c0bc9c9ac5e4 # v3 id: versions with: - fallbackNode: '^24' - fallbackNpm: '^11.3' + fallbackNode: "^24" + fallbackNpm: "^11.3" - name: Set up node ${{ steps.versions.outputs.nodeVersion }} uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 @@ -142,7 +143,7 @@ jobs: matrix: # Run multiple copies of the current job in parallel # Please increase the number or runners as your tests suite grows (0 based index for e2e tests) - containers: ['setup', '0', '1', '2', '3', '4'] + containers: ["setup", "0", "1", "2", "3", "4"] # Hack as strategy.job-total includes the "setup" and GitHub does not allow math expressions # Always align this number with the total of e2e runners (max. index + 1) total-containers: [5] @@ -152,7 +153,7 @@ jobs: # Only start mysql if we are running the setup tests image: ${{matrix.containers == 'setup' && 'ghcr.io/nextcloud/continuous-integration-mysql-8.4:latest' || ''}} # zizmor: ignore[unpinned-images] ports: - - '3306/tcp' + - "3306/tcp" env: MYSQL_ROOT_PASSWORD: rootpassword MYSQL_USER: oc_autotest @@ -164,7 +165,7 @@ jobs: # Only start mariadb if we are running the setup tests image: ${{matrix.containers == 'setup' && 'mariadb:11.4' || ''}} # zizmor: ignore[unpinned-images] ports: - - '3306/tcp' + - "3306/tcp" env: MYSQL_ROOT_PASSWORD: rootpassword MYSQL_USER: oc_autotest @@ -176,7 +177,7 @@ jobs: # Only start postgres if we are running the setup tests image: ${{matrix.containers == 'setup' && 'ghcr.io/nextcloud/continuous-integration-postgres-17:latest' || ''}} # zizmor: ignore[unpinned-images] ports: - - '5432/tcp' + - "5432/tcp" env: POSTGRES_USER: root POSTGRES_PASSWORD: rootpassword @@ -187,10 +188,10 @@ jobs: # Only start oracle if we are running the setup tests image: ${{matrix.containers == 'setup' && 'ghcr.io/gvenzl/oracle-free:23' || ''}} # zizmor: ignore[unpinned-images] ports: - - '1521' + - "1521" env: ORACLE_PASSWORD: oracle - options: --health-cmd healthcheck.sh --health-interval 20s --health-timeout 10s --health-retries 10 + options: --health-cmd healthcheck.sh --health-interval 20s --health-timeout 10s --health-retries 10 name: runner ${{ matrix.containers }} @@ -215,7 +216,7 @@ jobs: if: steps.cache.outputs.cache-hit != 'true' uses: andstor/file-existence-action@558493d6c74bf472d87c84eab196434afc2fa029 # v3.1.0 with: - files: 'composer.json' + files: "composer.json" - name: Install composer dependencies if: steps.check_composer.outputs.files_exists == 'true' && steps.cache.outputs.cache-hit != 'true' diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 8aa395cca8c78..5bbd03348d9a3 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -12,6 +12,7 @@ on: - reopened - ready_for_review - labeled + - unlabeled permissions: contents: read @@ -52,28 +53,28 @@ jobs: runs-on: ubuntu-latest needs: gate steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - persist-credentials: false - submodules: true # for 3rdparty - - name: Read package.json - uses: nextcloud-libraries/parse-package-engines-action@122ae05d4257008180a514e1ddeb0c1b9d094bdd # v0.1.0 - id: versions - - name: Set up node - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 - with: - node-version: ${{ steps.versions.outputs.node-version }} - - name: Set up npm - run: npm i -g 'npm@${{ steps.versions.outputs.package-manager-version }}' - - name: Install dependencies and build - run: | - npm ci - npm run build --if-present - - name: Save context - uses: buildjet/cache/save@3e70d19e31d6a8030aeddf6ed8dbe601f94d09f4 # v4.0.2 - with: - key: playwright-context-${{ github.run_id }} - path: ./ + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + submodules: true # for 3rdparty + - name: Read package.json + uses: nextcloud-libraries/parse-package-engines-action@122ae05d4257008180a514e1ddeb0c1b9d094bdd # v0.1.0 + id: versions + - name: Set up node + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: ${{ steps.versions.outputs.node-version }} + - name: Set up npm + run: npm i -g 'npm@${{ steps.versions.outputs.package-manager-version }}' + - name: Install dependencies and build + run: | + npm ci + npm run build --if-present + - name: Save context + uses: buildjet/cache/save@3e70d19e31d6a8030aeddf6ed8dbe601f94d09f4 # v4.0.2 + with: + key: playwright-context-${{ github.run_id }} + path: ./ playwright-tests: needs: [gate, playwright-setup] @@ -90,63 +91,63 @@ jobs: package-manager-version: ${{ steps.versions.outputs.package-manager-version }} steps: - - name: Restore context - id: cache - uses: buildjet/cache/restore@3e70d19e31d6a8030aeddf6ed8dbe601f94d09f4 # v4.0.2 - with: - key: playwright-context-${{ github.run_id }} - path: ./ - - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - if: steps.cache.outputs.cache-hit != 'true' - with: - persist-credentials: false - submodules: true # for 3rdparty - - - name: Read package.json - if: steps.cache.outputs.cache-hit != 'true' - uses: nextcloud-libraries/parse-package-engines-action@122ae05d4257008180a514e1ddeb0c1b9d094bdd # v0.1.0 - id: versions - - - name: Set up node - if: steps.cache.outputs.cache-hit != 'true' - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 - with: - node-version: ${{ steps.versions.outputs.node-version }} - - - name: Set up npm - if: steps.cache.outputs.cache-hit != 'true' - run: npm i -g 'npm@${{ steps.versions.outputs.package-manager-version }}' - - - name: Install dependencies and build - if: steps.cache.outputs.cache-hit != 'true' - run: | - npm ci - npm run build --if-present - - - name: Install Playwright browsers - run: npx playwright install --with-deps - - - name: Run Playwright tests - run: npm run playwright -- --shard='${{ matrix.shardIndex }}/${{ matrix.shardTotal }}' - - - name: Show logs - if: failure() - run: | - for id in $(docker ps -aq); do - docker container inspect "$id" --format '=== Logs for container {{.Name}} ===' - docker logs "$id" >> nextcloud.log - done - echo '=== Nextcloud server logs ===' - docker exec nextcloud-e2e-test-server_server cat data/nextcloud.log - - - name: Upload blob report to GitHub Actions Artifacts - if: ${{ !cancelled() }} - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: blob-report-${{ matrix.shardIndex }} - path: blob-report - retention-days: 1 + - name: Restore context + id: cache + uses: buildjet/cache/restore@3e70d19e31d6a8030aeddf6ed8dbe601f94d09f4 # v4.0.2 + with: + key: playwright-context-${{ github.run_id }} + path: ./ + + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + if: steps.cache.outputs.cache-hit != 'true' + with: + persist-credentials: false + submodules: true # for 3rdparty + + - name: Read package.json + if: steps.cache.outputs.cache-hit != 'true' + uses: nextcloud-libraries/parse-package-engines-action@122ae05d4257008180a514e1ddeb0c1b9d094bdd # v0.1.0 + id: versions + + - name: Set up node + if: steps.cache.outputs.cache-hit != 'true' + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: ${{ steps.versions.outputs.node-version }} + + - name: Set up npm + if: steps.cache.outputs.cache-hit != 'true' + run: npm i -g 'npm@${{ steps.versions.outputs.package-manager-version }}' + + - name: Install dependencies and build + if: steps.cache.outputs.cache-hit != 'true' + run: | + npm ci + npm run build --if-present + + - name: Install Playwright browsers + run: npx playwright install --with-deps + + - name: Run Playwright tests + run: npm run playwright -- --shard='${{ matrix.shardIndex }}/${{ matrix.shardTotal }}' + + - name: Show logs + if: failure() + run: | + for id in $(docker ps -aq); do + docker container inspect "$id" --format '=== Logs for container {{.Name}} ===' + docker logs "$id" >> nextcloud.log + done + echo '=== Nextcloud server logs ===' + docker exec nextcloud-e2e-test-server_server cat data/nextcloud.log + + - name: Upload blob report to GitHub Actions Artifacts + if: ${{ !cancelled() }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: blob-report-${{ matrix.shardIndex }} + path: blob-report + retention-days: 1 merge-reports: # Merge reports after playwright-tests, even if some shards have failed @@ -155,53 +156,53 @@ jobs: runs-on: ubuntu-latest-low steps: - - name: Restore context - id: cache - uses: buildjet/cache/restore@3e70d19e31d6a8030aeddf6ed8dbe601f94d09f4 # v4.0.2 - with: - key: playwright-context-${{ github.run_id }} - path: ./ - - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - if: steps.cache.outputs.cache-hit != 'true' - with: - persist-credentials: false - - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 - if: steps.cache.outputs.cache-hit != 'true' - with: - node-version: ${{ needs.playwright-tests.outputs.node-version }} - - - name: Set up npm - if: steps.cache.outputs.cache-hit != 'true' - run: npm i -g 'npm@${{ needs.playwright-tests.outputs.package-manager-version }}' - - - name: Install dependencies - if: steps.cache.outputs.cache-hit != 'true' - run: npm ci - - - name: Download blob reports from GitHub Actions Artifacts - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - path: all-blob-reports - pattern: blob-report-* - merge-multiple: true - - - name: Merge into HTML Report - run: npx playwright merge-reports --config tests/playwright/merge.config.ts --reporter html,github ./all-blob-reports - - - name: Upload HTML report - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: html-report--attempt-${{ github.run_attempt }} - path: playwright-report - retention-days: 7 - - - name: Show the logs - run: | - echo 'To view the report:' - echo ' 1. Extract the folder from the zip file' - echo ' 2. run "npx playwright show-report name-of-my-extracted-playwright-report"' + - name: Restore context + id: cache + uses: buildjet/cache/restore@3e70d19e31d6a8030aeddf6ed8dbe601f94d09f4 # v4.0.2 + with: + key: playwright-context-${{ github.run_id }} + path: ./ + + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + if: steps.cache.outputs.cache-hit != 'true' + with: + persist-credentials: false + + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + if: steps.cache.outputs.cache-hit != 'true' + with: + node-version: ${{ needs.playwright-tests.outputs.node-version }} + + - name: Set up npm + if: steps.cache.outputs.cache-hit != 'true' + run: npm i -g 'npm@${{ needs.playwright-tests.outputs.package-manager-version }}' + + - name: Install dependencies + if: steps.cache.outputs.cache-hit != 'true' + run: npm ci + + - name: Download blob reports from GitHub Actions Artifacts + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + path: all-blob-reports + pattern: blob-report-* + merge-multiple: true + + - name: Merge into HTML Report + run: npx playwright merge-reports --config tests/playwright/merge.config.ts --reporter html,github ./all-blob-reports + + - name: Upload HTML report + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: html-report--attempt-${{ github.run_attempt }} + path: playwright-report + retention-days: 7 + + - name: Show the logs + run: | + echo 'To view the report:' + echo ' 1. Extract the folder from the zip file' + echo ' 2. run "npx playwright show-report name-of-my-extracted-playwright-report"' summary: permissions: From f933725f0a51189c84192f414c42f22dab5b550f Mon Sep 17 00:00:00 2001 From: Louis Chmn Date: Fri, 3 Jul 2026 14:08:48 +0200 Subject: [PATCH 3/3] fix(ci): Limit number of fail steps for e2e workflows Signed-off-by: Louis Chmn --- .github/workflows/cypress.yml | 4 ---- .github/workflows/playwright.yml | 9 ++------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml index f297e8a3461f3..1ce938bccbdd2 100644 --- a/.github/workflows/cypress.yml +++ b/.github/workflows/cypress.yml @@ -291,11 +291,7 @@ jobs: summary: runs-on: ubuntu-latest-low needs: [init, cypress] - - if: always() - name: cypress-summary - steps: - name: Summary status run: if ${{ needs.init.result != 'success' || ( needs.cypress.result != 'success' && needs.cypress.result != 'skipped' ) }}; then exit 1; fi diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 5bbd03348d9a3..a5ecb5c9fb32f 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -151,9 +151,8 @@ jobs: merge-reports: # Merge reports after playwright-tests, even if some shards have failed - if: ${{ !cancelled() }} - needs: [gate, playwright-tests] - + if: ${{ !cancelled() && steps.gate.conclusion != 'failure' }} + needs: [playwright-tests] runs-on: ubuntu-latest-low steps: - name: Restore context @@ -209,11 +208,7 @@ jobs: contents: none runs-on: ubuntu-latest-low needs: [gate, playwright-tests] - - if: always() - name: playwright-test-summary - steps: - name: Summary status run: if ${{ needs.playwright-tests.result != 'success' }}; then exit 1; fi