[release-10.11] Automate setup-go-work as a dependency for Make targets (#35780)

* Automate setup-go-work as a dependency for Make targets (#35476)

* automate setup-go-work

It's all to easy to forget to `make setup-go-work`, only to run into mysterious build failures. Let's default to doing this automatically, unless `SKIP_SETUP_GO_WORK` is true (or the legacy `IGNORE_GO_WORK_IF_EXISTS`, which was oddly named, since we can't actually ignore it.)

* Make setup-go-work recipe fail-fast with set -e

* ci: post success to required e2e status contexts when no relevant changes (#35880)

* ci: post correct skip status from within cypress/playwright reusable workflows

The 'Required Status Checks' ruleset requires e2e-test/cypress-full/enterprise
and e2e-test/playwright-full/enterprise on master and release-*.* branches.
When a PR has no E2E-relevant changes, the jobs were silently skipped, leaving
required statuses unset and the PR permanently blocked.

Architecture fix: instead of a separate skip-e2e job in the caller that
hardcodes status context names, the skip logic now lives inside the reusable
workflows that already own and compute those context names.

Changes:
- e2e-tests-cypress.yml: add should_run input (default 'true') + skip job
  that uses the dynamically-computed context_name when should_run == 'false'
- e2e-tests-playwright.yml: same pattern
- e2e-tests-ci.yml: change e2e-cypress/e2e-playwright job conditions from
  should_run == 'true' to PR_NUMBER != '' (always run when there's a PR),
  pass should_run as input to both reusable workflows

* Add E2E template workflows for Cypress and Playwright

* Add check-e2e-test-only action for E2E workflow

* Fix: Remove circular E2E workflow file check - skip tests when only CI files change

* Add pull_request trigger to E2E workflow - run automatically on PR events

* Fix resolve-pr to use github.event context for automatic pull_request trigger

* Fix checkout condition to work with pull_request events

* Fix: Remove orphaned fi statement in check-changes script

---------

Co-authored-by: yasser khan <attitude3cena.yf@gmail.com>
Этот коммит содержится в:
Jesse Hallam
2026-04-02 10:45:24 -03:00
коммит произвёл GitHub
родитель 075d975ca7
Коммит 70d1794916
9 изменённых файлов: 1999 добавлений и 59 удалений

229
.github/workflows/e2e-tests-ci.yml поставляемый
Просмотреть файл

@@ -1,19 +1,230 @@
---
name: E2E Smoketests
name: E2E Tests (pull request)
on:
# For PRs, this workflow gets triggered from the Argo Events platform.
# Check the following repo for details: https://github.com/mattermost/delivery-platform
pull_request:
types:
- opened
- synchronize
- reopened
# Argo Events Trigger (automated):
# - Triggered by: Enterprise CI/docker-image status check (success)
# - Payload: { ref: "<branch>", inputs: { commit_sha: "<sha>" } }
# - Uses commit-specific docker image
# - Checks for relevant file changes before running tests
#
# Manual Trigger:
# - Enter PR number only - commit SHA is resolved automatically from PR head
# - Uses commit-specific docker image
# - E2E tests always run (no file change check)
#
workflow_dispatch:
inputs:
commit_sha:
pr_number:
description: "PR number to test (for manual triggers)"
type: string
required: true
required: false
commit_sha:
description: "Commit SHA to test (for Argo Events)"
type: string
required: false
jobs:
e2e-smoketest:
uses: ./.github/workflows/e2e-tests-ci-template.yml
resolve-pr:
runs-on: ubuntu-24.04
outputs:
PR_NUMBER: "${{ steps.resolve.outputs.PR_NUMBER }}"
COMMIT_SHA: "${{ steps.resolve.outputs.COMMIT_SHA }}"
SERVER_IMAGE_TAG: "${{ steps.e2e-check.outputs.image_tag }}"
steps:
- name: ci/checkout-repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- name: ci/resolve-pr-and-commit
id: resolve
env:
GH_TOKEN: ${{ github.token }}
INPUT_PR_NUMBER: ${{ inputs.pr_number || github.event.pull_request.number }}
INPUT_COMMIT_SHA: ${{ inputs.commit_sha || github.event.pull_request.head.sha }}
run: |
# Validate inputs
if [ -n "$INPUT_PR_NUMBER" ] && ! [[ "$INPUT_PR_NUMBER" =~ ^[0-9]+$ ]]; then
echo "::error::Invalid PR number format. Must be numeric."
exit 1
fi
if [ -n "$INPUT_COMMIT_SHA" ] && ! [[ "$INPUT_COMMIT_SHA" =~ ^[a-f0-9]{7,40}$ ]]; then
echo "::error::Invalid commit SHA format. Must be 7-40 hex characters."
exit 1
fi
# Manual trigger: PR number provided, resolve commit SHA from PR head
if [ -n "$INPUT_PR_NUMBER" ]; then
echo "Manual trigger: resolving commit SHA from PR #${INPUT_PR_NUMBER}"
PR_DATA=$(gh api "repos/${{ github.repository }}/pulls/${INPUT_PR_NUMBER}")
COMMIT_SHA=$(echo "$PR_DATA" | jq -r '.head.sha')
if [ -z "$COMMIT_SHA" ] || [ "$COMMIT_SHA" = "null" ]; then
echo "::error::Could not resolve commit SHA for PR #${INPUT_PR_NUMBER}"
exit 1
fi
echo "PR_NUMBER=${INPUT_PR_NUMBER}" >> $GITHUB_OUTPUT
echo "COMMIT_SHA=${COMMIT_SHA}" >> $GITHUB_OUTPUT
exit 0
fi
# Argo Events trigger: commit SHA provided, resolve PR number
if [ -n "$INPUT_COMMIT_SHA" ]; then
echo "Automated trigger: resolving PR number from commit ${INPUT_COMMIT_SHA}"
PR_DATA=$(gh api "repos/${{ github.repository }}/commits/${INPUT_COMMIT_SHA}/pulls" \
--jq '.[0] // empty' 2>/dev/null || echo "")
PR_NUMBER=$(echo "$PR_DATA" | jq -r '.number // empty' 2>/dev/null || echo "")
if [ -z "$PR_NUMBER" ]; then
echo "::error::No PR found for commit ${INPUT_COMMIT_SHA}. This workflow is for PRs only."
exit 1
fi
echo "Found PR #${PR_NUMBER} for commit ${INPUT_COMMIT_SHA}"
# Skip if PR is already merged to master or a release branch.
# The e2e-tests-on-merge workflow handles post-merge E2E tests.
PR_MERGED=$(echo "$PR_DATA" | jq -r '.merged_at // empty' 2>/dev/null || echo "")
PR_BASE_REF=$(echo "$PR_DATA" | jq -r '.base.ref // empty' 2>/dev/null || echo "")
if [ -n "$PR_MERGED" ]; then
if [ "$PR_BASE_REF" = "master" ] || [[ "$PR_BASE_REF" =~ ^release-[0-9]+\.[0-9]+$ ]]; then
echo "PR #${PR_NUMBER} is already merged to ${PR_BASE_REF}. Skipping - handled by e2e-tests-on-merge workflow."
echo "PR_NUMBER=" >> $GITHUB_OUTPUT
echo "COMMIT_SHA=" >> $GITHUB_OUTPUT
exit 0
fi
fi
echo "PR_NUMBER=${PR_NUMBER}" >> $GITHUB_OUTPUT
echo "COMMIT_SHA=${INPUT_COMMIT_SHA}" >> $GITHUB_OUTPUT
exit 0
fi
# Neither provided
echo "::error::Either pr_number or commit_sha must be provided"
exit 1
- name: ci/check-e2e-test-only
if: steps.resolve.outputs.PR_NUMBER != ''
id: e2e-check
uses: ./.github/actions/check-e2e-test-only
with:
pr_number: ${{ steps.resolve.outputs.PR_NUMBER }}
check-changes:
needs: resolve-pr
if: needs.resolve-pr.outputs.PR_NUMBER != ''
runs-on: ubuntu-24.04
outputs:
should_run: "${{ steps.check.outputs.should_run }}"
steps:
- name: ci/checkout-repo
if: inputs.commit_sha != '' || github.event.pull_request
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ needs.resolve-pr.outputs.COMMIT_SHA }}
fetch-depth: 0
- name: ci/check-relevant-changes
id: check
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ needs.resolve-pr.outputs.PR_NUMBER }}
COMMIT_SHA: ${{ needs.resolve-pr.outputs.COMMIT_SHA }}
INPUT_PR_NUMBER: ${{ inputs.pr_number }}
run: |
# Manual trigger (pr_number provided): always run E2E tests
if [ -n "$INPUT_PR_NUMBER" ]; then
echo "Manual trigger detected - skipping file change check"
echo "should_run=true" >> $GITHUB_OUTPUT
exit 0
fi
# Automated trigger (commit_sha provided): check for relevant file changes
echo "Automated trigger detected - checking for relevant file changes"
# Get the base branch of the PR
BASE_SHA=$(gh api "repos/${{ github.repository }}/pulls/${PR_NUMBER}" --jq '.base.sha')
# Get changed files between base and head
CHANGED_FILES=$(git diff --name-only "${BASE_SHA}...${COMMIT_SHA}")
echo "Changed files:"
echo "$CHANGED_FILES"
# Check for relevant changes
SHOULD_RUN="false"
# Check for server Go files
if echo "$CHANGED_FILES" | grep -qE '^server/.*\.go$'; then
echo "Found server Go file changes"
SHOULD_RUN="true"
fi
# Check for webapp ts/js/tsx/jsx files
if echo "$CHANGED_FILES" | grep -qE '^webapp/.*\.(ts|tsx|js|jsx)$'; then
echo "Found webapp TypeScript/JavaScript file changes"
SHOULD_RUN="true"
fi
# Check for e2e-tests ts/js/tsx/jsx files
if echo "$CHANGED_FILES" | grep -qE '^e2e-tests/.*\.(ts|tsx|js|jsx)$'; then
echo "Found e2e-tests TypeScript/JavaScript file changes"
SHOULD_RUN="true"
fi
echo "should_run=${SHOULD_RUN}" >> $GITHUB_OUTPUT
echo "Should run E2E tests: ${SHOULD_RUN}"
e2e-cypress:
needs:
- resolve-pr
- check-changes
if: needs.resolve-pr.outputs.PR_NUMBER != ''
permissions:
statuses: write
uses: ./.github/workflows/e2e-tests-cypress.yml
with:
commit_sha: "${{ inputs.commit_sha }}"
status_check_context: "E2E Tests/smoketests"
commit_sha: "${{ needs.resolve-pr.outputs.COMMIT_SHA }}"
server: "onprem"
server_image_tag: "${{ needs.resolve-pr.outputs.SERVER_IMAGE_TAG }}"
enable_reporting: true
report_type: "PR"
pr_number: "${{ needs.resolve-pr.outputs.PR_NUMBER }}"
should_run: "${{ needs.check-changes.outputs.should_run }}"
secrets:
MM_LICENSE: "${{ secrets.MM_E2E_TEST_LICENSE_ONPREM_ENT }}"
AUTOMATION_DASHBOARD_URL: "${{ secrets.MM_E2E_AUTOMATION_DASHBOARD_URL }}"
AUTOMATION_DASHBOARD_TOKEN: "${{ secrets.MM_E2E_AUTOMATION_DASHBOARD_TOKEN }}"
PUSH_NOTIFICATION_SERVER: "${{ secrets.MM_E2E_PUSH_NOTIFICATION_SERVER }}"
REPORT_WEBHOOK_URL: "${{ secrets.MM_E2E_REPORT_WEBHOOK_URL }}"
CWS_URL: "${{ secrets.MM_E2E_CWS_URL }}"
CWS_EXTRA_HTTP_HEADERS: "${{ secrets.MM_E2E_CWS_EXTRA_HTTP_HEADERS }}"
e2e-playwright:
needs:
- resolve-pr
- check-changes
if: needs.resolve-pr.outputs.PR_NUMBER != ''
permissions:
statuses: write
uses: ./.github/workflows/e2e-tests-playwright.yml
with:
commit_sha: "${{ needs.resolve-pr.outputs.COMMIT_SHA }}"
server: "onprem"
server_image_tag: "${{ needs.resolve-pr.outputs.SERVER_IMAGE_TAG }}"
enable_reporting: true
report_type: "PR"
pr_number: "${{ needs.resolve-pr.outputs.PR_NUMBER }}"
should_run: "${{ needs.check-changes.outputs.should_run }}"
secrets:
MM_LICENSE: "${{ secrets.MM_E2E_TEST_LICENSE_ONPREM_ENT }}"
AWS_ACCESS_KEY_ID: "${{ secrets.CYPRESS_AWS_ACCESS_KEY_ID }}"
AWS_SECRET_ACCESS_KEY: "${{ secrets.CYPRESS_AWS_SECRET_ACCESS_KEY }}"
REPORT_WEBHOOK_URL: "${{ secrets.MM_E2E_REPORT_WEBHOOK_URL }}"

649
.github/workflows/e2e-tests-cypress-template.yml поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,649 @@
---
name: E2E Tests - Cypress Template
on:
workflow_call:
inputs:
# Test configuration
test_type:
description: "Type of test run (smoke or full)"
type: string
required: true
test_filter:
description: "Test filter arguments"
type: string
required: true
workers:
description: "Number of parallel workers"
type: number
required: false
default: 1
enabled_docker_services:
description: "Space-separated list of docker services to enable"
type: string
required: false
default: "postgres inbucket"
# Common build variables
commit_sha:
type: string
required: true
branch:
type: string
required: true
build_id:
type: string
required: true
server_image_tag:
description: "Server image tag (e.g., master or short SHA)"
type: string
required: true
server:
type: string
required: false
default: onprem
server_edition:
description: "Server edition: enterprise (default), fips, or team"
type: string
required: false
default: enterprise
server_image_repo:
description: "Docker registry: mattermostdevelopment (default) or mattermost"
type: string
required: false
default: mattermostdevelopment
server_image_aliases:
description: "Comma-separated alias tags for description (e.g., 'release-11.4, release-11')"
type: string
required: false
# Reporting options
enable_reporting:
type: boolean
required: false
default: false
report_type:
type: string
required: false
ref_branch:
description: "Source branch name for webhook messages (e.g., 'master' or 'release-11.4')"
type: string
required: false
pr_number:
type: string
required: false
# Commit status configuration
context_name:
description: "GitHub commit status context name"
type: string
required: true
outputs:
passed:
description: "Number of passed tests"
value: ${{ jobs.report.outputs.passed }}
failed:
description: "Number of failed tests"
value: ${{ jobs.report.outputs.failed }}
status_check_url:
description: "URL to test results"
value: ${{ jobs.generate-test-cycle.outputs.status_check_url }}
secrets:
MM_LICENSE:
required: false
AUTOMATION_DASHBOARD_URL:
required: false
AUTOMATION_DASHBOARD_TOKEN:
required: false
PUSH_NOTIFICATION_SERVER:
required: false
REPORT_WEBHOOK_URL:
required: false
CWS_URL:
required: false
CWS_EXTRA_HTTP_HEADERS:
required: false
env:
SERVER_IMAGE: "${{ inputs.server_image_repo }}/${{ inputs.server_edition == 'fips' && 'mattermost-enterprise-fips-edition' || inputs.server_edition == 'team' && 'mattermost-team-edition' || 'mattermost-enterprise-edition' }}:${{ inputs.server_image_tag }}"
jobs:
update-initial-status:
runs-on: ubuntu-24.04
steps:
- name: ci/set-initial-status
uses: mattermost/actions/delivery/update-commit-status@f324ac89b05cc3511cb06e60642ac2fb829f0a63
env:
GITHUB_TOKEN: ${{ github.token }}
with:
repository_full_name: ${{ github.repository }}
commit_sha: ${{ inputs.commit_sha }}
context: ${{ inputs.context_name }}
description: "tests running, image_tag:${{ inputs.server_image_tag }}${{ inputs.server_image_aliases && format(' ({0})', inputs.server_image_aliases) || '' }}"
status: pending
generate-test-cycle:
runs-on: ubuntu-24.04
outputs:
status_check_url: "${{ steps.generate-cycle.outputs.status_check_url }}"
workers: "${{ steps.generate-workers.outputs.workers }}"
start_time: "${{ steps.generate-workers.outputs.start_time }}"
steps:
- name: ci/generate-workers
id: generate-workers
run: |
echo "workers=$(jq -nc '[range(${{ inputs.workers }})]')" >> $GITHUB_OUTPUT
echo "start_time=$(date +%s)" >> $GITHUB_OUTPUT
- name: ci/checkout-repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ inputs.commit_sha }}
fetch-depth: 0
- name: ci/setup-node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: ".nvmrc"
cache: npm
cache-dependency-path: "e2e-tests/cypress/package-lock.json"
- name: ci/generate-test-cycle
id: generate-cycle
working-directory: e2e-tests
env:
AUTOMATION_DASHBOARD_URL: "${{ secrets.AUTOMATION_DASHBOARD_URL }}"
AUTOMATION_DASHBOARD_TOKEN: "${{ secrets.AUTOMATION_DASHBOARD_TOKEN }}"
BRANCH: "${{ inputs.branch }}-${{ inputs.test_type }}"
BUILD_ID: "${{ inputs.build_id }}"
TEST: cypress
TEST_FILTER: "${{ inputs.test_filter }}"
run: |
set -e -o pipefail
make generate-test-cycle | tee generate-test-cycle.out
TEST_CYCLE_ID=$(sed -nE "s/^.*id: '([^']+)'.*$/\1/p" <generate-test-cycle.out)
if [ -n "$TEST_CYCLE_ID" ]; then
echo "status_check_url=https://automation-dashboard.vercel.app/cycles/${TEST_CYCLE_ID}" >> $GITHUB_OUTPUT
else
echo "status_check_url=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> $GITHUB_OUTPUT
fi
run-tests:
runs-on: ubuntu-24.04
timeout-minutes: 30
continue-on-error: ${{ inputs.workers > 1 }}
needs:
- generate-test-cycle
if: needs.generate-test-cycle.result == 'success'
strategy:
fail-fast: false
matrix:
worker_index: ${{ fromJSON(needs.generate-test-cycle.outputs.workers) }}
defaults:
run:
working-directory: e2e-tests
env:
AUTOMATION_DASHBOARD_URL: "${{ secrets.AUTOMATION_DASHBOARD_URL }}"
AUTOMATION_DASHBOARD_TOKEN: "${{ secrets.AUTOMATION_DASHBOARD_TOKEN }}"
SERVER: "${{ inputs.server }}"
MM_LICENSE: "${{ secrets.MM_LICENSE }}"
ENABLED_DOCKER_SERVICES: "${{ inputs.enabled_docker_services }}"
TEST: cypress
TEST_FILTER: "${{ inputs.test_filter }}"
BRANCH: "${{ inputs.branch }}-${{ inputs.test_type }}"
BUILD_ID: "${{ inputs.build_id }}"
CI_BASE_URL: "${{ inputs.test_type }}-test-${{ matrix.worker_index }}"
CYPRESS_pushNotificationServer: "${{ secrets.PUSH_NOTIFICATION_SERVER }}"
CWS_URL: "${{ secrets.CWS_URL }}"
CWS_EXTRA_HTTP_HEADERS: "${{ secrets.CWS_EXTRA_HTTP_HEADERS }}"
steps:
- name: ci/checkout-repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ inputs.commit_sha }}
fetch-depth: 0
- name: ci/setup-node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: ".nvmrc"
cache: npm
cache-dependency-path: "e2e-tests/cypress/package-lock.json"
- name: ci/run-tests
run: |
make cloud-init
make
- name: ci/cloud-teardown
if: always()
run: make cloud-teardown
- name: ci/upload-results
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
if: always()
with:
name: cypress-${{ inputs.test_type }}-${{ inputs.server_edition }}-results-${{ matrix.worker_index }}
path: |
e2e-tests/cypress/logs/
e2e-tests/cypress/results/
retention-days: 5
calculate-results:
runs-on: ubuntu-24.04
needs:
- generate-test-cycle
- run-tests
if: always() && needs.generate-test-cycle.result == 'success'
outputs:
passed: ${{ steps.calculate.outputs.passed }}
failed: ${{ steps.calculate.outputs.failed }}
pending: ${{ steps.calculate.outputs.pending }}
total_specs: ${{ steps.calculate.outputs.total_specs }}
failed_specs: ${{ steps.calculate.outputs.failed_specs }}
failed_specs_count: ${{ steps.calculate.outputs.failed_specs_count }}
failed_tests: ${{ steps.calculate.outputs.failed_tests }}
commit_status_message: ${{ steps.calculate.outputs.commit_status_message }}
total: ${{ steps.calculate.outputs.total }}
pass_rate: ${{ steps.calculate.outputs.pass_rate }}
color: ${{ steps.calculate.outputs.color }}
test_duration: ${{ steps.calculate.outputs.test_duration }}
end_time: ${{ steps.record-end-time.outputs.end_time }}
steps:
- name: ci/checkout-repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: ci/download-results
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
pattern: cypress-${{ inputs.test_type }}-${{ inputs.server_edition }}-results-*
path: e2e-tests/cypress/
merge-multiple: true
- name: ci/calculate
id: calculate
uses: ./.github/actions/calculate-cypress-results
with:
original-results-path: e2e-tests/cypress/results
- name: ci/record-end-time
id: record-end-time
run: echo "end_time=$(date +%s)" >> $GITHUB_OUTPUT
run-failed-tests:
runs-on: ubuntu-24.04
timeout-minutes: 30
needs:
- generate-test-cycle
- run-tests
- calculate-results
if: >-
always() &&
needs.calculate-results.result == 'success' &&
needs.calculate-results.outputs.failed != '0' &&
fromJSON(needs.calculate-results.outputs.failed_specs_count) <= 20
defaults:
run:
working-directory: e2e-tests
env:
AUTOMATION_DASHBOARD_URL: "${{ secrets.AUTOMATION_DASHBOARD_URL }}"
AUTOMATION_DASHBOARD_TOKEN: "${{ secrets.AUTOMATION_DASHBOARD_TOKEN }}"
SERVER: "${{ inputs.server }}"
MM_LICENSE: "${{ secrets.MM_LICENSE }}"
ENABLED_DOCKER_SERVICES: "${{ inputs.enabled_docker_services }}"
TEST: cypress
BRANCH: "${{ inputs.branch }}-${{ inputs.test_type }}-retest"
BUILD_ID: "${{ inputs.build_id }}-retest"
CYPRESS_pushNotificationServer: "${{ secrets.PUSH_NOTIFICATION_SERVER }}"
CWS_URL: "${{ secrets.CWS_URL }}"
CWS_EXTRA_HTTP_HEADERS: "${{ secrets.CWS_EXTRA_HTTP_HEADERS }}"
steps:
- name: ci/checkout-repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ inputs.commit_sha }}
fetch-depth: 0
- name: ci/setup-node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: ".nvmrc"
cache: npm
cache-dependency-path: "e2e-tests/cypress/package-lock.json"
- name: ci/run-failed-specs
env:
SPEC_FILES: ${{ needs.calculate-results.outputs.failed_specs }}
run: |
echo "Retesting failed specs: $SPEC_FILES"
make cloud-init
make start-server run-specs
- name: ci/cloud-teardown
if: always()
run: make cloud-teardown
- name: ci/upload-retest-results
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
if: always()
with:
name: cypress-${{ inputs.test_type }}-${{ inputs.server_edition }}-retest-results
path: |
e2e-tests/cypress/logs/
e2e-tests/cypress/results/
retention-days: 5
report:
runs-on: ubuntu-24.04
needs:
- generate-test-cycle
- run-tests
- calculate-results
- run-failed-tests
if: always() && needs.calculate-results.result == 'success'
outputs:
passed: "${{ steps.final-results.outputs.passed }}"
failed: "${{ steps.final-results.outputs.failed }}"
commit_status_message: "${{ steps.final-results.outputs.commit_status_message }}"
duration: "${{ steps.duration.outputs.duration }}"
duration_display: "${{ steps.duration.outputs.duration_display }}"
retest_display: "${{ steps.duration.outputs.retest_display }}"
defaults:
run:
working-directory: e2e-tests
steps:
- name: ci/checkout-repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: ci/setup-node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: ".nvmrc"
cache: npm
cache-dependency-path: "e2e-tests/cypress/package-lock.json"
# PATH A: run-failed-tests was skipped (no failures to retest)
- name: ci/download-results-path-a
if: needs.run-failed-tests.result == 'skipped'
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
pattern: cypress-${{ inputs.test_type }}-${{ inputs.server_edition }}-results-*
path: e2e-tests/cypress/
merge-multiple: true
- name: ci/use-previous-calculation
if: needs.run-failed-tests.result == 'skipped'
id: use-previous
run: |
echo "passed=${{ needs.calculate-results.outputs.passed }}" >> $GITHUB_OUTPUT
echo "failed=${{ needs.calculate-results.outputs.failed }}" >> $GITHUB_OUTPUT
echo "pending=${{ needs.calculate-results.outputs.pending }}" >> $GITHUB_OUTPUT
echo "total_specs=${{ needs.calculate-results.outputs.total_specs }}" >> $GITHUB_OUTPUT
echo "failed_specs=${{ needs.calculate-results.outputs.failed_specs }}" >> $GITHUB_OUTPUT
echo "failed_specs_count=${{ needs.calculate-results.outputs.failed_specs_count }}" >> $GITHUB_OUTPUT
echo "commit_status_message=${{ needs.calculate-results.outputs.commit_status_message }}" >> $GITHUB_OUTPUT
echo "total=${{ needs.calculate-results.outputs.total }}" >> $GITHUB_OUTPUT
echo "pass_rate=${{ needs.calculate-results.outputs.pass_rate }}" >> $GITHUB_OUTPUT
echo "color=${{ needs.calculate-results.outputs.color }}" >> $GITHUB_OUTPUT
echo "test_duration=${{ needs.calculate-results.outputs.test_duration }}" >> $GITHUB_OUTPUT
{
echo "failed_tests<<EOF"
echo "${{ needs.calculate-results.outputs.failed_tests }}"
echo "EOF"
} >> $GITHUB_OUTPUT
# PATH B: run-failed-tests ran, need to merge and recalculate
- name: ci/download-original-results
if: needs.run-failed-tests.result != 'skipped'
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
pattern: cypress-${{ inputs.test_type }}-${{ inputs.server_edition }}-results-*
path: e2e-tests/cypress/
merge-multiple: true
- name: ci/download-retest-results
if: needs.run-failed-tests.result != 'skipped'
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: cypress-${{ inputs.test_type }}-${{ inputs.server_edition }}-retest-results
path: e2e-tests/cypress/retest-results/
- name: ci/calculate-results
if: needs.run-failed-tests.result != 'skipped'
id: recalculate
uses: ./.github/actions/calculate-cypress-results
with:
original-results-path: e2e-tests/cypress/results
retest-results-path: e2e-tests/cypress/retest-results/results
# Set final outputs from either path
- name: ci/set-final-results
id: final-results
env:
USE_PREVIOUS_FAILED_TESTS: ${{ steps.use-previous.outputs.failed_tests }}
RECALCULATE_FAILED_TESTS: ${{ steps.recalculate.outputs.failed_tests }}
run: |
if [ "${{ needs.run-failed-tests.result }}" == "skipped" ]; then
echo "passed=${{ steps.use-previous.outputs.passed }}" >> $GITHUB_OUTPUT
echo "failed=${{ steps.use-previous.outputs.failed }}" >> $GITHUB_OUTPUT
echo "pending=${{ steps.use-previous.outputs.pending }}" >> $GITHUB_OUTPUT
echo "total_specs=${{ steps.use-previous.outputs.total_specs }}" >> $GITHUB_OUTPUT
echo "failed_specs=${{ steps.use-previous.outputs.failed_specs }}" >> $GITHUB_OUTPUT
echo "failed_specs_count=${{ steps.use-previous.outputs.failed_specs_count }}" >> $GITHUB_OUTPUT
echo "commit_status_message=${{ steps.use-previous.outputs.commit_status_message }}" >> $GITHUB_OUTPUT
echo "total=${{ steps.use-previous.outputs.total }}" >> $GITHUB_OUTPUT
echo "pass_rate=${{ steps.use-previous.outputs.pass_rate }}" >> $GITHUB_OUTPUT
echo "color=${{ steps.use-previous.outputs.color }}" >> $GITHUB_OUTPUT
echo "test_duration=${{ steps.use-previous.outputs.test_duration }}" >> $GITHUB_OUTPUT
{
echo "failed_tests<<EOF"
echo "$USE_PREVIOUS_FAILED_TESTS"
echo "EOF"
} >> $GITHUB_OUTPUT
else
echo "passed=${{ steps.recalculate.outputs.passed }}" >> $GITHUB_OUTPUT
echo "failed=${{ steps.recalculate.outputs.failed }}" >> $GITHUB_OUTPUT
echo "pending=${{ steps.recalculate.outputs.pending }}" >> $GITHUB_OUTPUT
echo "total_specs=${{ steps.recalculate.outputs.total_specs }}" >> $GITHUB_OUTPUT
echo "failed_specs=${{ steps.recalculate.outputs.failed_specs }}" >> $GITHUB_OUTPUT
echo "failed_specs_count=${{ steps.recalculate.outputs.failed_specs_count }}" >> $GITHUB_OUTPUT
echo "commit_status_message=${{ steps.recalculate.outputs.commit_status_message }}" >> $GITHUB_OUTPUT
echo "total=${{ steps.recalculate.outputs.total }}" >> $GITHUB_OUTPUT
echo "pass_rate=${{ steps.recalculate.outputs.pass_rate }}" >> $GITHUB_OUTPUT
echo "color=${{ steps.recalculate.outputs.color }}" >> $GITHUB_OUTPUT
echo "test_duration=${{ steps.recalculate.outputs.test_duration }}" >> $GITHUB_OUTPUT
{
echo "failed_tests<<EOF"
echo "$RECALCULATE_FAILED_TESTS"
echo "EOF"
} >> $GITHUB_OUTPUT
fi
- name: ci/compute-duration
id: duration
env:
START_TIME: ${{ needs.generate-test-cycle.outputs.start_time }}
FIRST_PASS_END_TIME: ${{ needs.calculate-results.outputs.end_time }}
RETEST_RESULT: ${{ needs.run-failed-tests.result }}
RETEST_SPEC_COUNT: ${{ needs.calculate-results.outputs.failed_specs_count }}
TEST_DURATION: ${{ steps.final-results.outputs.test_duration }}
run: |
NOW=$(date +%s)
ELAPSED=$((NOW - START_TIME))
MINUTES=$((ELAPSED / 60))
SECONDS=$((ELAPSED % 60))
DURATION="${MINUTES}m ${SECONDS}s"
# Compute first-pass and re-run durations
FIRST_PASS_ELAPSED=$((FIRST_PASS_END_TIME - START_TIME))
FP_MIN=$((FIRST_PASS_ELAPSED / 60))
FP_SEC=$((FIRST_PASS_ELAPSED % 60))
FIRST_PASS="${FP_MIN}m ${FP_SEC}s"
if [ "$RETEST_RESULT" != "skipped" ]; then
RERUN_ELAPSED=$((NOW - FIRST_PASS_END_TIME))
RR_MIN=$((RERUN_ELAPSED / 60))
RR_SEC=$((RERUN_ELAPSED % 60))
RUN_BREAKDOWN=" (first-pass: ${FIRST_PASS}, re-run: ${RR_MIN}m ${RR_SEC}s)"
else
RUN_BREAKDOWN=""
fi
# Duration icons: >20m high alert, >15m warning, otherwise clock
if [ "$MINUTES" -ge 20 ]; then
DURATION_DISPLAY=":rotating_light: ${DURATION}${RUN_BREAKDOWN} | test: ${TEST_DURATION}"
elif [ "$MINUTES" -ge 15 ]; then
DURATION_DISPLAY=":warning: ${DURATION}${RUN_BREAKDOWN} | test: ${TEST_DURATION}"
else
DURATION_DISPLAY=":clock3: ${DURATION}${RUN_BREAKDOWN} | test: ${TEST_DURATION}"
fi
# Retest indicator with spec count
if [ "$RETEST_RESULT" != "skipped" ]; then
RETEST_DISPLAY=":repeat: re-run ${RETEST_SPEC_COUNT} spec(s)"
else
RETEST_DISPLAY=""
fi
echo "duration=${DURATION}" >> $GITHUB_OUTPUT
echo "duration_display=${DURATION_DISPLAY}" >> $GITHUB_OUTPUT
echo "retest_display=${RETEST_DISPLAY}" >> $GITHUB_OUTPUT
- name: ci/upload-combined-results
if: inputs.workers > 1
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: cypress-${{ inputs.test_type }}-${{ inputs.server_edition }}-results
path: |
e2e-tests/cypress/logs/
e2e-tests/cypress/results/
- name: ci/publish-report
if: inputs.enable_reporting && env.REPORT_WEBHOOK_URL != ''
env:
REPORT_WEBHOOK_URL: ${{ secrets.REPORT_WEBHOOK_URL }}
COMMIT_STATUS_MESSAGE: ${{ steps.final-results.outputs.commit_status_message }}
COLOR: ${{ steps.final-results.outputs.color }}
REPORT_URL: ${{ needs.generate-test-cycle.outputs.status_check_url }}
TEST_TYPE: ${{ inputs.test_type }}
REPORT_TYPE: ${{ inputs.report_type }}
COMMIT_SHA: ${{ inputs.commit_sha }}
REF_BRANCH: ${{ inputs.ref_branch }}
PR_NUMBER: ${{ inputs.pr_number }}
DURATION_DISPLAY: ${{ steps.duration.outputs.duration_display }}
RETEST_DISPLAY: ${{ steps.duration.outputs.retest_display }}
run: |
# Capitalize test type
TEST_TYPE_CAP=$(echo "$TEST_TYPE" | sed 's/.*/\u&/')
# Build source line based on report type
COMMIT_SHORT="${COMMIT_SHA::7}"
COMMIT_URL="https://github.com/${{ github.repository }}/commit/${COMMIT_SHA}"
if [ "$REPORT_TYPE" = "RELEASE_CUT" ]; then
SOURCE_LINE=":github_round: [${COMMIT_SHORT}](${COMMIT_URL}) on \`${REF_BRANCH}\`"
elif [ "$REPORT_TYPE" = "MASTER" ] || [ "$REPORT_TYPE" = "RELEASE" ]; then
SOURCE_LINE=":git_merge: [${COMMIT_SHORT}](${COMMIT_URL}) on \`${REF_BRANCH}\`"
else
SOURCE_LINE=":open-pull-request: [mattermost-pr-${PR_NUMBER}](https://github.com/${{ github.repository }}/pull/${PR_NUMBER})"
fi
# Build retest part for message
RETEST_PART=""
if [ -n "$RETEST_DISPLAY" ]; then
RETEST_PART=" | ${RETEST_DISPLAY}"
fi
# Build payload with attachments
PAYLOAD=$(cat <<EOF
{
"username": "E2E Test",
"icon_url": "https://mattermost.com/wp-content/uploads/2022/02/icon_WS.png",
"attachments": [{
"color": "${COLOR}",
"text": "**Results - Cypress ${TEST_TYPE_CAP} Tests**\n\n${SOURCE_LINE}\n:docker: \`${{ env.SERVER_IMAGE }}\`\n${COMMIT_STATUS_MESSAGE}${RETEST_PART} | [full report](${REPORT_URL})\n${DURATION_DISPLAY}"
}]
}
EOF
)
# Send to webhook
curl -X POST -H "Content-Type: application/json" -d "$PAYLOAD" "$REPORT_WEBHOOK_URL"
- name: ci/write-job-summary
if: always()
env:
STATUS_CHECK_URL: ${{ needs.generate-test-cycle.outputs.status_check_url }}
TEST_TYPE: ${{ inputs.test_type }}
PASSED: ${{ steps.final-results.outputs.passed }}
FAILED: ${{ steps.final-results.outputs.failed }}
PENDING: ${{ steps.final-results.outputs.pending }}
TOTAL_SPECS: ${{ steps.final-results.outputs.total_specs }}
FAILED_SPECS_COUNT: ${{ steps.final-results.outputs.failed_specs_count }}
FAILED_SPECS: ${{ steps.final-results.outputs.failed_specs }}
COMMIT_STATUS_MESSAGE: ${{ steps.final-results.outputs.commit_status_message }}
FAILED_TESTS: ${{ steps.final-results.outputs.failed_tests }}
DURATION_DISPLAY: ${{ steps.duration.outputs.duration_display }}
RETEST_RESULT: ${{ needs.run-failed-tests.result }}
run: |
{
echo "## E2E Test Results - Cypress ${TEST_TYPE}"
echo ""
if [ "$FAILED" = "0" ]; then
echo "All tests passed: **${PASSED} passed**"
else
echo "<details>"
echo "<summary>${FAILED} failed, ${PASSED} passed</summary>"
echo ""
echo "| Test | File |"
echo "|------|------|"
echo "${FAILED_TESTS}"
echo "</details>"
fi
echo ""
echo "### Calculation Outputs"
echo ""
echo "| Output | Value |"
echo "|--------|-------|"
echo "| passed | ${PASSED} |"
echo "| failed | ${FAILED} |"
echo "| pending | ${PENDING} |"
echo "| total_specs | ${TOTAL_SPECS} |"
echo "| failed_specs_count | ${FAILED_SPECS_COUNT} |"
echo "| commit_status_message | ${COMMIT_STATUS_MESSAGE} |"
echo "| failed_specs | ${FAILED_SPECS:-none} |"
echo "| duration | ${DURATION_DISPLAY} |"
if [ "$RETEST_RESULT" != "skipped" ]; then
echo "| retested | Yes |"
else
echo "| retested | No |"
fi
echo ""
echo "---"
echo "[View Full Report](${STATUS_CHECK_URL})"
} >> $GITHUB_STEP_SUMMARY
- name: ci/assert-results
run: |
[ "${{ steps.final-results.outputs.failed }}" = "0" ]
update-success-status:
runs-on: ubuntu-24.04
if: always() && needs.report.result == 'success' && needs.calculate-results.result == 'success'
needs:
- generate-test-cycle
- calculate-results
- report
steps:
- uses: mattermost/actions/delivery/update-commit-status@f324ac89b05cc3511cb06e60642ac2fb829f0a63
env:
GITHUB_TOKEN: ${{ github.token }}
with:
repository_full_name: ${{ github.repository }}
commit_sha: ${{ inputs.commit_sha }}
context: ${{ inputs.context_name }}
description: "${{ needs.report.outputs.commit_status_message }}, ${{ needs.report.outputs.duration }}, image_tag:${{ inputs.server_image_tag }}${{ inputs.server_image_aliases && format(' ({0})', inputs.server_image_aliases) || '' }}"
status: success
target_url: ${{ needs.generate-test-cycle.outputs.status_check_url }}
update-failure-status:
runs-on: ubuntu-24.04
if: always() && (needs.report.result != 'success' || needs.calculate-results.result != 'success')
needs:
- generate-test-cycle
- calculate-results
- report
steps:
- uses: mattermost/actions/delivery/update-commit-status@f324ac89b05cc3511cb06e60642ac2fb829f0a63
env:
GITHUB_TOKEN: ${{ github.token }}
with:
repository_full_name: ${{ github.repository }}
commit_sha: ${{ inputs.commit_sha }}
context: ${{ inputs.context_name }}
description: "${{ needs.report.outputs.commit_status_message }}, ${{ needs.report.outputs.duration }}, image_tag:${{ inputs.server_image_tag }}${{ inputs.server_image_aliases && format(' ({0})', inputs.server_image_aliases) || '' }}"
status: failure
target_url: ${{ needs.generate-test-cycle.outputs.status_check_url }}

