mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-05-26 09:28:35 +08:00
Previously, system tests ran with `PARALLEL_TEST_PROCESSORS = nproc / 2` (8 workers on the 16-core runners, 4 on the 8-core ones). This change bumps it to `nproc / 2 + 2` (10 on 16-core, 6 on 8-core), shortening every system test variant by 13-29%. `core system` is the slowest variant and gates the system test workflow wall time, so total CI wall drops by roughly 21% at the median (~717s to ~567s).
404 lines
16 KiB
YAML
Vendored
404 lines
16 KiB
YAML
Vendored
name: Tests
|
|
|
|
on:
|
|
pull_request:
|
|
paths-ignore:
|
|
- ".github/workflows/migration-tests.yml"
|
|
- ".github/dependabot.yml"
|
|
- ".github/labeler.yml"
|
|
- "migrations/**"
|
|
push:
|
|
branches:
|
|
- main
|
|
- beta
|
|
- stable
|
|
- release/*
|
|
|
|
concurrency:
|
|
group: tests-${{ format('{0}-{1}', github.head_ref || github.run_number, github.job) }}
|
|
cancel-in-progress: true
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
build:
|
|
if: github.event_name == 'pull_request' || github.repository != 'discourse/discourse-private-mirror'
|
|
name: ${{ (matrix.target == 'core-plugins' && 'plugins (core)') || (matrix.target == 'official-plugins' && 'plugins (official)') || (matrix.target == 'chat' && 'plugins (chat)') || matrix.target }} ${{ matrix.build_type }}
|
|
runs-on: >-
|
|
${{
|
|
github.repository_owner == 'discourse' && (
|
|
matrix.build_type == 'system' && (matrix.target == 'core' || matrix.target == 'core-plugins' || matrix.target == 'official-plugins')
|
|
&& 'cdck-linux-16-core'
|
|
|| 'cdck-linux-8-core'
|
|
)
|
|
|| 'ubuntu-latest'
|
|
}}
|
|
container: discourse/discourse_test:release
|
|
timeout-minutes: >-
|
|
${{
|
|
matrix.build_type == 'system' && (matrix.target == 'core-plugins' || matrix.target == 'official-plugins' || matrix.target == 'core')
|
|
&& 30
|
|
|| 20
|
|
}}
|
|
|
|
defaults:
|
|
run:
|
|
shell: bash
|
|
|
|
env:
|
|
RAILS_ENV: test
|
|
PGUSER: discourse
|
|
PGPASSWORD: discourse
|
|
USES_PARALLEL_DATABASES: ${{ matrix.build_type == 'backend' || matrix.build_type == 'system' }}
|
|
CAPYBARA_DEFAULT_MAX_WAIT_TIME: 20
|
|
MINIO_RUNNER_LOG_LEVEL: WARN
|
|
DISCOURSE_TURBO_RSPEC_RETRY_AND_LOG_FLAKY_TESTS: ${{ (matrix.build_type == 'system' || matrix.build_type == 'backend') && '1' }}
|
|
CHEAP_SOURCE_MAPS: "1"
|
|
DBUS_SESSION_BUS_ADDRESS: "unix:path=/dev/null"
|
|
MINIO_RUNNER_INSTALL_DIR: /home/discourse/.minio_runner
|
|
EMBER_ENV: development
|
|
LOAD_PLUGINS: ${{ (matrix.target == 'core-plugins' || matrix.target == 'official-plugins' || matrix.target == 'plugins' || matrix.target == 'chat') && '1' || '0' }}
|
|
QUNIT_REUSE_BUILD: 1
|
|
|
|
strategy:
|
|
fail-fast: false
|
|
|
|
matrix:
|
|
include:
|
|
- target: core
|
|
build_type: backend
|
|
- target: core
|
|
build_type: frontend
|
|
- target: core
|
|
build_type: system
|
|
- target: plugins
|
|
build_type: backend
|
|
- target: plugins
|
|
build_type: frontend
|
|
- target: core-plugins
|
|
build_type: system
|
|
- target: chat
|
|
build_type: system
|
|
- target: official-plugins
|
|
build_type: system
|
|
- target: themes
|
|
build_type: frontend
|
|
- target: themes
|
|
build_type: system
|
|
|
|
steps:
|
|
- name: Enable jemalloc
|
|
run: echo /usr/lib/libjemalloc.so.1 > /etc/ld.so.preload
|
|
|
|
- name: Verify jemalloc is loaded into Ruby
|
|
run: |
|
|
ruby -e 'abort "jemalloc not loaded" unless File.read("/proc/self/maps").include?("libjemalloc")'
|
|
|
|
- name: Set working directory owner
|
|
run: chown root:root .
|
|
|
|
- name: Set PARALLEL_TEST_PROCESSORS for system tests
|
|
if: matrix.build_type == 'system'
|
|
run: |
|
|
echo "PARALLEL_TEST_PROCESSORS=$(($(nproc) / 2 + 2))" >> $GITHUB_ENV
|
|
|
|
- name: Set QUNIT_PARALLEL for QUnit tests
|
|
if: matrix.build_type == 'frontend'
|
|
run: |
|
|
if [ "${{ matrix.target }}" = "themes" ]; then
|
|
echo "QUNIT_PARALLEL=2" >> $GITHUB_ENV
|
|
else
|
|
echo "QUNIT_PARALLEL=$(($(nproc) / 2))" >> $GITHUB_ENV
|
|
fi
|
|
|
|
- uses: actions/checkout@v6
|
|
with:
|
|
fetch-depth: 1
|
|
|
|
- name: Setup Git
|
|
run: |
|
|
git config --global user.email "ci@ci.invalid"
|
|
git config --global user.name "Discourse CI"
|
|
|
|
- name: Start redis
|
|
run: redis-server /etc/redis/redis.conf &
|
|
|
|
- name: Start Postgres
|
|
run: |
|
|
chown -R postgres /var/run/postgresql
|
|
sudo -E -u postgres script/start_test_db.rb
|
|
sudo -u postgres psql -c "CREATE ROLE $PGUSER LOGIN SUPERUSER PASSWORD '$PGPASSWORD';"
|
|
|
|
- name: Symlink vendor/bundle from image
|
|
run: |
|
|
ln -s /var/www/discourse/vendor/bundle vendor/bundle
|
|
|
|
- name: Setup gems
|
|
run: |
|
|
bundle config --local path vendor/bundle
|
|
bundle config --local deployment true
|
|
bundle config --local without development
|
|
bundle install --jobs $(($(nproc) - 1))
|
|
bundle clean
|
|
|
|
- name: pnpm install
|
|
run: pnpm install --frozen-lockfile
|
|
|
|
- name: Checkout official plugins
|
|
if: matrix.target == 'official-plugins' || matrix.target == 'plugins'
|
|
run: bin/rake plugin:install_all_official
|
|
|
|
- name: Symlinking plugin gems from image
|
|
if: matrix.target == 'plugins' || matrix.target == 'core-plugins' || matrix.target == 'official-plugins'
|
|
run: |
|
|
for dir in /var/www/discourse/plugins/*/gems; do
|
|
plugin_name=$(basename "$(dirname "$dir")")
|
|
plugin_dir="plugins/$plugin_name"
|
|
gem_dir="$plugin_dir/gems"
|
|
|
|
if [ ! -d "$plugin_dir" ]; then
|
|
echo "Skipping $plugin_name: Plugin directory does not exist"
|
|
elif [ -d "$gem_dir" ]; then
|
|
echo "Skipping $plugin_name: Source gems directory exists"
|
|
else
|
|
echo "Symlinking $dir to $gem_dir"
|
|
ln -s "$dir" "$gem_dir"
|
|
fi
|
|
done
|
|
|
|
- name: Pull compatible versions of plugins
|
|
if: (matrix.target == 'official-plugins' || matrix.target == 'plugins') && (github.ref_name != 'main' && github.base_ref != 'main')
|
|
run: LOAD_PLUGINS=0 bin/rake plugin:pull_compatible_all
|
|
|
|
- name: Checkout official themes
|
|
if: matrix.target == 'themes'
|
|
run: bin/rake themes:clone_all_official themes:pull_compatible_all
|
|
|
|
- name: Add hosts to /etc/hosts, otherwise Chrome cannot reach minio
|
|
if: matrix.build_type == 'system' && matrix.target == 'core'
|
|
run: |
|
|
echo "127.0.0.1 minio.local" | sudo tee -a /etc/hosts
|
|
echo "127.0.0.1 discoursetest.minio.local" | sudo tee -a /etc/hosts
|
|
|
|
- name: Create and migrate database
|
|
run: |
|
|
bin/rake db:create
|
|
bin/rake db:migrate
|
|
|
|
- name: Create and migrate parallel databases
|
|
if: env.USES_PARALLEL_DATABASES == 'true'
|
|
run: |
|
|
bin/rake parallel:create
|
|
bin/rake parallel:migrate
|
|
|
|
- name: Fetch turbo_rspec_runtime.log cache
|
|
uses: actions/cache@v5
|
|
id: test-runtime-cache
|
|
if: matrix.build_type == 'backend' || matrix.build_type == 'system'
|
|
with:
|
|
path: tmp/turbo_rspec_runtime.log
|
|
key: rspec-runtime-${{ matrix.build_type }}-${{ matrix.target }}-${{ github.run_id }}
|
|
restore-keys: rspec-runtime-${{ matrix.build_type }}-${{ matrix.target }}-
|
|
|
|
- name: Check Annotations
|
|
if: matrix.build_type == 'backend' && (matrix.target == 'core' || matrix.target == 'plugins')
|
|
env:
|
|
LOAD_PLUGINS: ""
|
|
run: |
|
|
directories=""
|
|
if [ "${{ matrix.target }}" = "core" ]; then
|
|
directories="app/models"
|
|
else
|
|
directories="$(script/list_bundled_plugins | xargs -I{} find {} -type d -path '*/app/models' -maxdepth 3)"
|
|
fi
|
|
|
|
# annotate:clean runs the seed + annotaterb in a temporary postgres so it
|
|
# doesn't pollute the rspec test DB.
|
|
MODEL_DIR="$(echo "${directories}" | tr '\n' ',')" bin/rake annotate:clean
|
|
|
|
if [ ! -z "$(git status --porcelain ${directories})" ]; then
|
|
echo "Core annotations are not up to date. To resolve, run:"
|
|
echo " bin/rake annotate:clean"
|
|
echo
|
|
echo "Or manually apply the diff printed below:"
|
|
echo "---------------------------------------------"
|
|
git -c color.ui=always diff ${directories}
|
|
exit 1
|
|
fi
|
|
|
|
- name: Check structure dump
|
|
if: matrix.build_type == 'backend' && matrix.target == 'core'
|
|
run: bin/rake db:check_structure_dump
|
|
|
|
- name: Check Zeitwerk reloading
|
|
if: matrix.build_type == 'backend'
|
|
run: |
|
|
if ! bin/rails runner 'Rails.application.reloader.reload!'; then
|
|
echo
|
|
echo "---------------------------------------------"
|
|
echo
|
|
echo "::error::Zeitwerk reload failed - the app will not be able to reload properly in development."
|
|
echo "To reproduce locally, run \`bin/rails runner 'Rails.application.reloader.reload!'\`."
|
|
echo
|
|
exit 1
|
|
fi
|
|
|
|
- name: Check SKIP_DB_AND_REDIS bootability
|
|
if: matrix.build_type == 'backend'
|
|
run: |
|
|
if ! (SKIP_DB_AND_REDIS=1 RAILS_DB="nonexistent" bin/rails runner "puts 'booted successfully'"); then
|
|
echo
|
|
echo "---------------------------------------------"
|
|
echo
|
|
echo "::error::SKIP_DB_AND_REDIS boot failed. Make sure the database is not being accessed during the Rails boot process."
|
|
echo "To reproduce locally, run \`SKIP_DB_AND_REDIS=1 RAILS_DB='nonexistent' bin/rails runner \"puts 'booted successfully'\"\`."
|
|
echo
|
|
exit 1
|
|
fi
|
|
|
|
- name: Core RSpec
|
|
if: matrix.build_type == 'backend' && matrix.target == 'core'
|
|
run: bin/turbo_rspec --use-runtime-info --format=progress
|
|
|
|
- name: Ember Build
|
|
if: matrix.build_type == 'system' || matrix.build_type == 'frontend'
|
|
run: script/assemble_ember_build.rb
|
|
|
|
- name: Prepare Chrome environment for QUnit
|
|
if: matrix.build_type == 'frontend'
|
|
run: |
|
|
mkdir -p "$HOME/.pki/nssdb" "$HOME/.local/share/pki/nssdb"
|
|
dbus-daemon --system --fork 2>/dev/null || true
|
|
bus_addr=$(dbus-daemon --session --fork --print-address 2>/dev/null)
|
|
echo "DBUS_SESSION_BUS_ADDRESS=$bus_addr" >> "$GITHUB_ENV"
|
|
|
|
- name: Core QUnit
|
|
if: matrix.build_type == 'frontend' && matrix.target == 'core'
|
|
run: QUNIT_WRITE_EXECUTION_FILE=1 bin/rake qunit:test
|
|
|
|
- name: Asset Processor Tests
|
|
if: matrix.build_type == 'frontend' && matrix.target == 'core'
|
|
working-directory: frontend/asset-processor
|
|
run: pnpm test
|
|
|
|
- name: Plugins RSpec
|
|
if: matrix.build_type == 'backend' && matrix.target == 'plugins'
|
|
run: |
|
|
bin/rake plugin:turbo_spec['*','--format=progress --use-runtime-info']
|
|
|
|
- name: Plugins QUnit
|
|
if: matrix.build_type == 'frontend' && matrix.target == 'plugins'
|
|
run: bin/rake 'plugin:qunit[*]'
|
|
|
|
- name: Theme QUnit
|
|
if: matrix.build_type == 'frontend' && matrix.target == 'themes'
|
|
run: DISCOURSE_DEV_DB=discourse_test bin/rake themes:qunit_all_official
|
|
|
|
- uses: actions/upload-artifact@v7
|
|
if: always() && matrix.build_type == 'frontend' && matrix.target == 'core'
|
|
with:
|
|
name: ember-exam-execution-${{ matrix.target }}-${{ matrix.browser }}-frontend-${{ hashFiles('./frontend/discourse/test-execution-*.json') }}
|
|
path: ./frontend/discourse/test-execution-*.json
|
|
|
|
- name: Install playwright
|
|
if: matrix.build_type == 'system'
|
|
run: pnpm playwright install --with-deps --no-shell chromium
|
|
|
|
- name: Core System Tests
|
|
if: matrix.build_type == 'system' && matrix.target == 'core'
|
|
env:
|
|
CHECKOUT_TIMEOUT: 10
|
|
run: RAILS_ENABLE_TEST_LOG=1 RAILS_TEST_LOG_LEVEL=error bin/turbo_rspec --use-runtime-info --format documentation spec/system
|
|
|
|
- name: Core Plugins System Tests
|
|
if: matrix.build_type == 'system' && matrix.target == 'core-plugins'
|
|
env:
|
|
CHECKOUT_TIMEOUT: 10
|
|
run: |
|
|
RAILS_ENABLE_TEST_LOG=1 RAILS_TEST_LOG_LEVEL=error bin/turbo_rspec --exclude-pattern="plugins/chat/*" --use-runtime-info --format documentation plugins/*/spec/system
|
|
|
|
- name: Official Plugins System Tests
|
|
if: matrix.build_type == 'system' && matrix.target == 'official-plugins'
|
|
env:
|
|
CHECKOUT_TIMEOUT: 10
|
|
run: |
|
|
RAILS_ENABLE_TEST_LOG=1 RAILS_TEST_LOG_LEVEL=error bin/turbo_rspec --exclude-pattern="$(script/list_bundled_plugins '/*' | paste -sd, -)" --use-runtime-info --format documentation plugins/*/spec/system
|
|
|
|
- name: Chat System Tests
|
|
if: matrix.build_type == 'system' && matrix.target == 'chat'
|
|
env:
|
|
CHECKOUT_TIMEOUT: 10
|
|
run: RAILS_ENABLE_TEST_LOG=1 RAILS_TEST_LOG_LEVEL=error bin/turbo_rspec --use-runtime-info --format documentation plugins/chat/spec/system
|
|
|
|
- name: Theme System Tests
|
|
if: matrix.build_type == 'system' && matrix.target == 'themes'
|
|
env:
|
|
CHECKOUT_TIMEOUT: 10
|
|
run: RAILS_ENABLE_TEST_LOG=1 RAILS_TEST_LOG_LEVEL=error bin/turbo_rspec --format documentation tmp/themes/*/spec/system themes/*/spec/system
|
|
|
|
- name: Upload failed system test artifacts
|
|
uses: actions/upload-artifact@v7
|
|
if: always() && matrix.build_type == 'system'
|
|
with:
|
|
name: failed-system-test-artifacts-${{ matrix.build_type }}-${{ matrix.target }}
|
|
if-no-files-found: ignore
|
|
path: |
|
|
tmp/capybara/*.png
|
|
tmp/capybara/*.zip
|
|
tmp/capybara/*.webm
|
|
|
|
- name: Check for flaky tests report
|
|
id: check-flaky-spec-report
|
|
if: github.repository == 'discourse/discourse' && github.ref_name == 'main' && env.DISCOURSE_TURBO_RSPEC_RETRY_AND_LOG_FLAKY_TESTS == '1'
|
|
run: |
|
|
if [ -f tmp/turbo_rspec_flaky_tests.json ]; then
|
|
echo "exists=true" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "exists=false" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
- name: Create flaky tests report artifact
|
|
if: steps.check-flaky-spec-report.outputs.exists == 'true'
|
|
run: cp tmp/turbo_rspec_flaky_tests.json tmp/turbo_rspec_flaky_tests-${{ matrix.build_type }}-${{ matrix.target }}-${{ job.check_run_id }}.json
|
|
|
|
- name: Upload flaky tests report
|
|
uses: actions/upload-artifact@v7
|
|
if: steps.check-flaky-spec-report.outputs.exists == 'true'
|
|
with:
|
|
name: flaky-test-reports-${{ matrix.build_type }}-${{ matrix.target }}
|
|
path: tmp/turbo_rspec_flaky_tests-${{ matrix.build_type }}-${{ matrix.target }}-${{ job.check_run_id }}.json
|
|
|
|
merge:
|
|
if: github.repository == 'discourse/discourse' && github.ref == 'refs/heads/main'
|
|
runs-on: ubuntu-latest
|
|
needs: build
|
|
steps:
|
|
- name: Check for artifacts to merge
|
|
id: check-artifacts
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
names=$(gh api "repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts?per_page=100" --jq '.artifacts[].name')
|
|
if echo "$names" | grep -q '^failed-system-test-artifacts-'; then
|
|
echo "has_failed_system=true" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
if echo "$names" | grep -q '^flaky-test-reports-'; then
|
|
echo "has_flaky=true" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
- name: Merge failed system test artifacts
|
|
if: steps.check-artifacts.outputs.has_failed_system == 'true'
|
|
uses: actions/upload-artifact/merge@v7
|
|
with:
|
|
name: failed-system-test-artifacts
|
|
pattern: failed-system-test-artifacts-*
|
|
delete-merged: true
|
|
|
|
- name: Merge flaky test reports
|
|
if: steps.check-artifacts.outputs.has_flaky == 'true'
|
|
uses: actions/upload-artifact/merge@v7
|
|
with:
|
|
name: flaky-test-reports
|
|
pattern: flaky-test-reports-*
|
|
delete-merged: true
|