BUG: make a failing Monte Carlo worker say so #3688
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Tests | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, reopened, ready_for_review] | |
| paths: | |
| - "**.py" | |
| - ".github/**" | |
| - "pyproject.toml" | |
| - "requirements*" | |
| # Codecov compares a pull request against the report it holds for the base | |
| # commit. Without this, nothing ever uploads one for a commit on develop, so | |
| # every pull request is measured against whichever old report is nearest and | |
| # says how far behind it has fallen. | |
| push: | |
| branches: [master, develop] | |
| paths: | |
| - "**.py" | |
| - ".github/**" | |
| - "pyproject.toml" | |
| - "requirements*" | |
| defaults: | |
| run: | |
| shell: bash | |
| env: | |
| # The Pytest matrix below is 3 os x 2 python-version, so CodecovUpload must | |
| # receive six coverage reports. Nothing can derive a matrix size from another | |
| # job, so it is written out here; the guard in CodecovUpload fails loudly if | |
| # the matrix grows and this does not. | |
| COVERAGE_LEG_COUNT: 6 | |
| jobs: | |
| Pytest: | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| # One platform's result should not decide the other five. Added in #1084; | |
| # kept explicit here so it does not get tidied away later. | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| python-version: ["3.10", "3.14"] | |
| env: | |
| MPLBACKEND: Agg | |
| steps: | |
| - uses: actions/checkout@main | |
| - name: Set up headless display | |
| uses: pyvista/setup-headless-display-action@v4 | |
| - name: Configure Mesa software rendering on Linux | |
| if: runner.os == 'Linux' | |
| run: | | |
| echo "LIBGL_ALWAYS_SOFTWARE=1" >> "$GITHUB_ENV" | |
| echo "GALLIUM_DRIVER=llvmpipe" >> "$GITHUB_ENV" | |
| - name: Set up Python | |
| uses: actions/setup-python@main | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache: 'pip' | |
| cache-dependency-path: | | |
| requirements.txt | |
| requirements-tests.txt | |
| requirements-optional.txt | |
| - name: Install rocketpy | |
| run: pip install . | |
| - name: Test importing rocketpy | |
| run: python -c "import sys, rocketpy; print(f'{rocketpy.__name__} running on Python {sys.version}')" | |
| - name: Install test dependencies | |
| run: | | |
| pip install -r requirements-tests.txt | |
| pip install .[all] | |
| - name: Run Unit Tests | |
| run: pytest tests/unit --cov=rocketpy | |
| - name: Run Documentation Tests | |
| run: | | |
| pip install numpy --upgrade | |
| pytest rocketpy --doctest-modules --cov=rocketpy --cov-append | |
| - name: Run Integration Tests | |
| run: | | |
| pytest tests/integration \ | |
| --deselect tests/integration/test_plots.py::test_flight_animations_run_off_screen \ | |
| --deselect tests/integration/test_plots.py::test_flight_animations_render_all_scene_options \ | |
| --deselect tests/integration/test_plots.py::test_flight_animation_export_gif \ | |
| --cov=rocketpy --cov-append | |
| - name: Run VTK animation tests | |
| run: | | |
| tests=( | |
| tests/integration/test_plots.py::test_flight_animations_run_off_screen | |
| tests/integration/test_plots.py::test_flight_animations_render_all_scene_options | |
| tests/integration/test_plots.py::test_flight_animation_export_gif | |
| ) | |
| attempts=3 | |
| for attempt in $(seq 1 "$attempts"); do | |
| if pytest "${tests[@]}" --cov=rocketpy --cov-append; then | |
| exit 0 | |
| else | |
| status=$? | |
| fi | |
| # 138 is SIGBUS on macOS, 135 is SIGBUS on Linux, 139 is SIGSEGV on | |
| # both. The measured split on this test was eight SIGSEGV to two | |
| # SIGBUS, so keying on 138 alone let the common case through. | |
| if [[ ! "$status" =~ ^(135|138|139)$ || "$attempt" == "$attempts" ]]; then | |
| exit "$status" | |
| fi | |
| done | |
| # This is the only step that writes XML; the earlier ones only --cov-append | |
| # into .coverage. Naming the report here, rather than renaming it after | |
| # the fact, is what lets CodecovUpload merge all six into one directory | |
| # without them clobbering each other as six identical coverage.xml. | |
| - name: Run Acceptance Tests | |
| run: >- | |
| pytest tests/acceptance --cov=rocketpy --cov-append | |
| --cov-report=xml:coverage-${{ matrix.os }}-py${{ matrix.python-version }}.xml | |
| - name: Upload coverage to artifacts | |
| uses: actions/upload-artifact@main | |
| with: | |
| name: coverage-${{ matrix.os }}-py${{ matrix.python-version }} | |
| path: coverage-${{ matrix.os }}-py${{ matrix.python-version }}.xml | |
| if-no-files-found: error | |
| # Artifacts are keyed by (run, name) and survive across attempts, so | |
| # without this every leg of a re-run fails with 409 Conflict. The VTK | |
| # tests above are flaky enough that re-runs are routine. | |
| overwrite: true | |
| # CodecovUpload consumes these minutes later and nothing else reads | |
| # them, so the 90 day default would be six dead artifacts per run. | |
| retention-days: 1 | |
| CodecovUpload: | |
| needs: Pytest | |
| # `needs` alone skips this job when a single leg fails, which sends Codecov | |
| # nothing at all for the commit and .codecov.yml resolves that as an error. | |
| # Run unless the workflow was cancelled and upload the legs that did finish. | |
| if: ${{ !cancelled() }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@main | |
| - name: Download coverage reports | |
| uses: actions/download-artifact@main | |
| with: | |
| pattern: coverage-* | |
| path: coverage-reports | |
| merge-multiple: true | |
| # download-artifact exits 0 when `pattern` matches fewer artifacts than | |
| # expected, so five of six reports would otherwise look like a clean run | |
| # and quietly under-report coverage. That silence is what #1088 is about. | |
| - name: Verify every leg reported coverage | |
| id: reports | |
| env: | |
| PYTEST_RESULT: ${{ needs.Pytest.result }} | |
| run: | | |
| mkdir -p coverage-reports | |
| count=$(find coverage-reports -maxdepth 1 -name '*.xml' | wc -l) | |
| echo "count=$count" >> "$GITHUB_OUTPUT" | |
| echo "Downloaded $count of $COVERAGE_LEG_COUNT coverage reports." | |
| if [ "$PYTEST_RESULT" = "success" ] && [ "$count" -ne "$COVERAGE_LEG_COUNT" ]; then | |
| echo "::error::Every Pytest leg passed, but only $count of $COVERAGE_LEG_COUNT coverage reports arrived." | |
| exit 1 | |
| fi | |
| if [ "$count" -eq 0 ]; then | |
| echo "::warning::No coverage report to upload; no Pytest leg produced one." | |
| fi | |
| - name: Upload to Codecov | |
| # Nothing to send, and Codecov would fail on an empty directory. The | |
| # failing Pytest leg is already reporting the real problem. | |
| if: steps.reports.outputs.count != '0' | |
| uses: codecov/codecov-action@main | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| directory: coverage-reports | |
| # Forks get no secrets, so the token above is empty for them and the | |
| # tokenless upload is rate limited. An external contributor should not | |
| # see red for a Codecov-side hiccup in their pull request. | |
| fail_ci_if_error: ${{ secrets.CODECOV_TOKEN != '' }} |