194
.github/workflows/e2e-tests-cypress.yml поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,194 @@
---
name: E2E Tests - Cypress
on:
workflow_call:
inputs:
commit_sha:
type: string
required: true
enable_reporting:
type: boolean
required: false
default: false
server:
type: string
required: false
default: onprem
report_type:
type: string
required: false
pr_number:
type: string
required: false
server_image_tag:
type: string
required: false
description: "Server image tag (e.g., master or short SHA)"
server_edition:
type: string
required: false
description: "Server edition: enterprise (default), fips, or team"
server_image_repo:
type: string
required: false
default: mattermostdevelopment
description: "Docker registry: mattermostdevelopment (default) or mattermost"
server_image_aliases:
type: string
required: false
description: "Comma-separated alias tags for context name (e.g., 'release-11.4, release-11')"
ref_branch:
type: string
required: false
description: "Source branch name for webhook messages (e.g., 'master' or 'release-11.4')"
should_run:
type: string
required: false
default: "true"
description: "Set to 'false' to skip tests and post a success status without running E2E"
secrets:
MM_LICENSE:
required: false
AUTOMATION_DASHBOARD_URL:
required: false
AUTOMATION_DASHBOARD_TOKEN:
required: false
PUSH_NOTIFICATION_SERVER:
required: false
REPORT_WEBHOOK_URL:
required: false
CWS_URL:
required: false
CWS_EXTRA_HTTP_HEADERS:
required: false
jobs:
generate-build-variables:
runs-on: ubuntu-24.04
outputs:
branch: "${{ steps.build-vars.outputs.branch }}"
build_id: "${{ steps.build-vars.outputs.build_id }}"
server_image_tag: "${{ steps.build-vars.outputs.server_image_tag }}"
server_image: "${{ steps.build-vars.outputs.server_image }}"
context_suffix: "${{ steps.build-vars.outputs.context_suffix }}"
steps:
- name: ci/generate-build-variables
id: build-vars
env:
COMMIT_SHA: ${{ inputs.commit_sha }}
PR_NUMBER: ${{ inputs.pr_number }}
INPUT_SERVER_IMAGE_TAG: ${{ inputs.server_image_tag }}
RUN_ID: ${{ github.run_id }}
RUN_ATTEMPT: ${{ github.run_attempt }}
run: |
# Use provided server_image_tag or derive from commit SHA
if [ -n "$INPUT_SERVER_IMAGE_TAG" ]; then
SERVER_IMAGE_TAG="$INPUT_SERVER_IMAGE_TAG"
else
SERVER_IMAGE_TAG="${COMMIT_SHA::7}"
fi
# Validate server_image_tag format (alphanumeric, dots, hyphens, underscores)
if ! [[ "$SERVER_IMAGE_TAG" =~ ^[a-zA-Z0-9._-]+$ ]]; then
echo "::error::Invalid server_image_tag format: ${SERVER_IMAGE_TAG}"
exit 1
fi
echo "server_image_tag=${SERVER_IMAGE_TAG}" >> $GITHUB_OUTPUT
# Generate branch name
REF_BRANCH="${{ inputs.ref_branch }}"
if [ -n "$PR_NUMBER" ]; then
echo "branch=server-pr-${PR_NUMBER}" >> $GITHUB_OUTPUT
elif [ -n "$REF_BRANCH" ]; then
echo "branch=server-${REF_BRANCH}-${SERVER_IMAGE_TAG}" >> $GITHUB_OUTPUT
else
echo "branch=server-commit-${SERVER_IMAGE_TAG}" >> $GITHUB_OUTPUT
fi
# Determine server image name
EDITION="${{ inputs.server_edition }}"
REPO="${{ inputs.server_image_repo }}"
REPO="${REPO:-mattermostdevelopment}"
case "$EDITION" in
fips) IMAGE_NAME="mattermost-enterprise-fips-edition" ;;
team) IMAGE_NAME="mattermost-team-edition" ;;
*) IMAGE_NAME="mattermost-enterprise-edition" ;;
esac
SERVER_IMAGE="${REPO}/${IMAGE_NAME}:${SERVER_IMAGE_TAG}"
echo "server_image=${SERVER_IMAGE}" >> $GITHUB_OUTPUT
# Validate server_image_aliases format if provided
ALIASES="${{ inputs.server_image_aliases }}"
if [ -n "$ALIASES" ] && ! [[ "$ALIASES" =~ ^[a-zA-Z0-9._,\ -]+$ ]]; then
echo "::error::Invalid server_image_aliases format: ${ALIASES}"
exit 1
fi
# Generate build ID
if [ -n "$EDITION" ] && [ "$EDITION" != "enterprise" ]; then
echo "build_id=${RUN_ID}_${RUN_ATTEMPT}-${SERVER_IMAGE_TAG}-cypress-onprem-${EDITION}" >> $GITHUB_OUTPUT
else
echo "build_id=${RUN_ID}_${RUN_ATTEMPT}-${SERVER_IMAGE_TAG}-cypress-onprem-ent" >> $GITHUB_OUTPUT
fi
# Generate context name suffix based on report type
REPORT_TYPE="${{ inputs.report_type }}"
case "$REPORT_TYPE" in
MASTER) echo "context_suffix=/master" >> $GITHUB_OUTPUT ;;
RELEASE) echo "context_suffix=/release" >> $GITHUB_OUTPUT ;;
RELEASE_CUT) echo "context_suffix=/release-cut" >> $GITHUB_OUTPUT ;;
*) echo "context_suffix=" >> $GITHUB_OUTPUT ;;
esac
skip:
needs:
- generate-build-variables
if: inputs.should_run == 'false'
runs-on: ubuntu-24.04
permissions:
statuses: write
steps:
- name: ci/post-skip-status
env:
GH_TOKEN: ${{ github.token }}
COMMIT_SHA: ${{ inputs.commit_sha }}
CONTEXT_NAME: "e2e-test/cypress-full/${{ inputs.server_edition || 'enterprise' }}${{ needs.generate-build-variables.outputs.context_suffix }}"
run: |
gh api repos/${{ github.repository }}/statuses/${COMMIT_SHA} \
-f state=success \
-f context="${CONTEXT_NAME}" \
-f description="No E2E-relevant changes - skipped" \
-f target_url="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
echo "Posted success for ${CONTEXT_NAME}"
cypress-full:
needs:
- generate-build-variables
if: inputs.should_run != 'false'
uses: ./.github/workflows/e2e-tests-cypress-template.yml
with:
test_type: full
test_filter: '--stage="@prod" --excludeGroup="@te_only,@cloud_only,@high_availability" --sortFirst="@compliance_export,@elasticsearch,@ldap_group,@ldap" --sortLast="@saml,@keycloak,@plugin,@plugins_uninstall,@mfa,@license_removal"'
workers: 40
enabled_docker_services: "postgres inbucket minio openldap elasticsearch keycloak"
commit_sha: ${{ inputs.commit_sha }}
branch: ${{ needs.generate-build-variables.outputs.branch }}
build_id: ${{ needs.generate-build-variables.outputs.build_id }}
server_image_tag: ${{ needs.generate-build-variables.outputs.server_image_tag }}
server_edition: ${{ inputs.server_edition }}
server_image_repo: ${{ inputs.server_image_repo }}
server_image_aliases: ${{ inputs.server_image_aliases }}
server: ${{ inputs.server }}
enable_reporting: ${{ inputs.enable_reporting }}
report_type: ${{ inputs.report_type }}
ref_branch: ${{ inputs.ref_branch }}
pr_number: ${{ inputs.pr_number }}
context_name: "e2e-test/cypress-full/${{ inputs.server_edition || 'enterprise' }}${{ needs.generate-build-variables.outputs.context_suffix }}"
secrets:
MM_LICENSE: ${{ secrets.MM_LICENSE }}
AUTOMATION_DASHBOARD_URL: ${{ secrets.AUTOMATION_DASHBOARD_URL }}
AUTOMATION_DASHBOARD_TOKEN: ${{ secrets.AUTOMATION_DASHBOARD_TOKEN }}
PUSH_NOTIFICATION_SERVER: ${{ secrets.PUSH_NOTIFICATION_SERVER }}
REPORT_WEBHOOK_URL: ${{ secrets.REPORT_WEBHOOK_URL }}
CWS_URL: ${{ secrets.CWS_URL }}
CWS_EXTRA_HTTP_HEADERS: ${{ secrets.CWS_EXTRA_HTTP_HEADERS }}

