name: Server Test Template on: workflow_call: inputs: name: required: true type: string datasource: required: true type: string drivername: required: true type: string logsartifact: required: true type: string fullyparallel: required: false type: boolean default: true allow-failure: required: false type: boolean default: false enablecoverage: required: false type: boolean default: false go-version: required: true type: string fips-enabled: required: false default: false type: boolean # -- Test sharding inputs (leave defaults for non-sharded callers) -- shard-index: required: false type: number default: -1 # -1 = no sharding; run all tests shard-total: required: false type: number default: 1 permissions: id-token: write contents: read jobs: test: name: ${{ inputs.name }} runs-on: ubuntu-latest-8-cores continue-on-error: ${{ inputs.allow-failure }} # Used to avoid blocking PRs in case of flakiness env: COMPOSE_PROJECT_NAME: ghactions steps: - name: buildenv/docker-login # Only FIPS requires login for private build container. (Forks won't have credentials.) if: inputs.fips-enabled uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Checkout mattermost project uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Restore test timing data if: inputs.shard-total > 1 id: timing-cache uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: | server/prev-report.xml server/prev-gotestsum.json # Always restore from master — timing is only saved on the default # branch and is stable enough for shard balancing. key: server-test-timing-master restore-keys: | server-test-timing- - name: Setup BUILD_IMAGE id: build run: | if [[ ${{ inputs.fips-enabled }} == 'true' ]]; then echo "BUILD_IMAGE=mattermost/mattermost-build-server-fips:${{ inputs.go-version }}" >> "${GITHUB_OUTPUT}" echo "LOG_ARTIFACT_NAME=${{ inputs.logsartifact }}-fips" >> "${GITHUB_OUTPUT}" else echo "BUILD_IMAGE=mattermost/mattermost-build-server:${{ inputs.go-version }}" >> "${GITHUB_OUTPUT}" echo "LOG_ARTIFACT_NAME=${{ inputs.logsartifact }}" >> "${GITHUB_OUTPUT}" fi - name: Store required variables for publishing results run: | echo "${{ inputs.name }}" > server/test-name echo "${{ github.event.pull_request.number }}" > server/pr-number - name: Run docker compose env: POSTGRES_PASSWORD: ${{ inputs.fips-enabled && 'mostest-fips-test' || 'mostest' }} run: | cd server/build docker compose --ansi never run --rm start_dependencies cat ../tests/custom-schema-objectID.ldif | docker compose --ansi never exec -T openldap bash -c 'ldapadd -Y EXTERNAL -H ldapi:/// -w mostest || true'; cat ../tests/custom-schema-cpa.ldif | docker compose --ansi never exec -T openldap bash -c 'ldapadd -Y EXTERNAL -H ldapi:/// -w mostest || true'; cat ../tests/test-data.ldif | docker compose --ansi never exec -T openldap bash -c 'ldapadd -x -D "cn=admin,dc=mm,dc=test,dc=com" -w mostest'; docker compose --ansi never exec -T minio sh -c 'mkdir -p /data/mattermost-test'; docker compose --ansi never ps # ── Test-level sharding ──────────────────────────────────────────── # When shard-total > 1, we split tests across N parallel runners. # # Two-tier splitting strategy: # - "Light" packages (< 5 min): assigned whole to a shard # - "Heavy" packages (≥ 5 min, e.g. api4, app): individual tests # are distributed across shards using -run regex filters # # See server/scripts/shard-split.js for the full algorithm. # ───────────────────────────────────────────────────────────────────── - name: Setup Go for test discovery if: inputs.shard-total > 1 uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: go-version: ${{ inputs.go-version }} - name: Split tests across shards if: inputs.shard-total > 1 id: test_split working-directory: server env: SHARD_INDEX: ${{ inputs.shard-index }} SHARD_TOTAL: ${{ inputs.shard-total }} run: | set -euo pipefail # ── List all test packages ── echo "::group::Listing test packages" TE_PKGS=$(find ./public/ ./ -name '*_test.go' -not -path './enterprise/*' -not -path './cmd/mmctl/*' 2>/dev/null \ | sed 's|/[^/]*$||' | sort -u \ | sed 's|^\./|github.com/mattermost/mattermost/server/v8/|' \ | sed 's|github.com/mattermost/mattermost/server/v8/public/|github.com/mattermost/mattermost/server/public/|') EE_PKGS=$(find ./enterprise/ -name '*_test.go' 2>/dev/null \ | sed 's|/[^/]*$||' | sort -u \ | sed 's|^\./|github.com/mattermost/mattermost/server/v8/|') ALL_PKGS=$(printf '%s\n%s' "$TE_PKGS" "$EE_PKGS" | grep -v '^$' | sort -u) TOTAL_PKGS=$(echo "$ALL_PKGS" | wc -l) echo "Found $TOTAL_PKGS test packages" echo "::endgroup::" if [[ "$TOTAL_PKGS" -eq 0 ]]; then echo "WARNING: No test packages found" echo "has_packages=false" >> "$GITHUB_OUTPUT" exit 0 fi echo "$ALL_PKGS" > all-packages.txt # ── Run shard solver ── node scripts/shard-split.js echo "has_packages=true" >> "$GITHUB_OUTPUT" - name: Run Tests env: BUILD_IMAGE: ${{ steps.build.outputs.BUILD_IMAGE }} run: | if [[ ${{ github.ref_name }} == 'master' && ${{ inputs.fullyparallel }} != true ]]; then export RACE_MODE="-race" fi MAKE_ARGS="test-server${RACE_MODE} BUILD_NUMBER=${GITHUB_HEAD_REF}-${GITHUB_RUN_ID}" DOCKER_CMD="make ${MAKE_ARGS}" # When sharding is active, use the multi-run wrapper script if [[ "${{ inputs.shard-total }}" -gt 1 && -f server/shard-te-packages.txt ]]; then SHARD_TE=$(cat server/shard-te-packages.txt) SHARD_EE=$(cat server/shard-ee-packages.txt) HEAVY_RUNS="" if [[ -f server/shard-heavy-runs.txt && -s server/shard-heavy-runs.txt ]]; then HEAVY_RUNS=$(cat server/shard-heavy-runs.txt) fi if [[ -z "$HEAVY_RUNS" ]]; then # No heavy packages — single run via Makefile with package filter. # Read packages from files at runtime to avoid interpolating # file-system-derived paths into a generated shell script. cat > server/run-shard-tests.sh <