snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
This commit is contained in:
128
e2e-tests/.ci/.e2erc
Normal file
128
e2e-tests/.ci/.e2erc
Normal file
@@ -0,0 +1,128 @@
|
||||
# shellcheck disable=SC2148,SC2155
|
||||
|
||||
# Function definitions
|
||||
mme2e_log () { echo "[$(date +%Y-%m-%dT%H:%M:%S%Z)]" "$@"; }
|
||||
mme2e_get_current_shopt_arg () {
|
||||
# This function lets you get the current value of shell options, e.g. pipefail or allexport, in the form
|
||||
# of arguments to pass to the 'set' shell function, in order to restore its value later
|
||||
SHOPT_ARG=${1:?}
|
||||
case $(set -o | sed -n -E "s/^${SHOPT_ARG}[[:space:]]+(o..?)$/\1/p") in
|
||||
on) echo -n "-o ${SHOPT_ARG}";;
|
||||
off) echo -n "+o ${SHOPT_ARG}";;
|
||||
*) return 1;;
|
||||
esac
|
||||
}
|
||||
mme2e_load_env_file () {
|
||||
# This loads the ./env file. Variables are automatically exported
|
||||
[ -f ./env ] || return 0
|
||||
MME2E_PREVIOUS_ALLEXPORT=$(mme2e_get_current_shopt_arg allexport)
|
||||
set -o allexport
|
||||
mme2e_log "Loading env file"
|
||||
. ./env
|
||||
# shellcheck disable=SC2086
|
||||
set ${MME2E_PREVIOUS_ALLEXPORT}
|
||||
}
|
||||
mme2e_generate_envfile_from_var_names () {
|
||||
# Read var names from stdin, one per line
|
||||
while read -r VARIABLE; do
|
||||
[ -z "${!VARIABLE:-}" ] || echo "${VARIABLE}=${!VARIABLE}";
|
||||
done
|
||||
}
|
||||
mme2e_wait_command_success () {
|
||||
COMMAND=${1?}
|
||||
RETRY_MESSAGE=${2?}
|
||||
RETRIES_LEFT=${3:-1}
|
||||
RETRIES_INTERVAL=${4:-10}
|
||||
MME2E_PREVIOUS_PIPEFAIL=$(mme2e_get_current_shopt_arg pipefail)
|
||||
set -o pipefail
|
||||
until bash -c "${COMMAND}"; do
|
||||
RETRIES_LEFT=$((RETRIES_LEFT - 1))
|
||||
[ "$RETRIES_LEFT" -le "0" ] && break
|
||||
mme2e_log "${RETRY_MESSAGE} ($RETRIES_LEFT retries left, sleeping $RETRIES_INTERVAL seconds)"
|
||||
sleep "$RETRIES_INTERVAL"
|
||||
done
|
||||
# shellcheck disable=SC2086
|
||||
set ${MME2E_PREVIOUS_PIPEFAIL}
|
||||
if [ "$RETRIES_LEFT" = "0" ]; then
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
mme2e_wait_service_healthy () {
|
||||
DC_COMMAND="${DC_COMMAND:-$MME2E_DC_SERVER}"
|
||||
SERVICE_NAME=${1?}
|
||||
RETRIES_LEFT=${2:-1}
|
||||
RETRIES_INTERVAL=${3:-10}
|
||||
mme2e_wait_command_success "${DC_COMMAND} ps ${SERVICE_NAME} | grep -qE '\(healthy\)'" "Waiting for ${SERVICE_NAME} container to be healthy" "$RETRIES_LEFT" "$RETRIES_INTERVAL"
|
||||
}
|
||||
mme2e_wait_image () {
|
||||
IMAGE_NAME=${1?}
|
||||
RETRIES_LEFT=${2:-1}
|
||||
RETRIES_INTERVAL=${3:-10}
|
||||
mme2e_wait_command_success "docker pull --platform linux/amd64 $IMAGE_NAME" "Waiting for docker image ${IMAGE_NAME} to be available" "$RETRIES_LEFT" "$RETRIES_INTERVAL"
|
||||
}
|
||||
mme2e_is_token_in_list() {
|
||||
local TOKEN=$1
|
||||
local LIST=$2
|
||||
grep -qE "(^| )$TOKEN( |$)" <<<"$LIST"
|
||||
}
|
||||
|
||||
# Call prerequisite utility functions
|
||||
mme2e_load_env_file
|
||||
|
||||
# Utility variables
|
||||
# NB: these assume you `source` them from the directory this file is in
|
||||
# Set up base docker compose file and export
|
||||
export MME2E_DC_SERVER="docker compose -p mmserver -f ./server.yml"
|
||||
export MME2E_DC_DASHBOARD="docker compose -p mmdashboard -f ./dashboard/docker/docker-compose.yml -f ./dashboard.override.yml"
|
||||
export MME2E_UID=$(id -u)
|
||||
export MME2E_OSTYPE=$(docker version -f '{{ .Client.Os }}')
|
||||
export MME2E_ARCHTYPE=$(docker version -f '{{ .Client.Arch }}')
|
||||
export NODE_VERSION_REQUIRED=$(cat ../../.nvmrc)
|
||||
|
||||
# Utility alias, for interactive shell usage. Can be reversed with 'unalias docker-compose-mmserver' in your shell
|
||||
# NB: this only works in interactive shells
|
||||
alias docker-compose-mmserver='${MME2E_DC_SERVER}'
|
||||
alias docker-compose-mmdashboard='${MME2E_DC_DASHBOARD}'
|
||||
|
||||
# Default values for optional variables
|
||||
export SERVER_IMAGE_DEFAULT="mattermostdevelopment/mattermost-enterprise-edition:$(git rev-parse --short=7 HEAD)"
|
||||
export BROWSER_DEFAULT="chrome"
|
||||
export SERVER_DEFAULT="onprem"
|
||||
export TEST_DEFAULT="cypress"
|
||||
export ENABLED_DOCKER_SERVICES_DEFAULT="postgres inbucket"
|
||||
export BRANCH_DEFAULT=$(git branch --show-current || echo -n "unknown")
|
||||
export BUILD_ID_DEFAULT=$(date +%s)
|
||||
export CI_BASE_URL_DEFAULT="localhost"
|
||||
# Test tool default overrides
|
||||
case "${TEST:-$TEST_DEFAULT}" in
|
||||
cypress )
|
||||
export TEST_FILTER_DEFAULT='--stage=@prod --group=@smoke' ;;
|
||||
playwright )
|
||||
export TEST_FILTER_DEFAULT='--grep @smoke' ;;
|
||||
* )
|
||||
export TEST_FILTER_DEFAULT='' ;;
|
||||
esac
|
||||
# OS specific defaults overrides
|
||||
case $MME2E_OSTYPE in
|
||||
linux )
|
||||
export IS_LINUX="-linux"
|
||||
;;
|
||||
darwin )
|
||||
export BROWSER_DEFAULT="chrome"
|
||||
export IS_LINUX=""
|
||||
;;
|
||||
* )
|
||||
esac
|
||||
|
||||
# Populate the optional variables that are used in the docker-compose file
|
||||
export SERVER_IMAGE=${SERVER_IMAGE:-$SERVER_IMAGE_DEFAULT}
|
||||
export BROWSER=${BROWSER:-$BROWSER_DEFAULT}
|
||||
export SERVER=${SERVER:-$SERVER_DEFAULT}
|
||||
export TEST=${TEST:-$TEST_DEFAULT}
|
||||
export ENABLED_DOCKER_SERVICES=${ENABLED_DOCKER_SERVICES:-$ENABLED_DOCKER_SERVICES_DEFAULT}
|
||||
export TEST_FILTER=${TEST_FILTER:-$TEST_FILTER_DEFAULT}
|
||||
export BRANCH_UNSANITIZED=${BRANCH:-$BRANCH_DEFAULT}
|
||||
export BRANCH=${BRANCH_UNSANITIZED::50} # The automation dashboard only accepts branch names up to 50 characters
|
||||
export BUILD_ID_UNSANITIZED=${BUILD_ID:-$BUILD_ID_DEFAULT}
|
||||
export BUILD_ID=${BUILD_ID_UNSANITIZED::64} # The automation dashboard only accepts build IDs up to 64 characters
|
||||
export CI_BASE_URL=${CI_BASE_URL:-$CI_BASE_URL_DEFAULT}
|
||||
4
e2e-tests/.ci/.gitignore
vendored
Normal file
4
e2e-tests/.ci/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
dashboard
|
||||
server.yml
|
||||
env
|
||||
.env.*
|
||||
4
e2e-tests/.ci/.shellcheckrc
Normal file
4
e2e-tests/.ci/.shellcheckrc
Normal file
@@ -0,0 +1,4 @@
|
||||
# SC1091: Not following: <file>
|
||||
# https://www.shellcheck.net/wiki/SC1091
|
||||
disable=SC1091
|
||||
# Used to ignore for sourcing ".e2erc*"
|
||||
21
e2e-tests/.ci/dashboard.entrypoint.sh
Executable file
21
e2e-tests/.ci/dashboard.entrypoint.sh
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
set -e -u -o pipefail
|
||||
|
||||
npm install
|
||||
|
||||
# Run migrations. This is also a way to wait for the database to be up
|
||||
MIGRATION_ATTEMPTS_LEFT=10
|
||||
MIGRATION_ATTEMPTS_INTERVAL=10
|
||||
until npm run migrate:latest; do
|
||||
MIGRATION_ATTEMPTS_LEFT=$((MIGRATION_ATTEMPTS_LEFT - 1))
|
||||
[ "$MIGRATION_ATTEMPTS_LEFT" -gt 0 ] || break
|
||||
echo "Migration script failed, sleeping $MIGRATION_ATTEMPTS_INTERVAL"
|
||||
sleep $MIGRATION_ATTEMPTS_INTERVAL
|
||||
done
|
||||
[ "$MIGRATION_ATTEMPTS_LEFT" -gt 0 ] || {
|
||||
echo "Migration script failed, exhausted attempts. Giving up."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Launch the dashboard
|
||||
exec npm run dev
|
||||
26
e2e-tests/.ci/dashboard.generate_test_cycle.sh
Normal file
26
e2e-tests/.ci/dashboard.generate_test_cycle.sh
Normal file
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
set -e -u -o pipefail
|
||||
cd "$(dirname "$0")"
|
||||
. .e2erc
|
||||
|
||||
TEST_ENV_FILE=".env.$TEST"
|
||||
|
||||
mme2e_log "Loading variables from $TEST_ENV_FILE"
|
||||
if ! [ -f "$TEST_ENV_FILE" ]; then
|
||||
mme2e_log "Error: $TEST_ENV_FILE is required to exist, for the test cycle to be generated. Aborting." >&2
|
||||
exit 1
|
||||
fi
|
||||
set -a
|
||||
# shellcheck disable=SC1090
|
||||
. "$TEST_ENV_FILE"
|
||||
|
||||
if [ -z "${AUTOMATION_DASHBOARD_URL:-}" ]; then
|
||||
mme2e_log "AUTOMATION_DASHBOARD_URL is unset. Skipping test cycle generation."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
mme2e_log "Generating the test cycle on the Automation Dashboard"
|
||||
cd "../$TEST"
|
||||
npm i
|
||||
# shellcheck disable=SC2086
|
||||
exec node --trace-warnings generate_test_cycle.js $TEST_FILTER
|
||||
25
e2e-tests/.ci/dashboard.override.yml
Normal file
25
e2e-tests/.ci/dashboard.override.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
services:
|
||||
dashboard:
|
||||
image: mattermostdevelopment/mirrored-node:18.17
|
||||
environment:
|
||||
PG_URI: postgres://mmuser:mostest_password@db:5432/automation_dashboard_db
|
||||
JWT_SECRET: s8gGBA3ujKRohSw1L8HLOY7Jjnu2ZYv8 # Generated with e.g. `dd if=/dev/urandom count=24 bs=1 2>/dev/null | base64 -w0`
|
||||
JWT_USER: cypress-test
|
||||
JWT_ROLE: integration
|
||||
JWT_ALG: HS256
|
||||
JWT_EXPIRES_IN: 365d
|
||||
ALLOWED_USER: cypress-test
|
||||
ALLOWED_ROLE: integration
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-s", "-o/dev/null", "127.0.0.1:4000"]
|
||||
interval: 10s
|
||||
timeout: 15s
|
||||
retries: 12
|
||||
volumes:
|
||||
- "../:/app"
|
||||
- "../../dashboard.entrypoint.sh:/usr/local/bin/dashboard.entrypoint.sh:ro"
|
||||
working_dir: /app
|
||||
entrypoint: /usr/local/bin/dashboard.entrypoint.sh
|
||||
ports:
|
||||
- 4000:4000
|
||||
50
e2e-tests/.ci/dashboard.start.sh
Executable file
50
e2e-tests/.ci/dashboard.start.sh
Executable file
@@ -0,0 +1,50 @@
|
||||
#!/bin/bash
|
||||
set -e -u -o pipefail
|
||||
cd "$(dirname "$0")"
|
||||
. .e2erc
|
||||
|
||||
MME2E_DASHBOARD_REF_DEFAULT="origin/main"
|
||||
MME2E_DASHBOARD_REF=${MME2E_DASHBOARD_REF:-$MME2E_DASHBOARD_REF_DEFAULT}
|
||||
|
||||
mme2e_log "Cloning the automation-dashboard project"
|
||||
if [ ! -d dashboard ]; then
|
||||
git clone https://github.com/saturninoabril/automation-dashboard.git dashboard
|
||||
# Must reinitialize some variables that depend on the dashboard repo being checked out
|
||||
. .e2erc
|
||||
fi
|
||||
git -C dashboard fetch
|
||||
git -C dashboard checkout "$MME2E_DASHBOARD_REF"
|
||||
|
||||
mme2e_log "Starting the dashboard"
|
||||
${MME2E_DC_DASHBOARD} up -d db dashboard
|
||||
|
||||
if ! DC_COMMAND="${MME2E_DC_DASHBOARD}" mme2e_wait_service_healthy dashboard 60 10; then
|
||||
mme2e_log "Dashboard container not healthy, retry attempts exhausted. Giving up." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mme2e_log "Generating the dashboard's local URL"
|
||||
case $MME2E_OSTYPE in
|
||||
darwin)
|
||||
# shellcheck disable=SC2046
|
||||
AUTOMATION_DASHBOARD_IP=$(ifconfig $(route get 1.1.1.1 | grep interface | awk '{print $2}') | grep -w inet | awk '{print $2}')
|
||||
;;
|
||||
*)
|
||||
# shellcheck disable=SC2046
|
||||
MME2E_DC_DASHBOARD_NETWORK=$(docker inspect $(${MME2E_DC_DASHBOARD} ps -q dashboard) | jq -r '.[0].NetworkSettings.Networks | (keys|.[0])')
|
||||
AUTOMATION_DASHBOARD_IP=$(docker network inspect "$MME2E_DC_DASHBOARD_NETWORK" | jq -r '.[0].IPAM.Config[0].Gateway')
|
||||
;;
|
||||
esac
|
||||
# shellcheck disable=SC2034
|
||||
AUTOMATION_DASHBOARD_URL="http://${AUTOMATION_DASHBOARD_IP}:4000/api"
|
||||
|
||||
mme2e_log "Generating a signed JWT token for accessing the dashboard"
|
||||
# Not running 'npm install'. We assume it has been run by the dashboard container's entrypoint
|
||||
# shellcheck disable=SC2034
|
||||
AUTOMATION_DASHBOARD_TOKEN=$(${MME2E_DC_DASHBOARD} exec -T dashboard node script/sign.js | awk '{ print $2; }') # The token secret is specified in the dashboard.override.yml file
|
||||
|
||||
mme2e_log "Generating the .env.dashboard file, to point Cypress to the dashboard URL"
|
||||
mme2e_generate_envfile_from_var_names >.env.dashboard <<EOF
|
||||
AUTOMATION_DASHBOARD_URL
|
||||
AUTOMATION_DASHBOARD_TOKEN
|
||||
EOF
|
||||
12
e2e-tests/.ci/dashboard.stop.sh
Executable file
12
e2e-tests/.ci/dashboard.stop.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
set -e -u -o pipefail
|
||||
cd "$(dirname "$0")"
|
||||
. .e2erc
|
||||
|
||||
if [ -d dashboard ]; then
|
||||
mme2e_log "Stopping the dashboard containers"
|
||||
${MME2E_DC_DASHBOARD} down
|
||||
else
|
||||
# Base docker-compose file is found in the dashboard repo, we can't run docker commands without it
|
||||
mme2e_log "Not stopping the dashboard containers: dashboard repo not checked out locally, skipping"
|
||||
fi
|
||||
27
e2e-tests/.ci/report.collect.sh
Executable file
27
e2e-tests/.ci/report.collect.sh
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
# SC2034: <variable> appears unused.
|
||||
# https://www.shellcheck.net/wiki/SC2034
|
||||
# shellcheck disable=SC2034
|
||||
# shellcheck disable=SC2086,SC2223
|
||||
|
||||
set -e -u -o pipefail
|
||||
cd "$(dirname "$0")"
|
||||
. .e2erc
|
||||
|
||||
# The collected data will be written to the "e2e-tests/$TEST/results/" directory
|
||||
cd "../$TEST/"
|
||||
if [ ! -d "results/" ]; then
|
||||
mme2e_log "Error: 'results/' directory does not exist. Aborting report data collection." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# If the Automation Dashboard is used, try to collect run data from it
|
||||
if [ -n "${AUTOMATION_DASHBOARD_URL:-}" ]; then
|
||||
mme2e_log "Automation Dashboard usage detected: retrieving cycle results"
|
||||
# Assume that we're in the cypress dir and that the 'results/' directory exists
|
||||
: ${BUILD_ID:?}
|
||||
AD_CYCLE_FILE="results/ad_cycle.json"
|
||||
AD_SPECS_FILE="results/ad_specs.json"
|
||||
curl -o "$AD_CYCLE_FILE" -fsSL "${AUTOMATION_DASHBOARD_URL}/cycle?build=${BUILD_ID}"
|
||||
curl -o "$AD_SPECS_FILE" -fsSL "${AUTOMATION_DASHBOARD_URL}/executions/specs?cycle_id=$(jq -r .id "$AD_CYCLE_FILE")"
|
||||
fi
|
||||
115
e2e-tests/.ci/report.publish.sh
Executable file
115
e2e-tests/.ci/report.publish.sh
Executable file
@@ -0,0 +1,115 @@
|
||||
#!/bin/bash
|
||||
# SC2034: <variable> appears unused.
|
||||
# https://www.shellcheck.net/wiki/SC2034
|
||||
# shellcheck disable=SC2034
|
||||
# shellcheck disable=SC2086,SC2223
|
||||
|
||||
set -e -u -o pipefail
|
||||
cd "$(dirname "$0")"
|
||||
. .e2erc
|
||||
|
||||
# Default or assert variables required by the save_report.js script, and document optional variables
|
||||
: ${FULL_REPORT:=false} # Valid values: true, false
|
||||
: ${TYPE:=NONE} # Valid values: PR, RELEASE, MASTER, MASTER_UNSTABLE, CLOUD, CLOUD_UNSTABLE, NONE (which is the same as omitting it); also known as REPORT_TYPE
|
||||
: ${WEBHOOK_URL:-} # Optional. Mattermost webhook to post the report back to
|
||||
: ${RELEASE_DATE:-} # Optional. If set, its value will be included in the report as the release date of the tested artifact
|
||||
if [ "$TYPE" = "PR" ]; then
|
||||
# Try to determine PR number: first from PR_NUMBER, then from BRANCH (server-pr-XXXX format)
|
||||
if [ -n "${PR_NUMBER:-}" ]; then
|
||||
export PULL_REQUEST="https://github.com/mattermost/mattermost/pull/${PR_NUMBER}"
|
||||
elif grep -qE '^server-pr-[0-9]+$' <<<"${BRANCH:-}"; then
|
||||
PR_NUMBER="${BRANCH##*-}"
|
||||
export PULL_REQUEST="https://github.com/mattermost/mattermost/pull/${PR_NUMBER}"
|
||||
else
|
||||
mme2e_log "Warning: TYPE=PR but cannot determine PR number from PR_NUMBER or BRANCH. Falling back to TYPE=NONE."
|
||||
TYPE=NONE
|
||||
fi
|
||||
fi
|
||||
|
||||
# Env vars used during the test. Their values will be included in the report
|
||||
: ${TEST:?} # See E2E tests' readme
|
||||
: ${BRANCH:?} # May be either a ref, a commit hash, or 'server-pr-PR_NUMBER' (if TYPE=PR)
|
||||
: ${BUILD_ID:?}
|
||||
: ${SERVER:?} # May be either 'onprem' or 'cloud'
|
||||
: ${MM_ENV:-}
|
||||
|
||||
# Populate intermediate variables
|
||||
export BUILD_TAG="${SERVER_IMAGE##*/}"
|
||||
export MM_DOCKER_IMAGE="${BUILD_TAG%%:*}" # NB: the 'mattermostdevelopment/' prefix is assumed
|
||||
export MM_DOCKER_TAG="${BUILD_TAG##*:}"
|
||||
export SERVER_TYPE="${SERVER}"
|
||||
export AUTOMATION_DASHBOARD_FRONTEND_URL="${AUTOMATION_DASHBOARD_URL:+${AUTOMATION_DASHBOARD_URL%%/api}}"
|
||||
|
||||
if [ -n "${TM4J_API_KEY:-}" ]; then
|
||||
# Set additional variables required for Zephyr reporting
|
||||
if grep -q 'UNSTABLE' <<<"$TYPE"; then
|
||||
export TEST_SUITE=prod
|
||||
else
|
||||
export TEST_SUITE=unstable
|
||||
fi
|
||||
if [ "$TYPE" = "RELEASE" ]; then
|
||||
export SERVER_ARTIFACT_TYPE=release
|
||||
else
|
||||
export SERVER_ARTIFACT_TYPE=master
|
||||
export TEST_IS_DAILY=yes
|
||||
fi
|
||||
export TM4J_ENABLE=true
|
||||
export JIRA_PROJECT_KEY=MM
|
||||
export TM4J_ENVIRONMENT_NAME="${TEST@u}/${BROWSER@u}/${SERVER_ARTIFACT_TYPE}-${SERVER_TYPE}-ent${TEST_IS_DAILY:+-${TEST_SUITE}}"
|
||||
# Assert that the test type is among the ones supported by Zephyr, and select the corresponding folderId
|
||||
case "${SERVER}-${TYPE}" in
|
||||
onprem-RELEASE)
|
||||
export TM4J_FOLDER_ID="2014475"
|
||||
;;
|
||||
onprem-MASTER)
|
||||
export TM4J_FOLDER_ID="2014476"
|
||||
;;
|
||||
onprem-MASTER_UNSTABLE)
|
||||
export TM4J_FOLDER_ID="2014478"
|
||||
;;
|
||||
cloud-RELEASE)
|
||||
export TM4J_FOLDER_ID="2014474"
|
||||
;;
|
||||
cloud-CLOUD)
|
||||
export TM4J_FOLDER_ID="2014479"
|
||||
;;
|
||||
cloud-CLOUD_UNSTABLE)
|
||||
export TM4J_FOLDER_ID="2014481"
|
||||
;;
|
||||
*)
|
||||
mme2e_log "Error: unsupported Zephyr environment for the requested report (SERVER=${SERVER}, TYPE=${TYPE}). Aborting." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
: ${TEST_CYCLE_LINK_PREFIX:?}
|
||||
: ${TM4J_CYCLE_KEY:-} # Optional. Populated automatically by the reporting script
|
||||
: ${TM4J_CYCLE_NAME:-} # Optional. Populated automatically by the reporting script
|
||||
mme2e_log "TMJ4 integration enabled. Environment: $TM4J_ENVIRONMENT_NAME"
|
||||
fi
|
||||
|
||||
if [ -n "${DIAGNOSTIC_WEBHOOK_URL:-}" ]; then
|
||||
: ${DIAGNOSTIC_USER_ID:?}
|
||||
: ${DIAGNOSTIC_TEAM_ID:?}
|
||||
mme2e_log "Diagnostic report upload enabled."
|
||||
fi
|
||||
|
||||
# Double check that the "results/" subdirectory to collect report informations from exists
|
||||
cd "../${TEST}/"
|
||||
if [ ! -d "results/" ]; then
|
||||
mme2e_log "Error: 'results/' directory does not exist. Aborting report data collection." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$TEST" in
|
||||
cypress)
|
||||
npm i
|
||||
node save_report.js
|
||||
;;
|
||||
playwright)
|
||||
if [ -n "$WEBHOOK_URL" ]; then
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm i
|
||||
# Utilize environment data and report files to generate the webhook body
|
||||
node report.webhookgen.js | curl -X POST -fsSL -H 'Content-Type: application/json' -d @- "$WEBHOOK_URL"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
41
e2e-tests/.ci/server.cloud_init.sh
Executable file
41
e2e-tests/.ci/server.cloud_init.sh
Executable file
@@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
set -e -u -o pipefail
|
||||
cd "$(dirname "$0")"
|
||||
. .e2erc
|
||||
[ -f .env.cloud ] && . .env.cloud
|
||||
|
||||
if [ "$SERVER" != "cloud" ]; then
|
||||
mme2e_log "Skipping cloud instance initialization: operation supported only for cloud server, but running with SERVER='$SERVER'"
|
||||
exit 0
|
||||
elif [ -n "${MM_CUSTOMER_ID:-}" ]; then
|
||||
mme2e_log "Skipping cloud user creation: customer with ID '$MM_CUSTOMER_ID' is already configured. Please run 'make cloud-teardown' before creating a new user."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
mme2e_log "Initializing cloud tests"
|
||||
|
||||
# Assert that required variables are set
|
||||
MME2E_ENVCHECK_MSG="variable required for initializing cloud tests, but is empty or unset."
|
||||
: "${CWS_URL:?$MME2E_ENVCHECK_MSG}"
|
||||
: "${MM_LICENSE:?$MME2E_ENVCHECK_MSG}"
|
||||
|
||||
response=$(curl -fsSL -X POST -H @- "${CWS_URL}/api/v1/tests/create-customer?sku=cloud-enterprise&is_paid=true" <<<"${CWS_EXTRA_HTTP_HEADERS:-}")
|
||||
MM_CUSTOMER_ID=$(echo "$response" | jq -r .customer_id)
|
||||
MM_CLOUD_API_KEY=$(echo "$response" | jq -r .api_key)
|
||||
MM_CLOUD_INSTALLATION_ID=$(echo "$response" | jq -r .installation_id)
|
||||
|
||||
export MM_CUSTOMER_ID=$MM_CUSTOMER_ID
|
||||
export MM_CLOUD_API_KEY=$MM_CLOUD_API_KEY
|
||||
export MM_CLOUD_INSTALLATION_ID=$MM_CLOUD_INSTALLATION_ID
|
||||
export MM_CLOUDSETTINGS_CWSURL=$CWS_URL
|
||||
export MM_CLOUDSETTINGS_CWSAPIURL=$CWS_URL
|
||||
|
||||
mme2e_generate_envfile_from_var_names >.env.cloud <<EOF
|
||||
MM_CLOUDSETTINGS_CWSURL
|
||||
MM_CLOUDSETTINGS_CWSAPIURL
|
||||
MM_CLOUD_API_KEY
|
||||
MM_CUSTOMER_ID
|
||||
MM_CLOUD_INSTALLATION_ID
|
||||
EOF
|
||||
|
||||
mme2e_log "Test cloud customer created, MM_CUSTOMER_ID: $MM_CUSTOMER_ID."
|
||||
26
e2e-tests/.ci/server.cloud_teardown.sh
Executable file
26
e2e-tests/.ci/server.cloud_teardown.sh
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
set -e -u -o pipefail
|
||||
cd "$(dirname "$0")"
|
||||
. .e2erc
|
||||
[ -f .env.cloud ] && . .env.cloud
|
||||
|
||||
if [ -z "${MM_CUSTOMER_ID:-}" ]; then
|
||||
mme2e_log "Skipping cloud instance teardown: MM_CUSTOMER_ID variable is empty, no cloud user to cleanup."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
mme2e_log "Tearing down cloud tests"
|
||||
|
||||
mme2e_log "Loading .env.cloud"
|
||||
. .env.cloud
|
||||
|
||||
# Assert that required variables are set
|
||||
MME2E_ENVCHECK_MSG="variable required for tearing down cloud tests, but is empty or unset."
|
||||
: "${CWS_URL:?$MME2E_ENVCHECK_MSG}"
|
||||
: "${MM_CUSTOMER_ID:?$MME2E_ENVCHECK_MSG}"
|
||||
|
||||
mme2e_log "Deleting customer $MM_CUSTOMER_ID."
|
||||
curl -fsSL -X DELETE -H @- "${CWS_URL}/api/v1/tests/customers/$MM_CUSTOMER_ID/payment-customer" <<<"${CWS_EXTRA_HTTP_HEADERS:-}"
|
||||
rm -fv .env.cloud
|
||||
|
||||
mme2e_log "Test cloud customer deleted, MM_CUSTOMER_ID: $MM_CUSTOMER_ID."
|
||||
477
e2e-tests/.ci/server.generate.sh
Executable file
477
e2e-tests/.ci/server.generate.sh
Executable file
@@ -0,0 +1,477 @@
|
||||
#!/bin/bash
|
||||
# SC2034: <variable> appears unused.
|
||||
# https://www.shellcheck.net/wiki/SC2034
|
||||
# shellcheck disable=SC2034
|
||||
# Note: Variables are dynamically used depending on usage input (ENABLED_DOCKER_SERVICES)
|
||||
|
||||
set -e -u -o pipefail
|
||||
cd "$(dirname "$0")"
|
||||
. .e2erc
|
||||
|
||||
enable_docker_service() {
|
||||
local SERVICE_TO_ENABLE="$1"
|
||||
if ! mme2e_is_token_in_list "$SERVICE_TO_ENABLE" "$ENABLED_DOCKER_SERVICES"; then
|
||||
ENABLED_DOCKER_SERVICES="$ENABLED_DOCKER_SERVICES $SERVICE_TO_ENABLE"
|
||||
fi
|
||||
}
|
||||
|
||||
assert_docker_services_validity() {
|
||||
local SERVICES_TO_CHECK="$*"
|
||||
local SERVICES_VALID="postgres minio inbucket openldap elasticsearch opensearch redis keycloak cypress webhook-interactions playwright"
|
||||
local SERVICES_REQUIRED="postgres inbucket"
|
||||
for SERVICE_NAME in $SERVICES_TO_CHECK; do
|
||||
if ! mme2e_is_token_in_list "$SERVICE_NAME" "$SERVICES_VALID"; then
|
||||
mme2e_log "Error, requested invalid service: $SERVICE_NAME" >&2
|
||||
mme2e_log "Valid services are: $SERVICES_VALID" >&2
|
||||
mme2e_log "Aborting" >&2
|
||||
exit 1
|
||||
fi
|
||||
SERVICES_REQUIRED="${SERVICES_REQUIRED/$SERVICE_NAME/}"
|
||||
done
|
||||
if [ -n "${SERVICES_REQUIRED/ /}" ]; then
|
||||
mme2e_log "Missing required services: $SERVICES_REQUIRED" >&2
|
||||
mme2e_log "Aborting" >&2
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
generate_docker_compose_file() {
|
||||
# Generating the server docker-compose file
|
||||
local DC_FILE="server.yml"
|
||||
mme2e_log "Generating docker-compose file in: $DC_FILE"
|
||||
|
||||
# Generate the docker compose override file
|
||||
cat <<EOL >"$DC_FILE"
|
||||
# Image hashes in this file are for amd64 systems
|
||||
# NB: May include paths relative to the "server/build" directory, which contains the original compose file that this yaml is overriding
|
||||
|
||||
services:
|
||||
server:
|
||||
image: \${SERVER_IMAGE}
|
||||
platform: linux/amd64
|
||||
restart: always
|
||||
env_file:
|
||||
- "./.env.server"
|
||||
environment:
|
||||
MM_SERVICESETTINGS_ALLOWCORSFROM: "*"
|
||||
MM_SERVICESETTINGS_ENABLELOCALMODE: "true"
|
||||
MM_SERVICESETTINGS_ENABLESECURITYFIXALERT: "false"
|
||||
MM_CONNECTEDWORKSPACESSETTINGS_ENABLEREMOTECLUSTERSERVICE: "true"
|
||||
MM_CONNECTEDWORKSPACESSETTINGS_ENABLESHAREDWORKSPACES: "true"
|
||||
MM_SQLSETTINGS_DATASOURCE: "postgres://mmuser:mostest_password@localhost:5432/mattermost_test?sslmode=disable&connect_timeout=10&binary_parameters=yes"
|
||||
MM_SQLSETTINGS_DRIVERNAME: "postgres"
|
||||
MM_EMAILSETTINGS_SMTPSERVER: "localhost"
|
||||
MM_CLUSTERSETTINGS_READONLYCONFIG: "false"
|
||||
MM_SERVICEENVIRONMENT: "test"
|
||||
MM_FEATUREFLAGS_CUSTOMPROFILEATTRIBUTES: "true"
|
||||
MM_FEATUREFLAGS_PERMISSIONPOLICIES: "true"
|
||||
MM_FEATUREFLAGS_TEAMMEMBERSHIPACCESSCONTROL: "true"
|
||||
MM_FEATUREFLAGS_CLASSIFICATIONMARKINGS: "true"
|
||||
MM_FEATUREFLAGS_INTEGRATEDBOARDS: "true"
|
||||
MM_FEATUREFLAGS_PROPERTYFIELDRANK: "true"
|
||||
MM_FEATUREFLAGS_RESOURCEATTRIBUTESINPOLICIES: "true"
|
||||
MM_FEATUREFLAGS_ATTRIBUTEVALUEMASKING: "true"
|
||||
MM_FEATUREFLAGS_WYSIWYGEDITOR: "true"
|
||||
MM_FEATUREFLAGS_RECURRINGSCHEDULEDPOSTS: "true"
|
||||
MM_FEATUREFLAGS_ENABLECONCURRENTREACT: "true"
|
||||
MM_LOGSETTINGS_ENABLEDIAGNOSTICS: "false"
|
||||
MM_LOGSETTINGS_CONSOLELEVEL: "DEBUG"
|
||||
network_mode: host
|
||||
ports:
|
||||
- "8065:8065"
|
||||
depends_on:
|
||||
$(for service in $ENABLED_DOCKER_SERVICES; do
|
||||
# The server container will start only if all other dependent services are healthy
|
||||
# Skip creating the dependency for containers that don't need a healthcheck
|
||||
if grep -qE "^(cypress|webhook-interactions|playwright)" <<<"$service"; then
|
||||
continue
|
||||
fi
|
||||
echo " $service:"
|
||||
echo " condition: service_healthy"
|
||||
done)
|
||||
|
||||
$(if mme2e_is_token_in_list "postgres" "$ENABLED_DOCKER_SERVICES"; then
|
||||
echo '
|
||||
postgres:
|
||||
image: mattermostdevelopment/mirrored-postgres:15
|
||||
restart: "no"
|
||||
network_mode: host
|
||||
networks: !reset []
|
||||
extends:
|
||||
file: ../../server/build/docker-compose.common.yml
|
||||
service: postgres'
|
||||
fi)
|
||||
|
||||
$(if mme2e_is_token_in_list "inbucket" "$ENABLED_DOCKER_SERVICES"; then
|
||||
echo '
|
||||
inbucket:
|
||||
network_mode: host
|
||||
networks: !reset []
|
||||
restart: "no"
|
||||
extends:
|
||||
file: ../../server/build/docker-compose.common.yml
|
||||
service: inbucket
|
||||
healthcheck:
|
||||
test: [ "CMD", "nc", "-z", "-w1", "127.0.0.1", "10025" ]
|
||||
interval: 10s
|
||||
timeout: 15s
|
||||
retries: 12'
|
||||
fi)
|
||||
|
||||
$(if mme2e_is_token_in_list "minio" "$ENABLED_DOCKER_SERVICES"; then
|
||||
echo '
|
||||
minio:
|
||||
network_mode: host
|
||||
networks: !reset []
|
||||
restart: "no"
|
||||
extends:
|
||||
file: ../../server/build/docker-compose.common.yml
|
||||
service: minio
|
||||
healthcheck:
|
||||
test: [ "CMD", "mc", "ready", "local" ]
|
||||
interval: 10s
|
||||
timeout: 15s
|
||||
retries: 12'
|
||||
fi)
|
||||
|
||||
$(if mme2e_is_token_in_list "openldap" "$ENABLED_DOCKER_SERVICES"; then
|
||||
echo '
|
||||
openldap:
|
||||
network_mode: host
|
||||
networks: !reset []
|
||||
restart: "no"
|
||||
extends:
|
||||
file: ../../server/build/docker-compose.common.yml
|
||||
service: openldap
|
||||
healthcheck:
|
||||
test: [ "CMD", "bash", "-o", "pipefail", "-c", "ss -ltn \"sport = :636\" | grep -qE \"^LISTEN\"" ]
|
||||
interval: 10s
|
||||
timeout: 15s
|
||||
retries: 12'
|
||||
fi)
|
||||
|
||||
$(if mme2e_is_token_in_list "elasticsearch" "$ENABLED_DOCKER_SERVICES"; then
|
||||
echo '
|
||||
elasticsearch:
|
||||
restart: "no"
|
||||
network_mode: host
|
||||
networks: !reset []
|
||||
environment:
|
||||
xpack.security.enabled: "false"
|
||||
action.destructive_requires_name: "false"
|
||||
extends:
|
||||
file: ../../server/build/docker-compose.common.yml
|
||||
service: elasticsearch
|
||||
healthcheck:
|
||||
test: [ "CMD", "curl", "-fsS", "localhost:9200/_cluster/health?wait_for_status=green&timeout=5s" ]
|
||||
interval: 10s
|
||||
timeout: 15s
|
||||
retries: 12'
|
||||
if [ "$MME2E_ARCHTYPE" = "arm64" ]; then
|
||||
echo '
|
||||
image: mattermostdevelopment/mattermost-elasticsearch:8.9.0
|
||||
platform: linux/arm64/v8'
|
||||
fi
|
||||
fi)
|
||||
|
||||
$(if mme2e_is_token_in_list "opensearch" "$ENABLED_DOCKER_SERVICES"; then
|
||||
echo '
|
||||
opensearch:
|
||||
restart: "no"
|
||||
network_mode: host
|
||||
networks: !reset []
|
||||
environment:
|
||||
http.port: 9200
|
||||
extends:
|
||||
file: ../../server/build/docker-compose.common.yml
|
||||
service: opensearch
|
||||
healthcheck:
|
||||
test: [ "CMD", "curl", "-fsS", "localhost:9200/_cluster/health?wait_for_status=green&timeout=5s" ]
|
||||
interval: 10s
|
||||
timeout: 15s
|
||||
retries: 12'
|
||||
fi)
|
||||
|
||||
$(if mme2e_is_token_in_list "redis" "$ENABLED_DOCKER_SERVICES"; then
|
||||
echo '
|
||||
redis:
|
||||
restart: "no"
|
||||
network_mode: host
|
||||
networks: !reset []
|
||||
extends:
|
||||
file: ../../server/build/docker-compose.common.yml
|
||||
service: redis
|
||||
healthcheck:
|
||||
test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ]
|
||||
interval: 10s
|
||||
timeout: 15s
|
||||
retries: 12'
|
||||
fi)
|
||||
|
||||
$(if mme2e_is_token_in_list "keycloak" "$ENABLED_DOCKER_SERVICES"; then
|
||||
echo '
|
||||
keycloak:
|
||||
restart: "no"
|
||||
network_mode: host
|
||||
networks: !reset []
|
||||
environment:
|
||||
JAVA_OPTS: "-Xms64m -Xmx2G -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true"
|
||||
volumes:
|
||||
- "../../server/build/docker/keycloak/realm-export.json:/opt/keycloak/data/import/realm-export.json"
|
||||
- "../../server/build/docker/keycloak/kc-healthcheck.sh:/usr/local/bin/kc-healthcheck.sh"
|
||||
extends:
|
||||
file: ../../server/build/docker-compose.common.yml
|
||||
service: keycloak
|
||||
healthcheck:
|
||||
# We cannot use a simple curl --silent localhost:9990/health | grep -q \"status\":\"UP\" because theres no curl in the image: https://www.keycloak.org/server/health#_using_the_health_checks
|
||||
test: [ "CMD", "/usr/local/bin/kc-healthcheck.sh" ]
|
||||
interval: 10s
|
||||
timeout: 15s
|
||||
retries: 12'
|
||||
fi)
|
||||
|
||||
$(if mme2e_is_token_in_list "cypress" "$ENABLED_DOCKER_SERVICES"; then
|
||||
echo '
|
||||
cypress:
|
||||
image: "cypress/browsers:node-24.14.0-chrome-145.0.7632.116-1-ff-148.0-edge-145.0.3800.70-1"
|
||||
platform: linux/amd64
|
||||
entrypoint: ["/bin/bash", "-c"]
|
||||
command: ["until [ -f /var/run/mm_terminate ]; do sleep 5; done"]
|
||||
env_file:
|
||||
- "../../e2e-tests/.ci/.env.cypress"
|
||||
environment:
|
||||
CYPRESS_baseUrl: "http://localhost:8065"
|
||||
CYPRESS_dbConnection: "postgres://mmuser:mostest_password@localhost:5432/mattermost_test?sslmode=disable&connect_timeout=10"
|
||||
CYPRESS_smtpUrl: "http://localhost:9001"
|
||||
CYPRESS_webhookBaseUrl: "http://localhost:3000"
|
||||
CYPRESS_chromeWebSecurity: "false"
|
||||
CYPRESS_firstTest: "true"
|
||||
CYPRESS_resetBeforeTest: "true"
|
||||
CYPRESS_allowedUntrustedInternalConnections: "localhost webhook-interactions"
|
||||
TM4J_ENABLE: "false"
|
||||
# disable shared memory X11 affecting Cypress v4 and Chrome
|
||||
# https://github.com/cypress-io/cypress-docker-images/issues/270
|
||||
QT_X11_NO_MITSHM: "1"
|
||||
_X11_NO_MITSHM: "1"
|
||||
_MITSHM: "0"
|
||||
# avoid too many progress messages
|
||||
# https://github.com/cypress-io/cypress/issues/1243
|
||||
CI: "1"
|
||||
# Use /tmp for Cypress cache and config so any UID can write without permission issues
|
||||
CYPRESS_CACHE_FOLDER: /tmp/cypress-cache
|
||||
XDG_CONFIG_HOME: /tmp/xdg-config
|
||||
# Ensure we are independent from the global node environment
|
||||
PATH: /e2e-tests/cypress/node_modules/.bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 8096
|
||||
hard: 1048576
|
||||
working_dir: /e2e-tests/cypress
|
||||
network_mode: host
|
||||
volumes:
|
||||
- "../../e2e-tests/cypress/:/e2e-tests/cypress"
|
||||
- "../../webapp/platform/eslint-plugin/:/webapp/platform/eslint-plugin"'
|
||||
fi)
|
||||
|
||||
$(if mme2e_is_token_in_list "webhook-interactions" "$ENABLED_DOCKER_SERVICES"; then
|
||||
# shellcheck disable=SC2016
|
||||
echo '
|
||||
webhook-interactions:
|
||||
image: node:${NODE_VERSION_REQUIRED}
|
||||
command: sh -c "npm init -y > /dev/null && npm install express@5.1.0 axios@1.11.0 client-oauth2@github:larkox/js-client-oauth2#e24e2eb5dfcbbbb3a59d095e831dbe0012b0ac49 && exec node webhook_serve.js"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-s", "-o/dev/null", "127.0.0.1:3000"]
|
||||
interval: 10s
|
||||
timeout: 15s
|
||||
retries: 12
|
||||
working_dir: /webhook
|
||||
network_mode: host
|
||||
restart: on-failure
|
||||
volumes:
|
||||
- "../../e2e-tests/cypress/webhook_serve.js:/webhook/webhook_serve.js:ro"
|
||||
- "../../e2e-tests/cypress/utils/:/webhook/utils:ro"
|
||||
- "../../e2e-tests/cypress/tests/plugins/post_message_as.js:/webhook/tests/plugins/post_message_as.js:ro"'
|
||||
fi)
|
||||
|
||||
$(if mme2e_is_token_in_list "playwright" "$ENABLED_DOCKER_SERVICES"; then
|
||||
# shellcheck disable=SC2016
|
||||
echo '
|
||||
playwright:
|
||||
image: mcr.microsoft.com/playwright:v1.62.0-noble
|
||||
entrypoint: ["/bin/bash", "-c"]
|
||||
command:
|
||||
- |
|
||||
# Install Node.js based on .nvmrc
|
||||
NODE_VERSION=$$(cat /mattermost/.nvmrc)
|
||||
echo "Installing Node.js $${NODE_VERSION}..."
|
||||
curl -fsSL https://deb.nodesource.com/setup_$${NODE_VERSION%%.*}.x | bash -
|
||||
apt-get install -y nodejs
|
||||
echo "Node.js version: $$(node --version)"
|
||||
# Wait for termination signal
|
||||
until [ -f /var/run/mm_terminate ]; do sleep 5; done
|
||||
env_file:
|
||||
- "./.env.playwright"
|
||||
environment:
|
||||
CI: "true"
|
||||
PLAYWRIGHT_SKIP_BROWSER_GC: 1
|
||||
PW_BASE_URL: http://localhost:8065
|
||||
PW_ADMIN_USERNAME: sysadmin
|
||||
PW_ADMIN_PASSWORD: Sys@dmin-sample1
|
||||
PW_ADMIN_EMAIL: sysadmin@sample.mattermost.com
|
||||
PW_ENSURE_PLUGINS_INSTALLED: ""
|
||||
MM_TEST_DB_URL: "postgres://mmuser:mostest_password@localhost:5432/mattermost_test?sslmode=disable&connect_timeout=10&binary_parameters=yes"
|
||||
PW_HA_CLUSTER_ENABLED: "false"
|
||||
PW_RESET_BEFORE_TEST: "false"
|
||||
PW_HEADLESS: "true"
|
||||
PW_SLOWMO: 0
|
||||
PW_WORKERS: 1
|
||||
PW_SNAPSHOT_ENABLE: "false"
|
||||
PW_PERCY_ENABLE: "false"
|
||||
PW_WEBHOOK_BASE_URL: http://localhost:3000
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 8096
|
||||
hard: 1048576
|
||||
working_dir: /mattermost
|
||||
network_mode: host
|
||||
volumes:
|
||||
- "../../:/mattermost"'
|
||||
fi)
|
||||
EOL
|
||||
|
||||
mme2e_log "docker-compose file generated."
|
||||
}
|
||||
|
||||
generate_env_files() {
|
||||
# Generate .env.server
|
||||
mme2e_log "Generating .env.server"
|
||||
truncate -s 0 .env.server
|
||||
|
||||
# Setting SERVER-specific variables
|
||||
case "$SERVER" in
|
||||
cloud)
|
||||
echo "MM_NOTIFY_ADMIN_COOL_OFF_DAYS=0.00000001" >>.env.server
|
||||
echo 'MM_FEATUREFLAGS_ANNUALSUBSCRIPTION="true"' >>.env.server
|
||||
# Load .env.cloud into .env.server
|
||||
# We assume the file exist at this point. The actual check for that should be done before calling this function
|
||||
cat >>.env.server <.env.cloud
|
||||
;;
|
||||
esac
|
||||
|
||||
# Setting MM_ENV-injected variables
|
||||
# shellcheck disable=SC2086
|
||||
envarr=$(echo ${MM_ENV:-} | tr "," "\n")
|
||||
for env in $envarr; do
|
||||
echo "$env" >>.env.server
|
||||
done
|
||||
|
||||
# Generating service-specific env vars
|
||||
for SERVICE in $ENABLED_DOCKER_SERVICES; do
|
||||
case $SERVICE in
|
||||
opensearch)
|
||||
echo "MM_ELASTICSEARCHSETTINGS_BACKEND=opensearch" >>.env.server
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Generating TEST-specific env files
|
||||
# Some are defaulted in .e2erc due to being needed to other scripts as well
|
||||
export REPO=mattermost # Static, but declared here for making generate_test_cycle.js easier to run
|
||||
export HEADLESS=true # Static, but declared here for making generate_test_cycle.js easier to run
|
||||
case "$TEST" in
|
||||
cypress)
|
||||
mme2e_log "Cypress: Generating .env.cypress"
|
||||
truncate -s 0 .env.cypress
|
||||
|
||||
mme2e_generate_envfile_from_var_names >.env.cypress <<-EOF
|
||||
BRANCH
|
||||
BUILD_ID
|
||||
CI_BASE_URL
|
||||
BROWSER
|
||||
HEADLESS
|
||||
REPO
|
||||
CYPRESS_pushNotificationServer
|
||||
EOF
|
||||
# Adding service-specific cypress variables
|
||||
for SERVICE in $ENABLED_DOCKER_SERVICES; do
|
||||
case $SERVICE in
|
||||
openldap)
|
||||
echo "CYPRESS_ldapServer=localhost" >>.env.cypress
|
||||
echo "CYPRESS_runLDAPSync=true" >>.env.cypress
|
||||
;;
|
||||
minio)
|
||||
echo "CYPRESS_minioS3Endpoint=localhost:9000" >>.env.cypress
|
||||
;;
|
||||
keycloak)
|
||||
echo "CYPRESS_keycloakBaseUrl=http://localhost:8484" >>.env.cypress
|
||||
;;
|
||||
elasticsearch | opensearch)
|
||||
echo "CYPRESS_elasticsearchConnectionURL=http://localhost:9200" >>.env.cypress
|
||||
;;
|
||||
esac
|
||||
done
|
||||
# Adding SERVER-specific cypress variables
|
||||
case "$SERVER" in
|
||||
cloud)
|
||||
echo "CYPRESS_serverEdition=Cloud" >>.env.cypress
|
||||
echo "CYPRESS_cwsURL=${CWS_URL}" >>.env.cypress
|
||||
echo "CYPRESS_cwsAPIURL=${CWS_URL}" >>.env.cypress
|
||||
;;
|
||||
*)
|
||||
echo "CYPRESS_serverEdition=E20" >>.env.cypress
|
||||
;;
|
||||
esac
|
||||
# Add Automation Dashboard related variables to cypress container
|
||||
if [ -n "${AUTOMATION_DASHBOARD_URL:-}" ]; then
|
||||
mme2e_log "Automation dashboard URL is set: loading related variables into the Cypress container"
|
||||
mme2e_generate_envfile_from_var_names >>.env.cypress <<-EOF
|
||||
AUTOMATION_DASHBOARD_URL
|
||||
AUTOMATION_DASHBOARD_TOKEN
|
||||
EOF
|
||||
elif DC_COMMAND="$MME2E_DC_DASHBOARD" mme2e_wait_service_healthy dashboard 1; then
|
||||
mme2e_log "Detected a running automation dashboard: loading its access variables into the Cypress container"
|
||||
cat >>.env.cypress <.env.dashboard
|
||||
fi
|
||||
;;
|
||||
playwright)
|
||||
mme2e_log "Playwright: Generating .env.playwright"
|
||||
mme2e_generate_envfile_from_var_names >.env.playwright <<-EOF
|
||||
BRANCH
|
||||
BUILD_ID
|
||||
EOF
|
||||
;;
|
||||
none)
|
||||
mme2e_log "Requested TEST=$TEST. Skipping generation of test-specific env files."
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Perform SERVER-specific checks/customizations
|
||||
case "$SERVER" in
|
||||
cloud)
|
||||
if ! [ -f .env.cloud ]; then
|
||||
mme2e_log "Error: when using SERVER=$SERVER, the .env.cloud file is expected to exist, before generating the docker-compose file. Aborting." >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# Perform TEST-specific checks/customizations
|
||||
case $TEST in
|
||||
cypress)
|
||||
enable_docker_service cypress
|
||||
enable_docker_service webhook-interactions
|
||||
;;
|
||||
playwright)
|
||||
enable_docker_service playwright
|
||||
enable_docker_service webhook-interactions
|
||||
;;
|
||||
esac
|
||||
|
||||
mme2e_log "Generating docker-compose file using the following parameters..."
|
||||
mme2e_log "TEST: ${TEST}"
|
||||
mme2e_log "SERVER: ${SERVER}"
|
||||
mme2e_log "ENABLED_DOCKER_SERVICES: ${ENABLED_DOCKER_SERVICES}"
|
||||
assert_docker_services_validity "$ENABLED_DOCKER_SERVICES"
|
||||
generate_docker_compose_file
|
||||
generate_env_files
|
||||
74
e2e-tests/.ci/server.prepare.sh
Executable file
74
e2e-tests/.ci/server.prepare.sh
Executable file
@@ -0,0 +1,74 @@
|
||||
#!/bin/bash
|
||||
set -e -u -o pipefail
|
||||
cd "$(dirname "$0")"
|
||||
. .e2erc
|
||||
|
||||
mme2e_log "Configuring starting server parameters that may be changed at runtime"
|
||||
for SETTING in \
|
||||
TeamSettings.EnableOpenServer=true \
|
||||
PluginSettings.Enable=true \
|
||||
PluginSettings.EnableUploads=true \
|
||||
PluginSettings.AutomaticPrepackagedPlugins=true; do
|
||||
mme2e_log "Configuring parameter: $SETTING"
|
||||
# shellcheck disable=SC2046
|
||||
${MME2E_DC_SERVER} exec -T -- server mmctl --local config set $(tr '=' ' ' <<<$SETTING)
|
||||
done
|
||||
if [ -n "${MM_LICENSE:-}" ]; then
|
||||
# We prefer uploading the license here, instead of setting the env var for the server
|
||||
# This is to retain the flexibility of being able to remove it programmatically, if the tests require it
|
||||
mme2e_log "Uploading license to server"
|
||||
${MME2E_DC_SERVER} exec -T -- server mmctl --local license upload-string "$MM_LICENSE"
|
||||
fi
|
||||
|
||||
# if [ "$TEST" = "cypress" ]; then
|
||||
# mme2e_log "Prepare Cypress: install dependencies"
|
||||
# # Create user in Cypress container and work around the node image's assumption that the app files are owned by user 1000
|
||||
# mme2e_log "Creating user for Cypress container"
|
||||
# ${MME2E_DC_SERVER} exec -T -u 0 -- cypress bash -c "id $MME2E_UID || useradd -u $MME2E_UID -m nodeci"
|
||||
# ${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress npm i
|
||||
# ${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress cypress install
|
||||
# mme2e_log "Prepare Cypress: populating fixtures"
|
||||
# ${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress tee tests/fixtures/keycloak.crt >/dev/null <../../server/build/docker/keycloak/keycloak.crt
|
||||
# # Download plugins using wget in the Cypress container
|
||||
# for PLUGIN_URL in \
|
||||
# "https://github.com/mattermost/mattermost-plugin-gitlab/releases/download/v1.3.0/com.github.manland.mattermost-plugin-gitlab-1.3.0.tar.gz" \
|
||||
# "https://github.com/mattermost/mattermost-plugin-demo/releases/download/v0.9.0/com.mattermost.demo-plugin-0.9.0.tar.gz" \
|
||||
# "https://github.com/mattermost/mattermost-plugin-demo/releases/download/v0.8.0/com.mattermost.demo-plugin-0.8.0.tar.gz"; do
|
||||
# PLUGIN_NAME="${PLUGIN_URL##*/}"
|
||||
# PLUGIN_PATH="tests/fixtures/$PLUGIN_NAME"
|
||||
|
||||
# # Check if file exists
|
||||
# if ${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress test -f "$PLUGIN_PATH"; then
|
||||
# mme2e_log "Skipping installation of plugin $PLUGIN_NAME: file exists"
|
||||
# continue
|
||||
# fi
|
||||
|
||||
# # Use wget in the Cypress container instead of curl in server (for distroless compatibility)
|
||||
# mme2e_log "Downloading $PLUGIN_NAME to fixtures directory"
|
||||
# ${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress wget -q -O "/cypress/$PLUGIN_PATH" "$PLUGIN_URL"
|
||||
# done
|
||||
# fi
|
||||
|
||||
# Run service-specific initialization steps
|
||||
for SERVICE in $ENABLED_DOCKER_SERVICES; do
|
||||
case "$SERVICE" in
|
||||
openldap)
|
||||
LDIF_FILE=../../server/tests/test-data.ldif
|
||||
LDIF_CANARY=$(sed -n -E 's/^dn:[[:space:]]*(.*)$/\1/p' ${LDIF_FILE} | tail -n1)
|
||||
if ${MME2E_DC_SERVER} exec -T -- openldap bash -c "ldapsearch -x -D \"cn=admin,dc=mm,dc=test,dc=com\" -w mostest_password -b \"$LDIF_CANARY\" >/dev/null"; then
|
||||
mme2e_log "Skipping configuration for the $SERVICE container: already initialized"
|
||||
continue
|
||||
fi
|
||||
mme2e_log "Configuring the $SERVICE container"
|
||||
${MME2E_DC_SERVER} exec -T openldap bash -c 'ldapadd -Y EXTERNAL -H ldapi:/// -w mostest_password || true' <../../server/tests/custom-schema-objectID.ldif
|
||||
${MME2E_DC_SERVER} exec -T -- openldap bash -c 'ldapadd -Y EXTERNAL -H ldapi:/// -w mostest_password || true' <../../server/tests/custom-schema-cpa.ldif
|
||||
${MME2E_DC_SERVER} exec -T -- openldap bash -c 'ldapadd -x -D "cn=admin,dc=mm,dc=test,dc=com" -w mostest_password' <../../server/tests/test-data.ldif
|
||||
;;
|
||||
minio)
|
||||
mme2e_log "Configuring the $SERVICE container"
|
||||
${MME2E_DC_SERVER} exec -T -- minio sh -c 'mkdir -p /data/mattermost-test'
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
mme2e_log "Mattermost is running and ready for E2E testing"
|
||||
75
e2e-tests/.ci/server.run_cypress.sh
Executable file
75
e2e-tests/.ci/server.run_cypress.sh
Executable file
@@ -0,0 +1,75 @@
|
||||
#!/bin/bash
|
||||
# shellcheck disable=SC2038
|
||||
set -e -u -o pipefail
|
||||
cd "$(dirname "$0")"
|
||||
. .e2erc
|
||||
|
||||
# Print run information
|
||||
mme2e_log "Printing Cypress container informations"
|
||||
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress node -p 'module.paths'
|
||||
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress bash <<"EOF"
|
||||
cat <<INNEREOF
|
||||
node version: $(node -v)
|
||||
npm version: $(npm -v)
|
||||
debian version: $(cat /etc/debian_version)
|
||||
user: $(whoami)
|
||||
chrome: $(google-chrome --version || true)
|
||||
firefox: $(firefox --version || true)
|
||||
INNEREOF
|
||||
EOF
|
||||
|
||||
mme2e_log "Prepare Cypress: ensure container user entry exists for UID $MME2E_UID"
|
||||
${MME2E_DC_SERVER} exec -T -- cypress bash <<EOF
|
||||
getent passwd $MME2E_UID >/dev/null 2>&1 || \
|
||||
useradd --uid $MME2E_UID --gid 0 --home /root --no-create-home --shell /bin/bash mme2euser 2>/dev/null || true
|
||||
EOF
|
||||
|
||||
mme2e_log "Prepare Cypress: install dependencies"
|
||||
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress bash <<EOF
|
||||
npm install --cache /tmp/empty-cache
|
||||
EOF
|
||||
# cypress install verifies the Electron binary, which needs a display and dbus session.
|
||||
# Use xvfb-run and dbus-run-session for the headless virtual display and bus.
|
||||
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress bash <<'EOF'
|
||||
set -euo pipefail
|
||||
Xvfb :99 -screen 0 1280x1024x24 -nolisten tcp &
|
||||
XVFB_PID=$!
|
||||
trap 'kill "$XVFB_PID" 2>/dev/null || true' EXIT
|
||||
sleep 1
|
||||
DISPLAY=:99 dbus-run-session -- cypress install
|
||||
EOF
|
||||
|
||||
# Initialize cypress report directory
|
||||
mme2e_log "Prepare Cypress: clean and initialize report and logs directory"
|
||||
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress bash <<EOF
|
||||
rm -rf logs results
|
||||
mkdir -p logs
|
||||
mkdir -p results/junit
|
||||
touch results/junit/empty.xml
|
||||
echo '<?xml version="1.0" encoding="UTF-8"?>' > results/junit/empty.xml
|
||||
EOF
|
||||
|
||||
# Run cypress test
|
||||
# No need to collect its exit status: if it's nonzero, this script will terminate since we use '-e'
|
||||
LOGFILE_SUFFIX="${CI_BASE_URL//\//_}" # Remove slashes from CI_BASE_URL to produce a usable filename
|
||||
# shellcheck disable=SC2016
|
||||
if ${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress bash -c '[ -n "${AUTOMATION_DASHBOARD_URL}" ]'; then
|
||||
mme2e_log "AUTOMATION_DASHBOARD_URL is set. Using run_test_cycle.js for the cypress run"
|
||||
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress node run_test_cycle.js | tee "../cypress/logs/${LOGFILE_SUFFIX}_cypress.log"
|
||||
else
|
||||
mme2e_log "AUTOMATION_DASHBOARD_URL is unset. Using run_tests.js for the cypress run"
|
||||
# shellcheck disable=SC2086
|
||||
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress node run_tests.js $TEST_FILTER | tee "../cypress/logs/${LOGFILE_SUFFIX}_cypress.log"
|
||||
fi
|
||||
|
||||
# Collect run results
|
||||
cat >../cypress/results/summary.json <<EOF
|
||||
{
|
||||
"passed": $(find ../cypress/results/mochawesome-report/json/tests/ -name '*.json' | xargs -l jq -r '.stats.passes' | jq -s add),
|
||||
"failed": $(find ../cypress/results/mochawesome-report/json/tests/ -name '*.json' | xargs -l jq -r '.stats.failures' | jq -s add),
|
||||
"failed_expected": 0
|
||||
}
|
||||
EOF
|
||||
|
||||
# Collect server logs
|
||||
${MME2E_DC_SERVER} logs --no-log-prefix -- server >"../cypress/logs/${LOGFILE_SUFFIX}_mattermost.log" 2>&1
|
||||
154
e2e-tests/.ci/server.run_playwright.sh
Executable file
154
e2e-tests/.ci/server.run_playwright.sh
Executable file
@@ -0,0 +1,154 @@
|
||||
#!/bin/bash
|
||||
set -e -u -o pipefail
|
||||
cd "$(dirname "$0")"
|
||||
. .e2erc
|
||||
|
||||
mme2e_log "Prepare Playwright: clean and initialize report and logs directory"
|
||||
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- playwright bash <<EOF
|
||||
cd e2e-tests/playwright
|
||||
rm -rf logs results storage_state
|
||||
mkdir -p logs results
|
||||
touch logs/mattermost.log
|
||||
EOF
|
||||
|
||||
mme2e_log "Prepare Playwright: install dependencies"
|
||||
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- playwright bash <<EOF
|
||||
cd webapp/
|
||||
npm install --cache /tmp/empty-cache
|
||||
cd ../e2e-tests/playwright
|
||||
npm install --cache /tmp/empty-cache
|
||||
EOF
|
||||
|
||||
mme2e_log "Prepare Playwright: environment info"
|
||||
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- playwright bash <<"EOF"
|
||||
cat <<INNEREOF
|
||||
debian = $(cat /etc/debian_version)
|
||||
uname = $(uname -m)
|
||||
node = $(node -v)
|
||||
npm = $(npm -v)
|
||||
playwright = $(cd e2e-tests/playwright && npx playwright --version)
|
||||
browsers
|
||||
$(du -hs ~/ms-playwright/* | awk '{print " = " $2 " (" $1 ")"}')
|
||||
INNEREOF
|
||||
EOF
|
||||
|
||||
# Enable next line to debug Playwright
|
||||
# export DEBUG=pw:protocol,pw:browser,pw:api
|
||||
|
||||
mme2e_log "Start LibreTranslate mock server for autotranslation tests"
|
||||
${MME2E_DC_SERVER} exec -u "$MME2E_UID" -d -- playwright bash -c "cd e2e-tests/playwright && npm run start:libretranslate-mock" || true
|
||||
|
||||
mme2e_log "Wait for LibreTranslate mock server to be ready"
|
||||
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- playwright bash -c "for i in {1..30}; do curl -s http://localhost:3010/ && exit 0; sleep 1; done; echo 'Mock server failed to start'; exit 1" || true
|
||||
|
||||
# Pass TEST_FILTER and PW_SHARD with `docker compose exec -e VAR`
|
||||
# (no =value) so Compose copies them from this shell's environment.
|
||||
|
||||
mme2e_log "Playwright: running chrome project (sharded)"
|
||||
${MME2E_DC_SERVER} exec -i -T -u "$MME2E_UID" \
|
||||
-e TEST_FILTER \
|
||||
-e PW_SHARD \
|
||||
-- playwright bash -lc "cd e2e-tests/playwright && npm run test:ci -- \${TEST_FILTER:+\$TEST_FILTER} \${PW_SHARD:+\$PW_SHARD}" | tee ../playwright/logs/playwright-first.log || true
|
||||
|
||||
# IMPORTANT: Playwright's blob reporter WIPES its outputDir at the start
|
||||
# of every invocation. If we leave first-pass blobs inside
|
||||
# `results/blob-report/`, a follow-on `npm run test:ci` deletes them.
|
||||
STASH_DIR="e2e-tests/playwright/.blob-stash"
|
||||
|
||||
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- playwright bash -lc "
|
||||
rm -rf ${STASH_DIR}
|
||||
mkdir -p ${STASH_DIR}
|
||||
if compgen -G 'e2e-tests/playwright/results/blob-report/*.zip' >/dev/null 2>&1; then
|
||||
for f in e2e-tests/playwright/results/blob-report/*.zip; do
|
||||
mv \"\$f\" \"${STASH_DIR}/first-\$(basename \"\$f\")\"
|
||||
done
|
||||
fi
|
||||
" || true
|
||||
|
||||
RESULTS_FILE="../playwright/results/reporter/results.json"
|
||||
FAILED_SPECS=""
|
||||
if [ -f "$RESULTS_FILE" ]; then
|
||||
FAILED_SPECS=$(jq -r '
|
||||
[.suites[] | . as $top |
|
||||
(recurse(.suites[]?) | .specs[]? | .tests[]? |
|
||||
select((.results | length) > 0) |
|
||||
select((.results | last).status == "failed" or (.results | last).status == "timedOut") |
|
||||
(.location.file // $top.file))
|
||||
] | map(select(. != null)) | unique | join(",")
|
||||
' "$RESULTS_FILE" 2>/dev/null || echo "")
|
||||
fi
|
||||
|
||||
if [ -n "$FAILED_SPECS" ]; then
|
||||
mme2e_log "Retrying failed specs on the same runner: $FAILED_SPECS"
|
||||
# Split the comma-separated list into an array and shell-quote every entry
|
||||
# with printf '%q' so that filenames containing shell metacharacters
|
||||
# (;, &&, $(), etc.) are treated as literals by the inner bash -lc shell
|
||||
# and cannot be used for command injection.
|
||||
IFS=',' read -ra _spec_array <<< "$FAILED_SPECS"
|
||||
SPEC_ARGS=$(printf '%q ' "${_spec_array[@]}")
|
||||
|
||||
${MME2E_DC_SERVER} exec -i -T -u "$MME2E_UID" \
|
||||
-e TEST_FILTER \
|
||||
-e PW_SHARD \
|
||||
-- playwright bash -lc "cd e2e-tests/playwright && npm run test:ci -- $SPEC_ARGS \${TEST_FILTER:+\$TEST_FILTER} \${PW_SHARD:+\$PW_SHARD}" | tee ../playwright/logs/playwright-retry.log || true
|
||||
|
||||
# Stash retry blobs alongside the first-pass blobs.
|
||||
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- playwright bash -lc "
|
||||
if compgen -G 'e2e-tests/playwright/results/blob-report/*.zip' >/dev/null 2>&1; then
|
||||
for f in e2e-tests/playwright/results/blob-report/*.zip; do
|
||||
mv \"\$f\" \"${STASH_DIR}/retry-\$(basename \"\$f\")\"
|
||||
done
|
||||
fi
|
||||
" || true
|
||||
fi
|
||||
|
||||
# Move all stashed blobs back into blob-report/ for final merge-reports
|
||||
# and for upload-artifact. This step runs whether or not retries ran:
|
||||
# if no retries, we just put the first-pass blobs back.
|
||||
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- playwright bash -lc "
|
||||
mkdir -p e2e-tests/playwright/results/blob-report
|
||||
if compgen -G '${STASH_DIR}/*.zip' >/dev/null 2>&1; then
|
||||
mv ${STASH_DIR}/*.zip e2e-tests/playwright/results/blob-report/
|
||||
fi
|
||||
rmdir ${STASH_DIR} 2>/dev/null || true
|
||||
" || true
|
||||
|
||||
# Merge the combined blob set into a single results.json so the per-shard
|
||||
# `summary.json` below reflects first-pass + retry outcomes. The cross-
|
||||
# shard merge in the CI template's `calculate-results` job will re-merge
|
||||
# all shards' blobs from the same `blob-report/` contents.
|
||||
if [ -n "$FAILED_SPECS" ]; then
|
||||
mme2e_log "Merging first-pass + retry blob reports"
|
||||
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- playwright bash -lc "
|
||||
cd e2e-tests/playwright
|
||||
rm -rf results/reporter
|
||||
mkdir -p results/reporter
|
||||
npx playwright merge-reports --config merge.config.mjs results/blob-report
|
||||
" || true
|
||||
else
|
||||
mme2e_log "No failed specs in shard; skipping inline retry"
|
||||
fi
|
||||
|
||||
# Keep a combined tail log for backwards-compat with anything grepping
|
||||
# playwright.log. The authoritative results are the merged blob reports.
|
||||
cat ../playwright/logs/playwright-first.log \
|
||||
../playwright/logs/playwright-retry.log \
|
||||
>../playwright/logs/playwright.log 2>/dev/null || true
|
||||
|
||||
# Collect run results from the merged results.json. This summary is used
|
||||
# only by local dev / the cypress-oriented template; the playwright CI
|
||||
# template computes authoritative totals from the merged blob reports it
|
||||
# downloads across all shards.
|
||||
# Documentation: https://playwright.dev/docs/api/class-testcase#test-case-expected-status
|
||||
if [ -f ../playwright/results/reporter/results.json ]; then
|
||||
jq -f /dev/stdin ../playwright/results/reporter/results.json >../playwright/results/summary.json <<EOF
|
||||
{
|
||||
passed: .stats.expected,
|
||||
failed: .stats.unexpected,
|
||||
failed_expected: (.stats.skipped + .stats.flaky)
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Collect server logs
|
||||
${MME2E_DC_SERVER} logs --no-log-prefix -- server >../playwright/logs/mattermost.log 2>&1
|
||||
113
e2e-tests/.ci/server.run_specs.sh
Executable file
113
e2e-tests/.ci/server.run_specs.sh
Executable file
@@ -0,0 +1,113 @@
|
||||
#!/bin/bash
|
||||
# shellcheck disable=SC2038
|
||||
# Run specific spec files
|
||||
# Usage: SPEC_FILES="path/to/spec1.ts,path/to/spec2.ts" make start-server run-specs
|
||||
|
||||
set -e -u -o pipefail
|
||||
cd "$(dirname "$0")"
|
||||
. .e2erc
|
||||
|
||||
if [ -z "${SPEC_FILES:-}" ]; then
|
||||
mme2e_log "Error: SPEC_FILES environment variable is required"
|
||||
mme2e_log "Usage: SPEC_FILES=\"path/to/spec.ts\" make start-server run-specs"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mme2e_log "Running spec files: $SPEC_FILES"
|
||||
|
||||
case $TEST in
|
||||
cypress)
|
||||
mme2e_log "Running Cypress with specified specs"
|
||||
# Initialize cypress report directory
|
||||
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress bash <<EOF
|
||||
rm -rf logs results
|
||||
mkdir -p logs
|
||||
mkdir -p results/junit
|
||||
mkdir -p results/mochawesome-report/json/tests
|
||||
touch results/junit/empty.xml
|
||||
echo '<?xml version="1.0" encoding="UTF-8"?>' > results/junit/empty.xml
|
||||
EOF
|
||||
|
||||
# Install dependencies
|
||||
mme2e_log "Prepare Cypress: install dependencies"
|
||||
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress bash <<EOF
|
||||
cd webapp/
|
||||
npm install --cache /tmp/empty-cache
|
||||
cd ../e2e-tests/cypress
|
||||
npm install --cache /tmp/empty-cache
|
||||
EOF
|
||||
|
||||
# Run cypress with specific spec files and mochawesome reporter
|
||||
LOGFILE_SUFFIX="${CI_BASE_URL//\//_}_specs"
|
||||
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress npx cypress run \
|
||||
--spec "$SPEC_FILES" \
|
||||
--reporter cypress-multi-reporters \
|
||||
--reporter-options configFile=reporter-config.json \
|
||||
| tee "../cypress/logs/${LOGFILE_SUFFIX}_cypress.log" || true
|
||||
|
||||
# Collect run results
|
||||
if [ -d ../cypress/results/mochawesome-report/json/tests/ ]; then
|
||||
cat >../cypress/results/summary.json <<EOF
|
||||
{
|
||||
"passed": $(find ../cypress/results/mochawesome-report/json/tests/ -name '*.json' | xargs -l jq -r '.stats.passes' | jq -s add),
|
||||
"failed": $(find ../cypress/results/mochawesome-report/json/tests/ -name '*.json' | xargs -l jq -r '.stats.failures' | jq -s add),
|
||||
"failed_expected": 0
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Collect server logs
|
||||
${MME2E_DC_SERVER} logs --no-log-prefix -- server >"../cypress/logs/${LOGFILE_SUFFIX}_mattermost.log" 2>&1
|
||||
;;
|
||||
playwright)
|
||||
mme2e_log "Running Playwright with specified specs"
|
||||
# Convert comma-separated to space-separated for playwright
|
||||
SPEC_ARGS=$(echo "$SPEC_FILES" | tr ',' ' ')
|
||||
|
||||
# Initialize playwright report and logs directory
|
||||
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- playwright bash <<EOF
|
||||
cd e2e-tests/playwright
|
||||
rm -rf logs results storage_state
|
||||
mkdir -p logs results
|
||||
touch logs/mattermost.log
|
||||
EOF
|
||||
|
||||
# Install dependencies
|
||||
mme2e_log "Prepare Playwright: install dependencies"
|
||||
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- playwright bash <<EOF
|
||||
cd webapp/
|
||||
npm install --cache /tmp/empty-cache
|
||||
cd ../e2e-tests/playwright
|
||||
npm install --cache /tmp/empty-cache
|
||||
EOF
|
||||
|
||||
# Run playwright with specific spec files
|
||||
LOGFILE_SUFFIX="${CI_BASE_URL//\//_}_specs"
|
||||
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" \
|
||||
-e TEST_FILTER \
|
||||
-e PW_SHARD \
|
||||
-- playwright bash -lc "cd e2e-tests/playwright && npm run test:ci -- $SPEC_ARGS \${TEST_FILTER:+\$TEST_FILTER} \${PW_SHARD:+\$PW_SHARD}" | tee "../playwright/logs/${LOGFILE_SUFFIX}_playwright.log" || true
|
||||
|
||||
# Collect run results (if results.json exists)
|
||||
if [ -f ../playwright/results/reporter/results.json ]; then
|
||||
jq -f /dev/stdin ../playwright/results/reporter/results.json >../playwright/results/summary.json <<EOF
|
||||
{
|
||||
passed: .stats.expected,
|
||||
failed: .stats.unexpected,
|
||||
failed_expected: (.stats.skipped + .stats.flaky)
|
||||
}
|
||||
EOF
|
||||
mme2e_log "Results file found and summary generated"
|
||||
fi
|
||||
|
||||
# Collect server logs
|
||||
${MME2E_DC_SERVER} logs --no-log-prefix -- server >"../playwright/logs/${LOGFILE_SUFFIX}_mattermost.log" 2>&1
|
||||
;;
|
||||
*)
|
||||
mme2e_log "Error, unsupported value for TEST: $TEST" >&2
|
||||
mme2e_log "Aborting" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
mme2e_log "Spec run complete"
|
||||
22
e2e-tests/.ci/server.run_test.sh
Executable file
22
e2e-tests/.ci/server.run_test.sh
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
set -e -u -o pipefail
|
||||
cd "$(dirname "$0")"
|
||||
. .e2erc
|
||||
|
||||
case $TEST in
|
||||
cypress)
|
||||
./server.run_cypress.sh
|
||||
;;
|
||||
playwright)
|
||||
./server.run_playwright.sh
|
||||
;;
|
||||
none)
|
||||
mme2e_log "Running with TEST=$TEST. No tests to run."
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
mme2e_log "Error, unsupported value for TEST: $TEST" >&2
|
||||
mme2e_log "Aborting" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
123
e2e-tests/.ci/server.start.sh
Executable file
123
e2e-tests/.ci/server.start.sh
Executable file
@@ -0,0 +1,123 @@
|
||||
#!/bin/bash
|
||||
set -e -u -o pipefail
|
||||
cd "$(dirname "$0")"
|
||||
. .e2erc
|
||||
|
||||
# Wait for the required server image
|
||||
mme2e_log "Waiting for server image to be available"
|
||||
mme2e_wait_image "$SERVER_IMAGE" 4 30
|
||||
|
||||
# Launch mattermost-server, and wait for it to be healthy
|
||||
mme2e_log "Starting E2E containers"
|
||||
${MME2E_DC_SERVER} create
|
||||
|
||||
# `docker compose up -d` returns non-zero the moment any depended container
|
||||
# exits during startup, which masks openldap's own `restart: always` policy.
|
||||
# On a small fraction of ubuntu-24.04 runners the osixia/openldap:1.4.0 image
|
||||
# exits 1 on first boot (suspected init-script race under runner load). Retry
|
||||
# the `up` a bounded number of times, force-recreating openldap between tries
|
||||
# so its first-boot bootstrap re-runs cleanly, and dump rich diagnostics on
|
||||
# every failure so future CI failures contain the actual data we need to
|
||||
# permanently root-cause this. The diagnostics directory is also uploaded as
|
||||
# a workflow artifact (see e2e-tests-*-template.yml `ci/upload-docker-diagnostics`).
|
||||
DIAG_DIR="${PWD}/../docker-diagnostics"
|
||||
mkdir -p "$DIAG_DIR"
|
||||
|
||||
dump_openldap_diagnostics() {
|
||||
local label="$1"
|
||||
local out="$DIAG_DIR/${label}"
|
||||
mkdir -p "$out"
|
||||
mme2e_log "[diagnostics:${label}] capturing openldap state to $out"
|
||||
|
||||
# Container-level state (exit code, OOMKilled, error string, restart count)
|
||||
docker inspect mmserver-openldap-1 >"$out/openldap.inspect.json" 2>&1 || true
|
||||
${MME2E_DC_SERVER} ps -a >"$out/compose.ps.txt" 2>&1 || true
|
||||
${MME2E_DC_SERVER} logs --no-log-prefix -- openldap >"$out/openldap.log" 2>&1 || true
|
||||
|
||||
# Merged compose config — confirms which security_opt / cap_add / image is actually applied
|
||||
${MME2E_DC_SERVER} config >"$out/compose.config.yml" 2>&1 || true
|
||||
|
||||
# Host-level state useful for OOM / AppArmor diagnosis
|
||||
uname -a >"$out/host.uname.txt" 2>&1 || true
|
||||
free -m >"$out/host.free.txt" 2>&1 || true
|
||||
df -h >"$out/host.df.txt" 2>&1 || true
|
||||
docker version >"$out/docker.version.txt" 2>&1 || true
|
||||
docker info >"$out/docker.info.txt" 2>&1 || true
|
||||
docker compose version >"$out/compose.version.txt" 2>&1 || true
|
||||
cat /proc/sys/kernel/apparmor_restrict_unprivileged_userns >"$out/host.apparmor_userns.txt" 2>&1 || true
|
||||
# AppArmor denials and OOM kills land in dmesg — grep them out (needs sudo on GH runners).
|
||||
sudo dmesg | tail -200 >"$out/host.dmesg.tail.txt" 2>&1 || true
|
||||
sudo dmesg | grep -iE 'apparmor|denied|oom|killed|openldap|slapd' >"$out/host.dmesg.relevant.txt" 2>&1 || true
|
||||
|
||||
# Echo the most useful slice straight to the workflow log so it shows up
|
||||
# in the GH Actions UI without needing to download the artifact.
|
||||
mme2e_log "----- openldap inspect (exit/oom/error) -----"
|
||||
docker inspect mmserver-openldap-1 \
|
||||
--format 'ExitCode={{.State.ExitCode}} OOMKilled={{.State.OOMKilled}} Error={{.State.Error}} Restarts={{.RestartCount}} Status={{.State.Status}}' \
|
||||
2>&1 || true
|
||||
mme2e_log "----- openldap log (last 100) -----"
|
||||
${MME2E_DC_SERVER} logs --no-log-prefix --tail=100 -- openldap 2>&1 || true
|
||||
mme2e_log "----- relevant dmesg -----"
|
||||
sudo dmesg | grep -iE 'apparmor|denied|oom|killed|openldap|slapd' | tail -40 2>&1 || true
|
||||
mme2e_log "----- end diagnostics:${label} -----"
|
||||
}
|
||||
|
||||
UP_ATTEMPTS=3
|
||||
for attempt in $(seq 1 $UP_ATTEMPTS); do
|
||||
if ${MME2E_DC_SERVER} up -d --remove-orphans; then
|
||||
break
|
||||
fi
|
||||
dump_openldap_diagnostics "up-attempt-${attempt}"
|
||||
if [ "$attempt" -eq "$UP_ATTEMPTS" ]; then
|
||||
mme2e_log "compose up failed after ${UP_ATTEMPTS} attempts; aborting"
|
||||
exit 1
|
||||
fi
|
||||
# Force-recreate openldap so its first-boot init re-runs from a clean state
|
||||
${MME2E_DC_SERVER} rm -fsv openldap || true
|
||||
sleep 5
|
||||
done
|
||||
|
||||
# Postgres check
|
||||
if ! mme2e_wait_command_success "${MME2E_DC_SERVER} exec -T -- postgres pg_isready -h localhost" "Waiting for postgres to accept connections" "30" "5"; then
|
||||
mme2e_log "Postgres not accepting connections"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! mme2e_wait_service_healthy server 60 10; then
|
||||
mme2e_log "Mattermost container not healthy, retry attempts exhausted. Giving up." >&2
|
||||
exit 1
|
||||
fi
|
||||
# shellcheck disable=SC2043
|
||||
for MIGRATION in migration_advanced_permissions_phase_2; do
|
||||
# Query explanation: if it doesn't find the migration in the table, there are 0 results and the command fails with a divide-by-zero error. Otherwise the command succeeds
|
||||
MIGRATION_CHECK_COMMAND="${MME2E_DC_SERVER} exec -T postgres sh -c 'PGPASSWORD=mostest_password psql -U mmuser mattermost_test -c \"select 1 / (select count(*) from Systems where name = '\''${MIGRATION}'\'' and value = '\''true'\'');\"'"
|
||||
if ! mme2e_wait_command_success "$MIGRATION_CHECK_COMMAND" "Waiting for migration to be completed: ${MIGRATION}" "10" "10"; then
|
||||
mme2e_log "Migration ${MIGRATION} not completed, retry attempts exhausted. Giving up." >&2
|
||||
|
||||
LOG_DIR="../${TEST}/logs"
|
||||
mkdir -p "$LOG_DIR"
|
||||
|
||||
# Save server logs to a file
|
||||
${MME2E_DC_SERVER} logs --no-log-prefix -- server >"$LOG_DIR/server.log"
|
||||
mme2e_log "Server logs saved to server.log"
|
||||
|
||||
# Save postgres logs to a file
|
||||
${MME2E_DC_SERVER} logs --no-log-prefix -- postgres >"$LOG_DIR/postgres.log"
|
||||
mme2e_log "Postgres logs saved to postgres.log"
|
||||
|
||||
exit 2
|
||||
fi
|
||||
mme2e_log "${MIGRATION}: completed."
|
||||
done
|
||||
mme2e_log "Mattermost container is running and healthy"
|
||||
|
||||
# Wait for webhook-interactions container if running cypress or playwright tests
|
||||
if [ "$TEST" = "cypress" ] || [ "$TEST" = "playwright" ]; then
|
||||
mme2e_log "Checking webhook-interactions container health"
|
||||
${MME2E_DC_SERVER} logs --no-log-prefix -- webhook-interactions 2>&1 | tail -5
|
||||
if ! mme2e_wait_service_healthy webhook-interactions 2 10; then
|
||||
mme2e_log "Webhook interactions container not healthy, retry attempts exhausted. Giving up." >&2
|
||||
exit 1
|
||||
fi
|
||||
mme2e_log "Webhook interactions container is running and healthy"
|
||||
fi
|
||||
7
e2e-tests/.ci/server.stop.sh
Executable file
7
e2e-tests/.ci/server.stop.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
set -e -u -o pipefail
|
||||
cd "$(dirname "$0")"
|
||||
. .e2erc
|
||||
|
||||
mme2e_log "Stopping E2E containers"
|
||||
${MME2E_DC_SERVER} down -v --remove-orphans
|
||||
Reference in New Issue
Block a user