sharrow testing #379
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: sharrow testing | |
| on: | |
| push: | |
| branches: [ main, develop ] | |
| tags: | |
| - 'v[0-9]+.[0-9]+**' | |
| pull_request: | |
| branches: [ main, develop ] | |
| workflow_dispatch: | |
| inputs: | |
| deploy_docs: | |
| description: Build and deploy the documentation without publishing to PyPI | |
| required: true | |
| default: false | |
| type: boolean | |
| jobs: | |
| fmt: | |
| name: formatting quality | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| shell: bash -l {0} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Code Quality Check with Ruff | |
| # code quality check, stop the build for any errors | |
| uses: astral-sh/ruff-action@v3 | |
| with: | |
| version: "latest" | |
| args: "check . --show-fixes --exit-non-zero-on-fix" | |
| test-minimal: | |
| needs: fmt | |
| name: minimal tests | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| shell: bash -l {0} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: astral-sh/setup-uv@v7 | |
| with: | |
| version: "0.9.6" | |
| enable-cache: true | |
| cache-dependency-glob: "uv.lock" | |
| python-version: '3.11' | |
| - name: Initial simple tests | |
| # tests that sharrow can be imported and that categorical tests can be run | |
| run: | | |
| uv run pytest sharrow/tests/test_categorical.py | |
| - name: Dataset tests | |
| # tests that the datasets can be read and that the tests can be run | |
| run: | | |
| uv run pytest sharrow/tests/test_datasets.py | |
| - name: More complete test with pytest | |
| run: | | |
| uv run pytest -v --disable-warnings sharrow/tests | |
| test: | |
| needs: fmt | |
| name: ${{ matrix.os }} py${{ matrix.python-version }} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| matrix: | |
| os: ["ubuntu-latest", "windows-latest"] | |
| python-version: ["3.10", "3.11", "3.12", "3.13"] | |
| fail-fast: false | |
| defaults: | |
| run: | |
| shell: bash -l {0} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: astral-sh/setup-uv@v7 | |
| with: | |
| version: "0.9.6" | |
| python-version: ${{ matrix.python-version }} | |
| - name: File contents | |
| run: | | |
| cat sharrow/example_data.py | |
| - name: UV sync | |
| run: | | |
| uv self version | |
| uv cache clean | |
| uv sync --locked | |
| - name: Syntax Check with Ruff | |
| uses: astral-sh/ruff-action@v3 | |
| with: | |
| args: "check . --select=E9,F63,F7,F82 --no-fix" | |
| - name: Code Quality Check with Ruff | |
| uses: astral-sh/ruff-action@v3 | |
| with: | |
| args: "check . --show-fixes --exit-non-zero-on-fix" | |
| - name: Test with pytest | |
| run: | | |
| uv run --locked pytest | |
| deploy-docs: | |
| needs: test | |
| # Run for releases or when explicitly requested from the Actions UI. | |
| if: >- | |
| startsWith(github.ref, 'refs/tags/v') || | |
| (github.event_name == 'workflow_dispatch' && inputs.deploy_docs) | |
| runs-on: ubuntu-latest | |
| environment: asim | |
| defaults: | |
| run: | |
| shell: bash -l {0} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install uv and set Python 3.10 | |
| uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 | |
| with: | |
| version: "0.12.2" | |
| python-version: "3.10" | |
| enable-cache: true | |
| - name: Install dependencies | |
| run: | | |
| uv sync --locked --dev | |
| - name: UV checkup | |
| run: | | |
| uv --version | |
| uv run --locked python --version | |
| uv pip list | |
| - name: Build wheel | |
| run: | | |
| uv build | |
| - name: Build the docs | |
| run: | | |
| uv run --locked --with-requirements docs/requirements.txt python docs/_script/run_all.py | |
| uv run --locked --with-requirements docs/requirements.txt jupyter-book build ./docs | |
| - name: Push to GitHub Pages | |
| uses: peaceiris/actions-gh-pages@v3.5.9 | |
| with: | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| # Token is created automatically by Github Actions, no other config needed | |
| publish_dir: ./docs/_build/html | |
| # now send to PyPI | |
| - name: Publish to PyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| if: >- | |
| github.event_name == 'push' && | |
| github.repository_owner == 'ActivitySim' && | |
| startsWith(github.ref, 'refs/tags/v') | |
| with: | |
| user: __token__ | |
| password: ${{ secrets.PYPI_API_TOKEN }} | |
| activitysim-examples: | |
| # test that updates to sharrow will not break the activitysim canonical examples | |
| needs: fmt | |
| env: | |
| python-version: "3.10" | |
| label: linux-64 | |
| # limit glibc malloc arenas, which can otherwise retain large amounts | |
| # of memory freed during numba compilation | |
| MALLOC_ARENA_MAX: "2" | |
| # standard GitHub-hosted ubuntu runners have 4 cores | |
| NUMBA_NUM_THREADS: "4" | |
| strategy: | |
| matrix: | |
| include: | |
| - region: ActivitySim 1-Zone Example (MTC) | |
| region-org: ActivitySim | |
| region-repo: activitysim-prototype-mtc | |
| region-branch: extended | |
| - region: ActivitySim 2-Zone Example (SANDAG) | |
| region-org: ActivitySim | |
| region-repo: sandag-abm3-example | |
| region-branch: main | |
| fail-fast: false | |
| defaults: | |
| run: | |
| shell: bash -l {0} | |
| name: ${{ matrix.region }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 720 # Sets the timeout to 12 hours | |
| steps: | |
| - name: Checkout Sharrow | |
| uses: actions/checkout@v4 | |
| with: | |
| path: 'sharrow' | |
| - name: Checkout ActivitySim | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: 'ActivitySim/activitysim' | |
| ref: 'main' | |
| path: 'activitysim' | |
| - name: Setup UV | |
| uses: astral-sh/setup-uv@v7 | |
| with: | |
| version: "0.9.6" | |
| enable-cache: true | |
| cache-dependency-glob: "uv.lock" | |
| python-version: ${{ env.python-version }} | |
| - name: Set cache date for year and month | |
| run: echo "DATE=$(date +'%Y%m')" >> $GITHUB_ENV | |
| - name: Add swap space | |
| # numba compilation of sharrow flows has a large transient memory | |
| # peak; extra swap absorbs the spike instead of the runner being | |
| # terminated by the host when it exhausts physical memory | |
| run: | | |
| sudo swapon --show || true | |
| df -h /mnt | |
| sudo fallocate -l 8G /mnt/extra-swapfile | |
| sudo chmod 600 /mnt/extra-swapfile | |
| sudo mkswap /mnt/extra-swapfile | |
| sudo swapon /mnt/extra-swapfile | |
| free -h | |
| - name: Restore sharrow flow cache | |
| # restores previously compiled sharrow flows (including numba on-disk | |
| # cache files), which greatly reduces both the time and the peak | |
| # memory needed to run the examples | |
| uses: actions/cache/restore@v4 | |
| with: | |
| path: ~/.cache/ActivitySim | |
| key: sharrow-flows-${{ matrix.region-repo }}-${{ env.DATE }}-${{ github.run_id }} | |
| restore-keys: | | |
| sharrow-flows-${{ matrix.region-repo }}-${{ env.DATE }}- | |
| sharrow-flows-${{ matrix.region-repo }}- | |
| - name: Create Virtual Env | |
| run: | | |
| uv venv | |
| source .venv/bin/activate | |
| uv pip install "black==22.12.0" "coveralls>=4" \ | |
| "cytoolz==0.12.2" "dask>=2026" "isort>=5.12.0" \ | |
| "multimethod<2.0" "nbmake>=1.4.6" "numba>=0.57" \ | |
| "numpy>=2,<3" "openmatrix==0.3.5.0" \ | |
| "pandera>=0.30" "pandas>=2,<3" "platformdirs>=3.2" \ | |
| "psutil>=5.9" "pyarrow>=11.0" "pydantic>=2.6,<3" "pypyr>=5.8" \ | |
| "tables>=3.9" "pytest>=7.2" "pytest-cov" "pytest-regressions" \ | |
| "pyyaml>=6" "requests>=2.28" "ruff" "scikit-learn>=1.2" \ | |
| "simwrapper>1.7" "sparse" "xarray>=2025" \ | |
| "zarr>=2,<3" "zstandard" \ | |
| ./sharrow ./activitysim | |
| - name: UV checkup | |
| run: | | |
| source .venv/bin/activate | |
| uv pip list | |
| - name: Checkout Example | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: '${{ matrix.region-org }}/${{ matrix.region-repo }}' | |
| ref: '${{ matrix.region-branch }}' | |
| path: '${{ matrix.region-repo }}' | |
| - name: Test ${{ matrix.region }} | |
| # Each collected test runs in its own process, so memory is fully | |
| # released between tests. A watchdog stops a test gracefully when | |
| # system memory (incl. swap) is nearly exhausted: a controlled | |
| # failure (unlike a runner shutdown) lets later steps still run, in | |
| # particular saving the sharrow flow cache so a subsequent attempt | |
| # resumes from the already-compiled flows instead of starting over. | |
| run: | | |
| source .venv/bin/activate | |
| cd ${{ matrix.region-repo }} | |
| mem_line() { | |
| free -m | awk '/^Mem:/ {u=$3; a=$7} /^Swap:/ {s=$4} END {print "used_MB=" u " avail_MB=" a " swap_free_MB=" s}' | |
| } | |
| free_total() { | |
| free -m | awk '/^Mem:/ {a=$7} /^Swap:/ {s=$4} END {print a + s}' | |
| } | |
| # stream memory samples into the live log so the data survives | |
| # even if the runner is terminated abruptly | |
| ( while true; do echo "[mem] $(date +%H:%M:%S) $(mem_line)" | tee -a /tmp/mem-monitor.log; sleep 30; done ) & | |
| monitor_pid=$! | |
| readarray -t tests < <(python -m pytest ./test --collect-only -q | grep '::') | |
| echo "collected ${#tests[@]} tests" | |
| rc=0 | |
| for t in "${tests[@]}"; do | |
| echo "::group::pytest $t" | |
| python -m pytest "$t" & | |
| test_pid=$! | |
| while kill -0 "$test_pid" 2>/dev/null; do | |
| if [ "$(free_total)" -lt 1024 ]; then | |
| echo "[watchdog] $(date +%H:%M:%S) memory nearly exhausted ($(mem_line)); stopping test" | |
| kill "$test_pid" 2>/dev/null || true | |
| sleep 30 | |
| fi | |
| sleep 5 | |
| done | |
| if wait "$test_pid"; then | |
| echo "[ok] $t" | |
| else | |
| echo "[failed] $t (exit code $?)" | |
| rc=1 | |
| fi | |
| echo "::endgroup::" | |
| done | |
| kill "$monitor_pid" 2>/dev/null || true | |
| exit $rc | |
| - name: Save sharrow flow cache | |
| # saved even when the tests fail, so that flows compiled during a | |
| # failed run still warm the cache for subsequent runs | |
| if: always() | |
| uses: actions/cache/save@v4 | |
| with: | |
| path: ~/.cache/ActivitySim | |
| key: sharrow-flows-${{ matrix.region-repo }}-${{ env.DATE }}-${{ github.run_id }} | |
| - name: Memory monitor report | |
| if: always() | |
| run: | | |
| echo "== peak system memory usage ==" | |
| awk '{ for (i=1; i<=NF; i++) if ($i ~ /^used_MB=/) { v=substr($i,9)+0; if (v>max) max=v } } | |
| END { print "peak used_MB=" max }' /tmp/mem-monitor.log || true |