diff --git a/.github/workflows/pull-db-tests.yml b/.github/workflows/pull-db-tests.yml index 358f8b8eac..f21cdf8135 100644 --- a/.github/workflows/pull-db-tests.yml +++ b/.github/workflows/pull-db-tests.yml @@ -14,10 +14,14 @@ jobs: files-changed: uses: ./.github/workflows/files-changed.yml - test-pgsql: + test-pgsql-shards: if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true' needs: files-changed runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shard: [1, 2] services: pgsql: image: postgres:14 @@ -57,6 +61,7 @@ jobs: env: TAGS: bindata - name: run migration tests + if: matrix.shard == 1 run: GITEA_TEST_DATABASE=pgsql make test-migration - name: run tests run: GITEA_TEST_DATABASE=pgsql make test-integration @@ -66,11 +71,24 @@ jobs: GOTEST_FLAGS: -race -timeout=40m TAGS: bindata gogit TEST_LDAP: 1 + TEST_SHARD: ${{ matrix.shard }} + TEST_TOTAL_SHARDS: 2 - test-sqlite: + test-pgsql: + needs: [files-changed, test-pgsql-shards] + if: always() && (needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true') + runs-on: ubuntu-latest + steps: + - run: '[ "${{ needs.test-pgsql-shards.result }}" = "success" ]' + + test-sqlite-shards: if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true' needs: files-changed runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shard: [1, 2] steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -87,6 +105,7 @@ jobs: TAGS: bindata gogit GOEXPERIMENT: - name: run migration tests + if: matrix.shard == 1 run: GITEA_TEST_DATABASE=sqlite make test-migration env: TAGS: bindata gogit @@ -98,6 +117,15 @@ jobs: GOTEST_FLAGS: -timeout=40m TAGS: bindata gogit GOEXPERIMENT: + TEST_SHARD: ${{ matrix.shard }} + TEST_TOTAL_SHARDS: 2 + + test-sqlite: + needs: [files-changed, test-sqlite-shards] + if: always() && (needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true') + runs-on: ubuntu-latest + steps: + - run: '[ "${{ needs.test-sqlite-shards.result }}" = "success" ]' test-unit: if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true' @@ -168,10 +196,14 @@ jobs: GOEXPERIMENT: GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }} - test-mysql: + test-mysql-shards: if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true' needs: files-changed runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shard: [1, 2] services: mysql: # the bitnami mysql image has more options than the official one, it's easier to customize @@ -214,17 +246,31 @@ jobs: env: TAGS: bindata - name: run migration tests + if: matrix.shard == 1 run: GITEA_TEST_DATABASE=mysql make test-migration - name: run tests run: GITEA_TEST_DATABASE=mysql make test-integration env: TAGS: bindata TEST_INDEXER_CODE_ES_URL: "http://elastic:changeme@elasticsearch:9200" + TEST_SHARD: ${{ matrix.shard }} + TEST_TOTAL_SHARDS: 2 - test-mssql: + test-mysql: + needs: [files-changed, test-mysql-shards] + if: always() && (needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true') + runs-on: ubuntu-latest + steps: + - run: '[ "${{ needs.test-mysql-shards.result }}" = "success" ]' + + test-mssql-shards: if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true' needs: files-changed runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shard: [1, 2] services: mssql: image: mcr.microsoft.com/mssql/server:2019-latest @@ -254,9 +300,19 @@ jobs: - run: make backend env: TAGS: bindata - - run: GITEA_TEST_DATABASE=mssql make test-migration + - if: matrix.shard == 1 + run: GITEA_TEST_DATABASE=mssql make test-migration - name: run tests run: GITEA_TEST_DATABASE=mssql make test-integration timeout-minutes: 50 env: TAGS: bindata + TEST_SHARD: ${{ matrix.shard }} + TEST_TOTAL_SHARDS: 2 + + test-mssql: + needs: [files-changed, test-mssql-shards] + if: always() && (needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true') + runs-on: ubuntu-latest + steps: + - run: '[ "${{ needs.test-mssql-shards.result }}" = "success" ]' diff --git a/Makefile b/Makefile index e16a31399f..7f06ef39ff 100644 --- a/Makefile +++ b/Makefile @@ -450,7 +450,11 @@ test-integration: @# would flood output per passing test. testcache can't help these tests anyway — @# they mutate the work directory, so cache inputs change between runs. $(GO) test $(GOTEST_FLAGS) -tags '$(TAGS)' -c code.gitea.io/gitea/tests/integration -o ./test-integration-$(GITEA_TEST_DATABASE).test +ifdef TEST_SHARD + ./tools/test-integration-shard.sh ./test-integration-$(GITEA_TEST_DATABASE).test +else ./test-integration-$(GITEA_TEST_DATABASE).test +endif .PHONY: test-integration\#% test-integration\#%: diff --git a/tools/test-integration-shard.sh b/tools/test-integration-shard.sh new file mode 100755 index 0000000000..1f72dd5094 --- /dev/null +++ b/tools/test-integration-shard.sh @@ -0,0 +1,25 @@ +#!/bin/bash +set -euo pipefail + +# Run a deterministic shard of the integration test binary. Test names are +# enumerated from source — running the binary with -test.list isn't viable +# because TestMain boots the full Gitea environment and would panic without +# a configured database. + +binary=$1 +shard=${TEST_SHARD:?missing TEST_SHARD} +total=${TEST_TOTAL_SHARDS:?missing TEST_TOTAL_SHARDS} + +names=$(grep -hE '^func Test[A-Z][A-Za-z0-9_]*\(' tests/integration/*.go \ + | sed -E 's/^func (Test[A-Z][A-Za-z0-9_]*).*/\1/' \ + | sort -u \ + | awk -v s="$shard" -v t="$total" 'NR % t == (s - 1) % t') + +if [ -z "$names" ]; then + echo "shard $shard/$total has no tests assigned" >&2 + exit 0 +fi + +pattern=$(echo "$names" | paste -sd '|' -) +echo "Running shard $shard/$total ($(echo "$names" | wc -l | tr -d ' ') tests)" +exec "$binary" -test.run "^($pattern)\$"