1name: Tests
2
3on:
4  push:
5    branches: ["develop", "release-*"]
6  pull_request:
7
8concurrency:
9  group: ${{ github.workflow }}-${{ github.ref }}
10  cancel-in-progress: true
11
12jobs:
13  lint:
14    runs-on: ubuntu-latest
15    strategy:
16      matrix:
17        toxenv:
18          - "check-sampleconfig"
19          - "check_codestyle"
20          - "check_isort"
21          - "mypy"
22          - "packaging"
23
24    steps:
25      - uses: actions/checkout@v2
26      - uses: actions/setup-python@v2
27      - run: pip install tox
28      - run: tox -e ${{ matrix.toxenv }}
29
30  lint-crlf:
31    runs-on: ubuntu-latest
32    steps:
33      - uses: actions/checkout@v2
34      - name: Check line endings
35        run: scripts-dev/check_line_terminators.sh
36
37  lint-newsfile:
38    if: ${{ github.base_ref == 'develop'  || contains(github.base_ref, 'release-') }}
39    runs-on: ubuntu-latest
40    steps:
41      - uses: actions/checkout@v2
42        with:
43          ref: ${{ github.event.pull_request.head.sha }}
44          fetch-depth: 0
45      - uses: actions/setup-python@v2
46      - run: pip install tox
47      - run: scripts-dev/check-newsfragment
48        env:
49          PULL_REQUEST_NUMBER: ${{ github.event.number }}
50
51  lint-sdist:
52    runs-on: ubuntu-latest
53    steps:
54      - uses: actions/checkout@v2
55      - uses: actions/setup-python@v2
56        with:
57          python-version: "3.x"
58      - run: pip install wheel
59      - run: python setup.py sdist bdist_wheel
60      - uses: actions/upload-artifact@v2
61        with:
62          name: Python Distributions
63          path: dist/*
64
65  # Dummy step to gate other tests on without repeating the whole list
66  linting-done:
67    if: ${{ !cancelled() }} # Run this even if prior jobs were skipped
68    needs: [lint, lint-crlf, lint-newsfile, lint-sdist]
69    runs-on: ubuntu-latest
70    steps:
71      - run: "true"
72
73  trial:
74    if: ${{ !cancelled() && !failure() }} # Allow previous steps to be skipped, but not fail
75    needs: linting-done
76    runs-on: ubuntu-latest
77    strategy:
78      matrix:
79        python-version: ["3.7", "3.8", "3.9", "3.10"]
80        database: ["sqlite"]
81        toxenv: ["py"]
82        include:
83          # Newest Python without optional deps
84          - python-version: "3.10"
85            toxenv: "py-noextras"
86
87          # Oldest Python with PostgreSQL
88          - python-version: "3.7"
89            database: "postgres"
90            postgres-version: "10"
91            toxenv: "py"
92
93          # Newest Python with newest PostgreSQL
94          - python-version: "3.10"
95            database: "postgres"
96            postgres-version: "14"
97            toxenv: "py"
98
99    steps:
100      - uses: actions/checkout@v2
101      - run: sudo apt-get -qq install xmlsec1
102      - name: Set up PostgreSQL ${{ matrix.postgres-version }}
103        if: ${{ matrix.postgres-version }}
104        run: |
105          docker run -d -p 5432:5432 \
106            -e POSTGRES_PASSWORD=postgres \
107            -e POSTGRES_INITDB_ARGS="--lc-collate C --lc-ctype C --encoding UTF8" \
108            postgres:${{ matrix.postgres-version }}
109      - uses: actions/setup-python@v2
110        with:
111          python-version: ${{ matrix.python-version }}
112      - run: pip install tox
113      - name: Await PostgreSQL
114        if: ${{ matrix.postgres-version }}
115        timeout-minutes: 2
116        run: until pg_isready -h localhost; do sleep 1; done
117      - run: tox -e ${{ matrix.toxenv }}
118        env:
119          TRIAL_FLAGS: "--jobs=2"
120          SYNAPSE_POSTGRES: ${{ matrix.database == 'postgres' || '' }}
121          SYNAPSE_POSTGRES_HOST: localhost
122          SYNAPSE_POSTGRES_USER: postgres
123          SYNAPSE_POSTGRES_PASSWORD: postgres
124      - name: Dump logs
125        # Logs are most useful when the command fails, always include them.
126        if: ${{ always() }}
127        # Note: Dumps to workflow logs instead of using actions/upload-artifact
128        #       This keeps logs colocated with failing jobs
129        #       It also ignores find's exit code; this is a best effort affair
130        run: >-
131          find _trial_temp -name '*.log'
132          -exec echo "::group::{}" \;
133          -exec cat {} \;
134          -exec echo "::endgroup::" \;
135          || true
136
137  trial-olddeps:
138    if: ${{ !cancelled() && !failure() }} # Allow previous steps to be skipped, but not fail
139    needs: linting-done
140    runs-on: ubuntu-latest
141    steps:
142      - uses: actions/checkout@v2
143      - name: Test with old deps
144        uses: docker://ubuntu:bionic # For old python and sqlite
145        with:
146          workdir: /github/workspace
147          entrypoint: .ci/scripts/test_old_deps.sh
148        env:
149          TRIAL_FLAGS: "--jobs=2"
150      - name: Dump logs
151        # Logs are most useful when the command fails, always include them.
152        if: ${{ always() }}
153        # Note: Dumps to workflow logs instead of using actions/upload-artifact
154        #       This keeps logs colocated with failing jobs
155        #       It also ignores find's exit code; this is a best effort affair
156        run: >-
157          find _trial_temp -name '*.log'
158          -exec echo "::group::{}" \;
159          -exec cat {} \;
160          -exec echo "::endgroup::" \;
161          || true
162
163  trial-pypy:
164    # Very slow; only run if the branch name includes 'pypy'
165    if: ${{ contains(github.ref, 'pypy') && !failure() && !cancelled() }}
166    needs: linting-done
167    runs-on: ubuntu-latest
168    strategy:
169      matrix:
170        python-version: ["pypy-3.7"]
171
172    steps:
173      - uses: actions/checkout@v2
174      - run: sudo apt-get -qq install xmlsec1 libxml2-dev libxslt-dev
175      - uses: actions/setup-python@v2
176        with:
177          python-version: ${{ matrix.python-version }}
178      - run: pip install tox
179      - run: tox -e py
180        env:
181          TRIAL_FLAGS: "--jobs=2"
182      - name: Dump logs
183        # Logs are most useful when the command fails, always include them.
184        if: ${{ always() }}
185        # Note: Dumps to workflow logs instead of using actions/upload-artifact
186        #       This keeps logs colocated with failing jobs
187        #       It also ignores find's exit code; this is a best effort affair
188        run: >-
189          find _trial_temp -name '*.log'
190          -exec echo "::group::{}" \;
191          -exec cat {} \;
192          -exec echo "::endgroup::" \;
193          || true
194
195  sytest:
196    if: ${{ !failure() && !cancelled() }}
197    needs: linting-done
198    runs-on: ubuntu-latest
199    container:
200      image: matrixdotorg/sytest-synapse:${{ matrix.sytest-tag }}
201      volumes:
202        - ${{ github.workspace }}:/src
203      env:
204        SYTEST_BRANCH: ${{ github.head_ref }}
205        POSTGRES: ${{ matrix.postgres && 1}}
206        MULTI_POSTGRES: ${{ (matrix.postgres == 'multi-postgres') && 1}}
207        WORKERS: ${{ matrix.workers && 1 }}
208        REDIS: ${{ matrix.redis && 1 }}
209        BLACKLIST: ${{ matrix.workers && 'synapse-blacklist-with-workers' }}
210        TOP: ${{ github.workspace }}
211
212    strategy:
213      fail-fast: false
214      matrix:
215        include:
216          - sytest-tag: bionic
217
218          - sytest-tag: bionic
219            postgres: postgres
220
221          - sytest-tag: testing
222            postgres: postgres
223
224          - sytest-tag: bionic
225            postgres: multi-postgres
226            workers: workers
227
228          - sytest-tag: buster
229            postgres: multi-postgres
230            workers: workers
231
232          - sytest-tag: buster
233            postgres: postgres
234            workers: workers
235            redis: redis
236
237    steps:
238      - uses: actions/checkout@v2
239      - name: Prepare test blacklist
240        run: cat sytest-blacklist .ci/worker-blacklist > synapse-blacklist-with-workers
241      - name: Run SyTest
242        run: /bootstrap.sh synapse
243        working-directory: /src
244      - name: Summarise results.tap
245        if: ${{ always() }}
246        run: /sytest/scripts/tap_to_gha.pl /logs/results.tap
247      - name: Upload SyTest logs
248        uses: actions/upload-artifact@v2
249        if: ${{ always() }}
250        with:
251          name: Sytest Logs - ${{ job.status }} - (${{ join(matrix.*, ', ') }})
252          path: |
253            /logs/results.tap
254            /logs/**/*.log*
255
256  export-data:
257    if: ${{ !failure() && !cancelled() }} # Allow previous steps to be skipped, but not fail
258    needs: [linting-done, portdb]
259    runs-on: ubuntu-latest
260    env:
261      TOP: ${{ github.workspace }}
262
263    services:
264      postgres:
265        image: postgres
266        ports:
267          - 5432:5432
268        env:
269          POSTGRES_PASSWORD: "postgres"
270          POSTGRES_INITDB_ARGS: "--lc-collate C --lc-ctype C --encoding UTF8"
271        options: >-
272          --health-cmd pg_isready
273          --health-interval 10s
274          --health-timeout 5s
275          --health-retries 5
276
277    steps:
278      - uses: actions/checkout@v2
279      - run: sudo apt-get -qq install xmlsec1
280      - uses: actions/setup-python@v2
281        with:
282          python-version: "3.9"
283      - run: .ci/scripts/test_export_data_command.sh
284
285  portdb:
286    if: ${{ !failure() && !cancelled() }} # Allow previous steps to be skipped, but not fail
287    needs: linting-done
288    runs-on: ubuntu-latest
289    env:
290      TOP: ${{ github.workspace }}
291    strategy:
292      matrix:
293        include:
294          - python-version: "3.7"
295            postgres-version: "10"
296
297          - python-version: "3.10"
298            postgres-version: "14"
299
300    services:
301      postgres:
302        image: postgres:${{ matrix.postgres-version }}
303        ports:
304          - 5432:5432
305        env:
306          POSTGRES_PASSWORD: "postgres"
307          POSTGRES_INITDB_ARGS: "--lc-collate C --lc-ctype C --encoding UTF8"
308        options: >-
309          --health-cmd pg_isready
310          --health-interval 10s
311          --health-timeout 5s
312          --health-retries 5
313
314    steps:
315      - uses: actions/checkout@v2
316      - run: sudo apt-get -qq install xmlsec1
317      - uses: actions/setup-python@v2
318        with:
319          python-version: ${{ matrix.python-version }}
320      - run: .ci/scripts/test_synapse_port_db.sh
321
322  complement:
323    if: ${{ !failure() && !cancelled() }}
324    needs: linting-done
325    runs-on: ubuntu-latest
326    container:
327      # https://github.com/matrix-org/complement/blob/master/dockerfiles/ComplementCIBuildkite.Dockerfile
328      image: matrixdotorg/complement:latest
329      env:
330        CI: true
331      ports:
332        - 8448:8448
333      volumes:
334        - /var/run/docker.sock:/var/run/docker.sock
335
336    steps:
337      - name: Run actions/checkout@v2 for synapse
338        uses: actions/checkout@v2
339        with:
340          path: synapse
341
342      # Attempt to check out the same branch of Complement as the PR. If it
343      # doesn't exist, fallback to master.
344      - name: Checkout complement
345        shell: bash
346        run: |
347          mkdir -p complement
348          # Attempt to use the version of complement which best matches the current
349          # build. Depending on whether this is a PR or release, etc. we need to
350          # use different fallbacks.
351          #
352          # 1. First check if there's a similarly named branch (GITHUB_HEAD_REF
353          #    for pull requests, otherwise GITHUB_REF).
354          # 2. Attempt to use the base branch, e.g. when merging into release-vX.Y
355          #    (GITHUB_BASE_REF for pull requests).
356          # 3. Use the default complement branch ("master").
357          for BRANCH_NAME in "$GITHUB_HEAD_REF" "$GITHUB_BASE_REF" "${GITHUB_REF#refs/heads/}" "master"; do
358            # Skip empty branch names and merge commits.
359            if [[ -z "$BRANCH_NAME" || $BRANCH_NAME =~ ^refs/pull/.* ]]; then
360              continue
361            fi
362
363            (wget -O - "https://github.com/matrix-org/complement/archive/$BRANCH_NAME.tar.gz" | tar -xz --strip-components=1 -C complement) && break
364          done
365
366      # Build initial Synapse image
367      - run: docker build -t matrixdotorg/synapse:latest -f docker/Dockerfile .
368        working-directory: synapse
369
370      # Build a ready-to-run Synapse image based on the initial image above.
371      # This new image includes a config file, keys for signing and TLS, and
372      # other settings to make it suitable for testing under Complement.
373      - run: docker build -t complement-synapse -f Synapse.Dockerfile .
374        working-directory: complement/dockerfiles
375
376      # Run Complement
377      - run: go test -v -tags synapse_blacklist,msc2403 ./tests/...
378        env:
379          COMPLEMENT_BASE_IMAGE: complement-synapse:latest
380        working-directory: complement
381
382  # a job which marks all the other jobs as complete, thus allowing PRs to be merged.
383  tests-done:
384    if: ${{ always() }}
385    needs:
386      - lint
387      - lint-crlf
388      - lint-newsfile
389      - lint-sdist
390      - trial
391      - trial-olddeps
392      - sytest
393      - portdb
394      - complement
395    runs-on: ubuntu-latest
396    steps:
397      - name: Set build result
398        env:
399          NEEDS_CONTEXT: ${{ toJSON(needs) }}
400        # the `jq` incantation dumps out a series of "<job> <result>" lines.
401        # we set it to an intermediate variable to avoid a pipe, which makes it
402        # hard to set $rc.
403        run: |
404          rc=0
405          results=$(jq -r 'to_entries[] | [.key,.value.result] | join(" ")' <<< $NEEDS_CONTEXT)
406          while read job result ; do
407              # The newsfile lint may be skipped on non PR builds
408              if [ $result == "skipped" ] && [ $job == "lint-newsfile" ]; then
409                continue
410              fi
411
412              if [ "$result" != "success" ]; then
413                  echo "::set-failed ::Job $job returned $result"
414                  rc=1
415              fi
416          done <<< $results
417          exit $rc
418