583
.github/workflows/e2e-tests-playwright-template.yml поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,583 @@
---
name: E2E Tests - Playwright Template
on:
workflow_call:
inputs:
# Test configuration
test_type:
description: "Type of test run (smoke or full)"
type: string
required: true
test_filter:
description: "Test filter arguments (e.g., --grep @smoke)"
type: string
required: true
workers:
description: "Number of parallel shards"
type: number
required: false
default: 2
enabled_docker_services:
description: "Space-separated list of docker services to enable"
type: string
required: false
default: "postgres inbucket"
# Common build variables
commit_sha:
type: string
required: true
branch:
type: string
required: true
build_id:
type: string
required: true
server_image_tag:
description: "Server image tag (e.g., master or short SHA)"
type: string
required: true
server:
type: string
required: false
default: onprem
server_edition:
description: "Server edition: enterprise (default), fips, or team"
type: string
required: false
default: enterprise
server_image_repo:
description: "Docker registry: mattermostdevelopment (default) or mattermost"
type: string
required: false
default: mattermostdevelopment
server_image_aliases:
description: "Comma-separated alias tags for description (e.g., 'release-11.4, release-11')"
type: string
required: false
# Reporting options
enable_reporting:
type: boolean
required: false
default: false
report_type:
type: string
required: false
ref_branch:
description: "Source branch name for webhook messages (e.g., 'master' or 'release-11.4')"
type: string
required: false
pr_number:
type: string
required: false
# Commit status configuration
context_name:
description: "GitHub commit status context name"
type: string
required: true
outputs:
passed:
description: "Number of passed tests"
value: ${{ jobs.report.outputs.passed }}
failed:
description: "Number of failed tests"
value: ${{ jobs.report.outputs.failed }}
report_url:
description: "URL to test report on S3"
value: ${{ jobs.report.outputs.report_url }}
secrets:
MM_LICENSE:
required: false
REPORT_WEBHOOK_URL:
required: false
AWS_ACCESS_KEY_ID:
required: true
AWS_SECRET_ACCESS_KEY:
required: true
env:
SERVER_IMAGE: "${{ inputs.server_image_repo }}/${{ inputs.server_edition == 'fips' && 'mattermost-enterprise-fips-edition' || inputs.server_edition == 'team' && 'mattermost-team-edition' || 'mattermost-enterprise-edition' }}:${{ inputs.server_image_tag }}"
jobs:
update-initial-status:
runs-on: ubuntu-24.04
steps:
- name: ci/set-initial-status
uses: mattermost/actions/delivery/update-commit-status@f324ac89b05cc3511cb06e60642ac2fb829f0a63
env:
GITHUB_TOKEN: ${{ github.token }}
with:
repository_full_name: ${{ github.repository }}
commit_sha: ${{ inputs.commit_sha }}
context: ${{ inputs.context_name }}
description: "tests running, image_tag:${{ inputs.server_image_tag }}${{ inputs.server_image_aliases && format(' ({0})', inputs.server_image_aliases) || '' }}"
status: pending
generate-test-variables:
runs-on: ubuntu-24.04
outputs:
workers: "${{ steps.generate-workers.outputs.workers }}"
start_time: "${{ steps.generate-workers.outputs.start_time }}"
steps:
- name: ci/generate-workers
id: generate-workers
run: |
echo "workers=$(jq -nc '[range(1; ${{ inputs.workers }} + 1)]')" >> $GITHUB_OUTPUT
echo "start_time=$(date +%s)" >> $GITHUB_OUTPUT
run-tests:
runs-on: ubuntu-24.04
timeout-minutes: 30
continue-on-error: true
needs:
- generate-test-variables
if: needs.generate-test-variables.result == 'success'
strategy:
fail-fast: false
matrix:
worker_index: ${{ fromJSON(needs.generate-test-variables.outputs.workers) }}
defaults:
run:
working-directory: e2e-tests
env:
SERVER: "${{ inputs.server }}"
MM_LICENSE: "${{ secrets.MM_LICENSE }}"
ENABLED_DOCKER_SERVICES: "${{ inputs.enabled_docker_services }}"
TEST: playwright
TEST_FILTER: "${{ inputs.test_filter }}"
PW_SHARD: "${{ format('--shard={0}/{1}', matrix.worker_index, inputs.workers) }}"
BRANCH: "${{ inputs.branch }}-${{ inputs.test_type }}"
BUILD_ID: "${{ inputs.build_id }}"
CI_BASE_URL: "${{ inputs.test_type }}-test-${{ matrix.worker_index }}"
steps:
- name: ci/checkout-repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ inputs.commit_sha }}
fetch-depth: 0
- name: ci/setup-node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: ".nvmrc"
cache: npm
cache-dependency-path: "e2e-tests/playwright/package-lock.json"
- name: ci/get-webapp-node-modules
working-directory: webapp
run: make node_modules
- name: ci/run-tests
run: |
make cloud-init
make
- name: ci/cloud-teardown
if: always()
run: make cloud-teardown
- name: ci/upload-results
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
if: always()
with:
name: playwright-${{ inputs.test_type }}-${{ inputs.server_edition }}-results-${{ matrix.worker_index }}
path: |
e2e-tests/playwright/logs/
e2e-tests/playwright/results/
retention-days: 5
calculate-results:
runs-on: ubuntu-24.04
needs:
- generate-test-variables
- run-tests
if: always() && needs.generate-test-variables.result == 'success'
outputs:
passed: ${{ steps.calculate.outputs.passed }}
failed: ${{ steps.calculate.outputs.failed }}
flaky: ${{ steps.calculate.outputs.flaky }}
skipped: ${{ steps.calculate.outputs.skipped }}
total_specs: ${{ steps.calculate.outputs.total_specs }}
failed_specs: ${{ steps.calculate.outputs.failed_specs }}
failed_specs_count: ${{ steps.calculate.outputs.failed_specs_count }}
failed_tests: ${{ steps.calculate.outputs.failed_tests }}
commit_status_message: ${{ steps.calculate.outputs.commit_status_message }}
total: ${{ steps.calculate.outputs.total }}
pass_rate: ${{ steps.calculate.outputs.pass_rate }}
passing: ${{ steps.calculate.outputs.passing }}
color: ${{ steps.calculate.outputs.color }}
test_duration: ${{ steps.calculate.outputs.test_duration }}
end_time: ${{ steps.record-end-time.outputs.end_time }}
steps:
- name: ci/checkout-repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: ci/setup-node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: ".nvmrc"
cache: npm
cache-dependency-path: "e2e-tests/playwright/package-lock.json"
- name: ci/download-shard-results
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
pattern: playwright-${{ inputs.test_type }}-${{ inputs.server_edition }}-results-*
path: e2e-tests/playwright/shard-results/
merge-multiple: true
- name: ci/merge-shard-results
working-directory: e2e-tests/playwright
run: |
mkdir -p results/reporter
# Merge blob reports using Playwright merge-reports (per docs)
npm install --no-save @playwright/test
npx playwright merge-reports --config merge.config.mjs ./shard-results/results/blob-report/
- name: ci/calculate
id: calculate
uses: ./.github/actions/calculate-playwright-results
with:
original-results-path: e2e-tests/playwright/results/reporter/results.json
- name: ci/upload-merged-results
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: playwright-${{ inputs.test_type }}-${{ inputs.server_edition }}-results
path: e2e-tests/playwright/results/
retention-days: 5
- name: ci/record-end-time
id: record-end-time
run: echo "end_time=$(date +%s)" >> $GITHUB_OUTPUT
run-failed-tests:
runs-on: ubuntu-24.04
timeout-minutes: 30
needs:
- run-tests
- calculate-results
if: >-
always() &&
needs.calculate-results.result == 'success' &&
needs.calculate-results.outputs.failed != '0' &&
fromJSON(needs.calculate-results.outputs.failed_specs_count) <= 20
defaults:
run:
working-directory: e2e-tests
env:
SERVER: "${{ inputs.server }}"
MM_LICENSE: "${{ secrets.MM_LICENSE }}"
ENABLED_DOCKER_SERVICES: "${{ inputs.enabled_docker_services }}"
TEST: playwright
BRANCH: "${{ inputs.branch }}-${{ inputs.test_type }}-retest"
BUILD_ID: "${{ inputs.build_id }}-retest"
steps:
- name: ci/checkout-repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ inputs.commit_sha }}
fetch-depth: 0
- name: ci/setup-node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: ".nvmrc"
cache: npm
cache-dependency-path: "e2e-tests/playwright/package-lock.json"
- name: ci/get-webapp-node-modules
working-directory: webapp
run: make node_modules
- name: ci/run-failed-specs
env:
SPEC_FILES: ${{ needs.calculate-results.outputs.failed_specs }}
run: |
echo "Retesting failed specs: $SPEC_FILES"
make cloud-init
make start-server run-specs
- name: ci/cloud-teardown
if: always()
run: make cloud-teardown
- name: ci/upload-retest-results
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
if: always()
with:
name: playwright-${{ inputs.test_type }}-${{ inputs.server_edition }}-retest-results
path: |
e2e-tests/playwright/logs/
e2e-tests/playwright/results/
retention-days: 5
report:
runs-on: ubuntu-24.04
needs:
- generate-test-variables
- run-tests
- calculate-results
- run-failed-tests
if: always() && needs.calculate-results.result == 'success'
outputs:
passed: "${{ steps.final-results.outputs.passed }}"
failed: "${{ steps.final-results.outputs.failed }}"
commit_status_message: "${{ steps.final-results.outputs.commit_status_message }}"
report_url: "${{ steps.upload-to-s3.outputs.report_url }}"
duration: "${{ steps.duration.outputs.duration }}"
duration_display: "${{ steps.duration.outputs.duration_display }}"
retest_display: "${{ steps.duration.outputs.retest_display }}"
defaults:
run:
working-directory: e2e-tests
steps:
- name: ci/checkout-repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: ci/setup-node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: ".nvmrc"
cache: npm
cache-dependency-path: "e2e-tests/playwright/package-lock.json"
# Download merged results (uploaded by calculate-results)
- name: ci/download-results
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: playwright-${{ inputs.test_type }}-${{ inputs.server_edition }}-results
path: e2e-tests/playwright/results/
# Download retest results (only if retest ran)
- name: ci/download-retest-results
if: needs.run-failed-tests.result != 'skipped'
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: playwright-${{ inputs.test_type }}-${{ inputs.server_edition }}-retest-results
path: e2e-tests/playwright/retest-results/
# Calculate results (with optional merge of retest results)
- name: ci/calculate-results
id: final-results
uses: ./.github/actions/calculate-playwright-results
with:
original-results-path: e2e-tests/playwright/results/reporter/results.json
retest-results-path: ${{ needs.run-failed-tests.result != 'skipped' && 'e2e-tests/playwright/retest-results/results/reporter/results.json' || '' }}
- name: ci/aws-configure
uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
with:
aws-region: us-east-1
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: ci/upload-to-s3
id: upload-to-s3
env:
AWS_REGION: us-east-1
AWS_S3_BUCKET: mattermost-cypress-report
PR_NUMBER: "${{ inputs.pr_number }}"
RUN_ID: "${{ github.run_id }}"
COMMIT_SHA: "${{ inputs.commit_sha }}"
TEST_TYPE: "${{ inputs.test_type }}"
run: |
LOCAL_RESULTS_PATH="playwright/results/"
# Use PR number if available, otherwise use commit SHA prefix
if [ -n "$PR_NUMBER" ]; then
S3_PATH="server-pr-${PR_NUMBER}/e2e-reports/playwright-${TEST_TYPE}/${RUN_ID}"
else
S3_PATH="server-commit-${COMMIT_SHA::7}/e2e-reports/playwright-${TEST_TYPE}/${RUN_ID}"
fi
if [[ -d "$LOCAL_RESULTS_PATH" ]]; then
aws s3 sync "$LOCAL_RESULTS_PATH" "s3://${AWS_S3_BUCKET}/${S3_PATH}/results/" \
--acl public-read --cache-control "no-cache"
fi
REPORT_URL="https://${AWS_S3_BUCKET}.s3.amazonaws.com/${S3_PATH}/results/reporter/index.html"
echo "report_url=$REPORT_URL" >> "$GITHUB_OUTPUT"
- name: ci/compute-duration
id: duration
env:
START_TIME: ${{ needs.generate-test-variables.outputs.start_time }}
FIRST_PASS_END_TIME: ${{ needs.calculate-results.outputs.end_time }}
RETEST_RESULT: ${{ needs.run-failed-tests.result }}
RETEST_SPEC_COUNT: ${{ needs.calculate-results.outputs.failed_specs_count }}
TEST_DURATION: ${{ steps.final-results.outputs.test_duration }}
run: |
NOW=$(date +%s)
ELAPSED=$((NOW - START_TIME))
MINUTES=$((ELAPSED / 60))
SECONDS=$((ELAPSED % 60))
DURATION="${MINUTES}m ${SECONDS}s"
# Compute first-pass and re-run durations
FIRST_PASS_ELAPSED=$((FIRST_PASS_END_TIME - START_TIME))
FP_MIN=$((FIRST_PASS_ELAPSED / 60))
FP_SEC=$((FIRST_PASS_ELAPSED % 60))
FIRST_PASS="${FP_MIN}m ${FP_SEC}s"
if [ "$RETEST_RESULT" != "skipped" ]; then
RERUN_ELAPSED=$((NOW - FIRST_PASS_END_TIME))
RR_MIN=$((RERUN_ELAPSED / 60))
RR_SEC=$((RERUN_ELAPSED % 60))
RUN_BREAKDOWN=" (first-pass: ${FIRST_PASS}, re-run: ${RR_MIN}m ${RR_SEC}s)"
else
RUN_BREAKDOWN=""
fi
# Duration icons: >20m high alert, >15m warning, otherwise clock
if [ "$MINUTES" -ge 20 ]; then
DURATION_DISPLAY=":rotating_light: ${DURATION}${RUN_BREAKDOWN} | test: ${TEST_DURATION}"
elif [ "$MINUTES" -ge 15 ]; then
DURATION_DISPLAY=":warning: ${DURATION}${RUN_BREAKDOWN} | test: ${TEST_DURATION}"
else
DURATION_DISPLAY=":clock3: ${DURATION}${RUN_BREAKDOWN} | test: ${TEST_DURATION}"
fi
# Retest indicator with spec count
if [ "$RETEST_RESULT" != "skipped" ]; then
RETEST_DISPLAY=":repeat: re-run ${RETEST_SPEC_COUNT} spec(s)"
else
RETEST_DISPLAY=""
fi
echo "duration=${DURATION}" >> $GITHUB_OUTPUT
echo "duration_display=${DURATION_DISPLAY}" >> $GITHUB_OUTPUT
echo "retest_display=${RETEST_DISPLAY}" >> $GITHUB_OUTPUT
- name: ci/publish-report
if: inputs.enable_reporting && env.REPORT_WEBHOOK_URL != ''
env:
REPORT_WEBHOOK_URL: ${{ secrets.REPORT_WEBHOOK_URL }}
COMMIT_STATUS_MESSAGE: ${{ steps.final-results.outputs.commit_status_message }}
COLOR: ${{ steps.final-results.outputs.color }}
REPORT_URL: ${{ steps.upload-to-s3.outputs.report_url }}
TEST_TYPE: ${{ inputs.test_type }}
REPORT_TYPE: ${{ inputs.report_type }}
COMMIT_SHA: ${{ inputs.commit_sha }}
REF_BRANCH: ${{ inputs.ref_branch }}
PR_NUMBER: ${{ inputs.pr_number }}
DURATION_DISPLAY: ${{ steps.duration.outputs.duration_display }}
RETEST_DISPLAY: ${{ steps.duration.outputs.retest_display }}
run: |
# Capitalize test type
TEST_TYPE_CAP=$(echo "$TEST_TYPE" | sed 's/.*/\u&/')
# Build source line based on report type
COMMIT_SHORT="${COMMIT_SHA::7}"
COMMIT_URL="https://github.com/${{ github.repository }}/commit/${COMMIT_SHA}"
if [ "$REPORT_TYPE" = "RELEASE_CUT" ]; then
SOURCE_LINE=":github_round: [${COMMIT_SHORT}](${COMMIT_URL}) on \`${REF_BRANCH}\`"
elif [ "$REPORT_TYPE" = "MASTER" ] || [ "$REPORT_TYPE" = "RELEASE" ]; then
SOURCE_LINE=":git_merge: [${COMMIT_SHORT}](${COMMIT_URL}) on \`${REF_BRANCH}\`"
else
SOURCE_LINE=":open-pull-request: [mattermost-pr-${PR_NUMBER}](https://github.com/${{ github.repository }}/pull/${PR_NUMBER})"
fi
# Build retest part for message
RETEST_PART=""
if [ -n "$RETEST_DISPLAY" ]; then
RETEST_PART=" | ${RETEST_DISPLAY}"
fi
# Build payload with attachments
PAYLOAD=$(cat <<EOF
{
"username": "E2E Test",
"icon_url": "https://mattermost.com/wp-content/uploads/2022/02/icon_WS.png",
"attachments": [{
"color": "${COLOR}",
"text": "**Results - Playwright ${TEST_TYPE_CAP} Tests**\n\n${SOURCE_LINE}\n:docker: \`${{ env.SERVER_IMAGE }}\`\n${COMMIT_STATUS_MESSAGE}${RETEST_PART} | [full report](${REPORT_URL})\n${DURATION_DISPLAY}"
}]
}
EOF
)
# Send to webhook
curl -X POST -H "Content-Type: application/json" -d "$PAYLOAD" "$REPORT_WEBHOOK_URL"
- name: ci/write-job-summary
if: always()
env:
REPORT_URL: ${{ steps.upload-to-s3.outputs.report_url }}
TEST_TYPE: ${{ inputs.test_type }}
PASSED: ${{ steps.final-results.outputs.passed }}
FAILED: ${{ steps.final-results.outputs.failed }}
FLAKY: ${{ steps.final-results.outputs.flaky }}
SKIPPED: ${{ steps.final-results.outputs.skipped }}
TOTAL_SPECS: ${{ steps.final-results.outputs.total_specs }}
FAILED_SPECS_COUNT: ${{ steps.final-results.outputs.failed_specs_count }}
FAILED_SPECS: ${{ steps.final-results.outputs.failed_specs }}
COMMIT_STATUS_MESSAGE: ${{ steps.final-results.outputs.commit_status_message }}
FAILED_TESTS: ${{ steps.final-results.outputs.failed_tests }}
DURATION_DISPLAY: ${{ steps.duration.outputs.duration_display }}
RETEST_RESULT: ${{ needs.run-failed-tests.result }}
run: |
{
echo "## E2E Test Results - Playwright ${TEST_TYPE}"
echo ""
if [ "$FAILED" = "0" ]; then
echo "All tests passed: **${PASSED} passed**"
else
echo "<details>"
echo "<summary>${FAILED} failed, ${PASSED} passed</summary>"
echo ""
echo "| Test | File |"
echo "|------|------|"
echo "${FAILED_TESTS}"
echo "</details>"
fi
echo ""
echo "### Calculation Outputs"
echo ""
echo "| Output | Value |"
echo "|--------|-------|"
echo "| passed | ${PASSED} |"
echo "| failed | ${FAILED} |"
echo "| flaky | ${FLAKY} |"
echo "| skipped | ${SKIPPED} |"
echo "| total_specs | ${TOTAL_SPECS} |"
echo "| failed_specs_count | ${FAILED_SPECS_COUNT} |"
echo "| commit_status_message | ${COMMIT_STATUS_MESSAGE} |"
echo "| failed_specs | ${FAILED_SPECS:-none} |"
echo "| duration | ${DURATION_DISPLAY} |"
if [ "$RETEST_RESULT" != "skipped" ]; then
echo "| retested | Yes |"
else
echo "| retested | No |"
fi
echo ""
echo "---"
echo "[View Full Report](${REPORT_URL})"
} >> $GITHUB_STEP_SUMMARY
- name: ci/assert-results
run: |
[ "${{ steps.final-results.outputs.failed }}" = "0" ]
update-success-status:
runs-on: ubuntu-24.04
if: always() && needs.report.result == 'success' && needs.calculate-results.result == 'success'
needs:
- calculate-results
- report
steps:
- uses: mattermost/actions/delivery/update-commit-status@f324ac89b05cc3511cb06e60642ac2fb829f0a63
env:
GITHUB_TOKEN: ${{ github.token }}
with:
repository_full_name: ${{ github.repository }}
commit_sha: ${{ inputs.commit_sha }}
context: ${{ inputs.context_name }}
description: "${{ needs.report.outputs.commit_status_message }}, ${{ needs.report.outputs.duration }}, image_tag:${{ inputs.server_image_tag }}${{ inputs.server_image_aliases && format(' ({0})', inputs.server_image_aliases) || '' }}"
status: success
target_url: ${{ needs.report.outputs.report_url }}
update-failure-status:
runs-on: ubuntu-24.04
if: always() && (needs.report.result != 'success' || needs.calculate-results.result != 'success')
needs:
- calculate-results
- report
steps:
- uses: mattermost/actions/delivery/update-commit-status@f324ac89b05cc3511cb06e60642ac2fb829f0a63
env:
GITHUB_TOKEN: ${{ github.token }}
with:
repository_full_name: ${{ github.repository }}
commit_sha: ${{ inputs.commit_sha }}
context: ${{ inputs.context_name }}
description: "${{ needs.report.outputs.commit_status_message }}, ${{ needs.report.outputs.duration }}, image_tag:${{ inputs.server_image_tag }}${{ inputs.server_image_aliases && format(' ({0})', inputs.server_image_aliases) || '' }}"
status: failure
target_url: ${{ needs.report.outputs.report_url }}

