Skip to content

Commit

Permalink
(PC-34429)[API] ci: use cached pytest duration file
Browse files Browse the repository at this point in the history
  • Loading branch information
fseguin-pass committed Feb 11, 2025
1 parent 9284772 commit d08aa7c
Showing 1 changed file with 124 additions and 1 deletion.
125 changes: 124 additions & 1 deletion .github/workflows/dev_on_workflow_tests_api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,53 @@ jobs:
env:
SLACK_BOT_TOKEN: ${{ steps.secrets.outputs.SLACK_BOT_TOKEN }}

restore-test-durations:
name: Restore pytest durations
runs-on: ubuntu-22.04
steps:
# # http://man7.org/linux/man-pages/man1/date.1.html
# - name: Get Date
# id: get-date
# run: |
# echo "date=$(/bin/date -u "+%Y%m%d")" >> $GITHUB_OUTPUT
# shell: bash
# Then we'll use tests-durations-${{ steps.get-date.outputs.date }}

# It's mandatory to use the exact same path when saving/restoring cache, otherwise it won't work
# (the same key is not enough - see documentation:
# https://github.com/actions/cache/blob/main/README.md#cache-version).
# I went with `/tests/.test_durations`.
- name: Restore test durations
id: restore-test-durations
uses: actions/cache/restore@v4
with:
path: /tests/.test_durations
key: tests-durations-${{ github.sha }}
restore-keys: |
tests-durations-${{ github.sha }}-
tests-durations-
fail-on-cache-miss: false

# Then we upload the restored test durations as an artifact. This way, each matrix job will download
# it when it starts. When a matrix job will be manually retried, it will also reuse the artifact (to
# retry the exact same tests, even if the cache has been updated in the meantime).
- name: Upload test durations
if: steps.restore-test-durations.outputs.cache-hit != ''
uses: actions/upload-artifact@v4
with:
name: test-durations-before
path: /tests/.test_durations

outputs:
# This output will be used to know if we had a cache hit (exact match or not), or no cache hit at all.
# See documentation about the `cache-hit` output:
# https://github.com/actions/cache/blob/main/README.md#outputs
# > cache-hit - A string value to indicate an exact match was found for the key.
# > If there's a cache hit, this will be 'true' or 'false' to indicate if there's an exact match
# > for key.
# > If there's a cache miss, this will be an empty string.
restored: ${{ steps.restore-test-durations.outputs.cache-hit == '' && 'false' || 'true' }}

pytest:
name: "Pytest ${{ matrix.pytest_args.description }} : ${{ matrix.group }}/4"
env:
Expand Down Expand Up @@ -490,6 +537,7 @@ jobs:
alembic upgrade pre@head
alembic upgrade post@head
flask install_data
- name: "Mount a Volume with pcapi rights"
uses: addnab/docker-run-action@v3
with:
Expand All @@ -499,14 +547,48 @@ jobs:
run: |
echo "Changing owner and group fort directory test"
chown -R pcapi:pcapi /tests
# These two steps will be executed only when there IS a cache hit (exact match or not). When a matrix
# job is retried, it will reuse the same artifact, to execute the exact same split.
- name: Download test durations
if: needs.restore-test-durations.outputs.restored == 'true'
uses: actions/download-artifact@v4
with:
name: test-durations-before

- name: Use cached test durations
if: needs.restore-test-durations.outputs.restored == 'true'
run: mv .test_durations ${{ runner.workspace }}/pass-culture-main/api/tests/.test_durations

# When running pytest, we write the new test durations using options
# `--store-durations --clean-durations`.
# Option `--clean-durations` is undocumented but you can check its implementation here:
# https://github.com/jerry-git/pytest-split/blob/fb9af7e0122c18a96a7c01ca734c4ab01027f8d9/src/pytest_split/plugin.py#L68-L76
# > Removes the test duration info for tests which are not present while running the suite with
# > '--store-durations'.
- name: "Run pytest"
uses: addnab/docker-run-action@v3
with:
image: ${{ steps.compute-image-name.outputs.image_name }}
shell: bash
options: -e RUN_ENV -e DATABASE_URL_TEST -e REDIS_URL -e SQLALCHEMY_WARN_20 -v ${{ runner.workspace }}/pass-culture-main/api/tests/:/tests
run: |
pytest ${{ matrix.pytest_args.collection }} --splits 4 --group ${{ matrix.group }} --durations=10 --junitxml='/tests/junit.xml' -q --color=yes
pytest ${{ matrix.pytest_args.collection }} \
--splits 4 --group ${{ matrix.group }} \
--store-durations --durations-path /tests/.test_durations --clean-durations \
--durations=10 --junitxml='/tests/junit.xml' \
-q --color=yes
- name: Upload test durations
# if: github.run_attempt == 1
if: always()
uses: actions/upload-artifact@v4
with:
name: "test-durations-after-partial-${{ matrix.pytest_args.description }}-${{ matrix.group }}"
path: "${{ runner.workspace }}/pass-culture-main/api/tests/.test_durations"
if-no-files-found: warn
include-hidden-files: true

- name: "Publish Test Report"
uses: mikepenz/action-junit-report@v5
if: always() # always run even if the previous step fails
Expand Down Expand Up @@ -540,3 +622,44 @@ jobs:
}
env:
SLACK_BOT_TOKEN: ${{ steps.secrets.outputs.SLACK_BOT_TOKEN }}

cache-test-durations:
name: Cache test durations
needs: pytest
if: github.run_attempt == 1 && (success() || failure())
runs-on: ubuntu-22.04
steps:
- name: Download all partial test durations
uses: actions/download-artifact@v4
with:
pattern: test-durations-after-partial-*
path: ${{ runner.temp }}

# This step regroups the 8 partial files and sorts keys alphabetically:
- name: Merge all partial test durations
# overrides workflow default
working-directory: /
run: |
jq -s 'add' ${{ runner.temp }}/test-durations-after-partial-*/.test_durations \
| jq 'to_entries | sort_by(.key) | from_entries' \
> ${{ runner.temp }}/.test_durations
# This step uploads the final file as an artifact. You can then download it from the Github GUI,
# and use it to manually commit file `.test_durations_fallback` from time to time,
# to keep an up-to-date fallback:
- name: Upload final test durations
uses: actions/upload-artifact@v4
with:
name: test-durations-after
path: ${{ runner.temp }}/.test_durations
if-no-files-found: warn
include-hidden-files: true

# Finally, we cache the new test durations. This file will be restored in next CI execution
# (see step "Restore test durations" above).
- name: Cache final test durations
uses: actions/cache/save@v4
with:
path: ${{ runner.temp }}/.test_durations
key: tests-durations-${{ github.sha }}
# key: tests-durations-${{ steps.get-date.outputs.date }}}-

0 comments on commit d08aa7c

Please sign in to comment.