185
.github/workflows/e2e-tests-playwright.yml поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,185 @@
---
name: E2E Tests - Playwright
on:
workflow_call:
inputs:
commit_sha:
type: string
required: true
enable_reporting:
type: boolean
required: false
default: false
server:
type: string
required: false
default: onprem
report_type:
type: string
required: false
pr_number:
type: string
required: false
server_image_tag:
type: string
required: false
description: "Server image tag (e.g., master or short SHA)"
server_edition:
type: string
required: false
description: "Server edition: enterprise (default), fips, or team"
server_image_repo:
type: string
required: false
default: mattermostdevelopment
description: "Docker registry: mattermostdevelopment (default) or mattermost"
server_image_aliases:
type: string
required: false
description: "Comma-separated alias tags for context name (e.g., 'release-11.4, release-11')"
ref_branch:
type: string
required: false
description: "Source branch name for webhook messages (e.g., 'master' or 'release-11.4')"
should_run:
type: string
required: false
default: "true"
description: "Set to 'false' to skip tests and post a success status without running E2E"
secrets:
MM_LICENSE:
required: false
REPORT_WEBHOOK_URL:
required: false
AWS_ACCESS_KEY_ID:
required: true
AWS_SECRET_ACCESS_KEY:
required: true
jobs:
generate-build-variables:
runs-on: ubuntu-24.04
outputs:
branch: "${{ steps.build-vars.outputs.branch }}"
build_id: "${{ steps.build-vars.outputs.build_id }}"
server_image_tag: "${{ steps.build-vars.outputs.server_image_tag }}"
server_image: "${{ steps.build-vars.outputs.server_image }}"
context_suffix: "${{ steps.build-vars.outputs.context_suffix }}"
steps:
- name: ci/generate-build-variables
id: build-vars
env:
COMMIT_SHA: ${{ inputs.commit_sha }}
PR_NUMBER: ${{ inputs.pr_number }}
INPUT_SERVER_IMAGE_TAG: ${{ inputs.server_image_tag }}
RUN_ID: ${{ github.run_id }}
RUN_ATTEMPT: ${{ github.run_attempt }}
run: |
# Use provided server_image_tag or derive from commit SHA
if [ -n "$INPUT_SERVER_IMAGE_TAG" ]; then
SERVER_IMAGE_TAG="$INPUT_SERVER_IMAGE_TAG"
else
SERVER_IMAGE_TAG="${COMMIT_SHA::7}"
fi
# Validate server_image_tag format (alphanumeric, dots, hyphens, underscores)
if ! [[ "$SERVER_IMAGE_TAG" =~ ^[a-zA-Z0-9._-]+$ ]]; then
echo "::error::Invalid server_image_tag format: ${SERVER_IMAGE_TAG}"
exit 1
fi
echo "server_image_tag=${SERVER_IMAGE_TAG}" >> $GITHUB_OUTPUT
# Generate branch name
REF_BRANCH="${{ inputs.ref_branch }}"
if [ -n "$PR_NUMBER" ]; then
echo "branch=server-pr-${PR_NUMBER}" >> $GITHUB_OUTPUT
elif [ -n "$REF_BRANCH" ]; then
echo "branch=server-${REF_BRANCH}-${SERVER_IMAGE_TAG}" >> $GITHUB_OUTPUT
else
echo "branch=server-commit-${SERVER_IMAGE_TAG}" >> $GITHUB_OUTPUT
fi
# Determine server image name
EDITION="${{ inputs.server_edition }}"
REPO="${{ inputs.server_image_repo }}"
REPO="${REPO:-mattermostdevelopment}"
case "$EDITION" in
fips) IMAGE_NAME="mattermost-enterprise-fips-edition" ;;
team) IMAGE_NAME="mattermost-team-edition" ;;
*) IMAGE_NAME="mattermost-enterprise-edition" ;;
esac
SERVER_IMAGE="${REPO}/${IMAGE_NAME}:${SERVER_IMAGE_TAG}"
echo "server_image=${SERVER_IMAGE}" >> $GITHUB_OUTPUT
# Validate server_image_aliases format if provided
ALIASES="${{ inputs.server_image_aliases }}"
if [ -n "$ALIASES" ] && ! [[ "$ALIASES" =~ ^[a-zA-Z0-9._,\ -]+$ ]]; then
echo "::error::Invalid server_image_aliases format: ${ALIASES}"
exit 1
fi
# Generate build ID
if [ -n "$EDITION" ] && [ "$EDITION" != "enterprise" ]; then
echo "build_id=${RUN_ID}_${RUN_ATTEMPT}-${SERVER_IMAGE_TAG}-playwright-onprem-${EDITION}" >> $GITHUB_OUTPUT
else
echo "build_id=${RUN_ID}_${RUN_ATTEMPT}-${SERVER_IMAGE_TAG}-playwright-onprem-ent" >> $GITHUB_OUTPUT
fi
# Generate context name suffix based on report type
REPORT_TYPE="${{ inputs.report_type }}"
case "$REPORT_TYPE" in
MASTER) echo "context_suffix=/master" >> $GITHUB_OUTPUT ;;
RELEASE) echo "context_suffix=/release" >> $GITHUB_OUTPUT ;;
RELEASE_CUT) echo "context_suffix=/release-cut" >> $GITHUB_OUTPUT ;;
*) echo "context_suffix=" >> $GITHUB_OUTPUT ;;
esac
skip:
needs:
- generate-build-variables
if: inputs.should_run == 'false'
runs-on: ubuntu-24.04
permissions:
statuses: write
steps:
- name: ci/post-skip-status
env:
GH_TOKEN: ${{ github.token }}
COMMIT_SHA: ${{ inputs.commit_sha }}
CONTEXT_NAME: "e2e-test/playwright-full/${{ inputs.server_edition || 'enterprise' }}${{ needs.generate-build-variables.outputs.context_suffix }}"
run: |
gh api repos/${{ github.repository }}/statuses/${COMMIT_SHA} \
-f state=success \
-f context="${CONTEXT_NAME}" \
-f description="No E2E-relevant changes - skipped" \
-f target_url="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
echo "Posted success for ${CONTEXT_NAME}"
playwright-full:
needs:
- generate-build-variables
if: inputs.should_run != 'false'
uses: ./.github/workflows/e2e-tests-playwright-template.yml
with:
test_type: full
test_filter: '--grep-invert "@visual"'
workers: 4
enabled_docker_services: "postgres inbucket minio openldap elasticsearch keycloak"
commit_sha: ${{ inputs.commit_sha }}
branch: ${{ needs.generate-build-variables.outputs.branch }}
build_id: ${{ needs.generate-build-variables.outputs.build_id }}
server_image_tag: ${{ needs.generate-build-variables.outputs.server_image_tag }}
server_edition: ${{ inputs.server_edition }}
server_image_repo: ${{ inputs.server_image_repo }}
server_image_aliases: ${{ inputs.server_image_aliases }}
server: ${{ inputs.server }}
enable_reporting: ${{ inputs.enable_reporting }}
report_type: ${{ inputs.report_type }}
ref_branch: ${{ inputs.ref_branch }}
pr_number: ${{ inputs.pr_number }}
context_name: "e2e-test/playwright-full/${{ inputs.server_edition || 'enterprise' }}${{ needs.generate-build-variables.outputs.context_suffix }}"
secrets:
MM_LICENSE: ${{ secrets.MM_LICENSE }}
REPORT_WEBHOOK_URL: ${{ secrets.REPORT_WEBHOOK_URL }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}