| name: Validate | |
| on: | |
| push: | |
| branches: | |
| - master | |
| pull_request: | |
| # Called by publish.yml, so that a release is validated (and the exact files | |
| # that were validated are what gets uploaded) before it is published. | |
| workflow_call: | |
| inputs: | |
| release: | |
| description: "Run the full sdist validation before publishing" | |
| type: boolean | |
| default: false | |
| permissions: | |
| contents: read | |
| env: | |
| UV_PYTHON_PREFERENCE: only-managed | |
| TZDATA_VERSION: "2024a" | |
| jobs: | |
| build-tzdata: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Cache tzdata | |
| id: cache-tzdata | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6 | |
| with: | |
| path: tzdata_built | |
| key: tzdata-${{ env.TZDATA_VERSION }} | |
| - name: Build tzdata | |
| if: steps.cache-tzdata.outputs.cache-hit != 'true' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y lzip curl | |
| mkdir -p tzdata_built | |
| TMP_DIR=$(mktemp -d) | |
| cd $TMP_DIR | |
| curl --fail --location "https://data.iana.org/time-zones/releases/tzdb-${{ env.TZDATA_VERSION }}.tar.lz" -o tzdb.tar.lz | |
| tar --lzip -xf tzdb.tar.lz | |
| cd tzdb-${{ env.TZDATA_VERSION }} | |
| make install DESTDIR=$GITHUB_WORKSPACE/tzdata_built \ | |
| DATAFORM=main | |
| - name: Upload tzdata artifact | |
| uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7 | |
| with: | |
| name: tzdata | |
| path: tzdata_built/usr/share/zoneinfo | |
| test: | |
| strategy: | |
| matrix: | |
| python-version: [ | |
| "3.11", | |
| "3.12", | |
| "3.13", | |
| "3.14", | |
| "3.14t", | |
| "3.15", | |
| "3.15t", | |
| "pypy3.11", | |
| ] | |
| os: [ubuntu-latest, windows-latest, macos-latest] | |
| runs-on: ${{ matrix.os }} | |
| needs: [build-tzdata, build-dist] | |
| env: | |
| TOXENV: py | |
| TEST_PYTHON: ${{ matrix.python-version }} | |
| TZ: ${{ !startsWith(matrix.os, 'windows-latest') && 'America/New_York' || null }} | |
| # Coverage has no C tracer on PyPy, which makes the tests about ten times | |
| # slower there, and PyPy runs no dateutil code that CPython does not. | |
| COVERAGE: ${{ !startsWith(matrix.python-version, 'pypy') }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| persist-credentials: false | |
| - name: Set timezone (Windows) | |
| if: startsWith(matrix.os, 'windows-latest') | |
| run: tzutil /s "Eastern Standard Time" | |
| - name: Download distributions | |
| uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8 | |
| with: | |
| name: dist | |
| path: dist | |
| - name: Download tzdata artifact | |
| uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8 | |
| if: startsWith(matrix.os, 'ubuntu') | |
| with: | |
| name: tzdata | |
| path: tzdata_download | |
| - name: Install specific tzdata version | |
| run: | | |
| sudo rm -rf /usr/share/zoneinfo | |
| sudo mv tzdata_download /usr/share/zoneinfo | |
| head -n 1 /usr/share/zoneinfo/tzdata.zi | |
| if: startsWith(matrix.os, 'ubuntu') | |
| - &setup-uv | |
| name: Set up uv | |
| uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2 | |
| with: | |
| enable-cache: true | |
| cache-suffix: ${{ github.job }}-${{ env.TEST_PYTHON }}-${{ env.TOXENV }} | |
| cache-dependency-glob: | | |
| **/*requirements*.txt | |
| pyproject.toml | |
| tox.ini | |
| - name: Install tox | |
| run: uv tool install --python 3.13 --with tox-uv tox | |
| # Test the wheel that was built once, on a recent Python, rather than | |
| # building a package from source on each interpreter. Run locally, | |
| # `tox` still builds and installs an sdist itself. | |
| - name: Run tox on the wheel | |
| shell: bash | |
| run: | | |
| if [ "$COVERAGE" = true ]; then posargs=(); else posargs=(-- tests docs); fi | |
| tox -x "testenv:py.base_python=${{ matrix.python-version }}" --installpkg "$(ls dist/*.whl)" "${posargs[@]}" | |
| - name: Generate coverage.xml | |
| if: env.COVERAGE == 'true' | |
| run: tox -e coverage | |
| - name: Report coverage to Codecov | |
| if: env.COVERAGE == 'true' | |
| uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7 | |
| with: | |
| files: ./.tox/coverage.xml | |
| name: ${{ matrix.os }}:${{ matrix.python-version }} | |
| fail_ci_if_error: false | |
| # Exercise the files shipped in the sdist without using the Git checkout. | |
| test-sdist: | |
| if: ${{ inputs.release || needs.build-dist.outputs.test-sdist == 'true' }} | |
| strategy: | |
| matrix: | |
| toxenv: [py] | |
| python-version: [ | |
| "3.11", | |
| "3.12", | |
| "3.13", | |
| "3.14", | |
| "3.14t", | |
| "3.15", | |
| "3.15t", | |
| "pypy3.11", | |
| ] | |
| include: | |
| - python-version: "3.13" | |
| toxenv: tz-fat | |
| - python-version: "3.13" | |
| toxenv: tz-slim | |
| - python-version: "3.13" | |
| toxenv: tz-vanguard | |
| - python-version: "3.13" | |
| toxenv: tz-rearguard | |
| runs-on: ubuntu-latest | |
| needs: [build-tzdata, build-dist] | |
| env: | |
| TOXENV: ${{ matrix.toxenv }} | |
| TEST_PYTHON: ${{ matrix.python-version }} | |
| TZ: America/New_York | |
| # Coverage has no C tracer on PyPy, which makes the tests about ten times | |
| # slower there, and PyPy runs no dateutil code that CPython does not. | |
| COVERAGE: ${{ !startsWith(matrix.python-version, 'pypy') }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| persist-credentials: false | |
| - name: Download distributions | |
| uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8 | |
| with: | |
| name: dist | |
| path: dist | |
| - name: Download tzdata artifact | |
| uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8 | |
| with: | |
| name: tzdata | |
| path: tzdata_download | |
| - name: Install specific tzdata version | |
| run: | | |
| sudo rm -rf /usr/share/zoneinfo | |
| sudo mv tzdata_download /usr/share/zoneinfo | |
| head -n 1 /usr/share/zoneinfo/tzdata.zi | |
| - *setup-uv | |
| - name: Install tox | |
| run: uv tool install --python 3.13 --with tox-uv tox | |
| - name: Run tox on the sdist | |
| run: | | |
| sdist=$(realpath dist/*.tar.gz) | |
| sdist_dir="$RUNNER_TEMP/dateutil-sdist" | |
| mkdir -p "$sdist_dir" | |
| tar -xzf "$sdist" --strip-components=1 -C "$sdist_dir" | |
| cd "$sdist_dir" | |
| if [ "$COVERAGE" = true ]; then posargs=(); else posargs=(-- tests docs); fi | |
| tox -x "testenv:py.base_python=${{ matrix.python-version }}" --installpkg "$sdist" "${posargs[@]}" | |
| other: | |
| runs-on: "ubuntu-latest" | |
| needs: build-tzdata | |
| strategy: | |
| matrix: | |
| toxenv: ["docs", "tz-fat", "tz-slim", "tz-vanguard", "tz-rearguard", "precommit"] | |
| env: | |
| TOXENV: ${{ matrix.toxenv }} | |
| TZ: America/New_York | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| persist-credentials: false | |
| fetch-depth: 0 | |
| - name: Download tzdata artifact | |
| uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8 | |
| with: | |
| name: tzdata | |
| path: tzdata_download | |
| - name: Install specific tzdata version | |
| run: | | |
| sudo rm -rf /usr/share/zoneinfo | |
| sudo mv tzdata_download /usr/share/zoneinfo | |
| cat /usr/share/zoneinfo/tzdata.zi | head -n 1 | |
| - *setup-uv | |
| - name: Install tox | |
| run: uv tool install --python 3.13 --with tox-uv tox | |
| - name: Run action | |
| run: tox | |
| darker: | |
| # This checks the lines changed by a push or PR, so it is meaningless when | |
| # called from the publish workflow. | |
| if: ${{ !inputs.release }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| persist-credentials: false | |
| fetch-depth: 0 | |
| - *setup-uv | |
| - name: Install darker | |
| run: uv tool install --python 3.13 "darker[black,isort]==3.0.0" | |
| - name: Run darker | |
| # Check only the lines changed relative to the merge base of the PR | |
| # (or the commit before a push). The darker GitHub action is not used | |
| # because its released versions refer to a commit-range action by a | |
| # path local to the darker repo, so it fails for everyone else; see | |
| # https://github.com/akaihola/darker/issues/847 | |
| env: | |
| REVISION: ${{ github.event_name == 'pull_request' && format('{0}...', github.event.pull_request.base.sha) || format('{0}...', github.event.before) }} | |
| run: darker --check --diff --color --isort --revision "$REVISION" . | |
| zizmor: | |
| if: ${{ !inputs.release }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| persist-credentials: false | |
| - name: Check GitHub Actions security | |
| uses: zizmorcore/zizmor-action@53290badfa369cb0bf7ad16342cb62c2fa47ac7b # v0.8.0 | |
| with: | |
| advanced-security: false | |
| annotations: true | |
| build-dist: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| test-sdist: ${{ steps.sdist-changes.outputs.changed }} | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| persist-credentials: false | |
| # The version comes from git tags, so we need the history. | |
| fetch-depth: 0 | |
| - name: Check whether the sdist needs testing | |
| if: github.event_name == 'pull_request' | |
| id: sdist-changes | |
| env: | |
| BASE_SHA: ${{ github.event.pull_request.base.sha }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| run: | | |
| if ! git diff --quiet "$BASE_SHA...$HEAD_SHA" -- pyproject.toml; then | |
| echo "changed=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| - *setup-uv | |
| - name: Install tox | |
| run: uv tool install --python 3.13 --with tox-uv tox | |
| - name: Run tox | |
| run: tox -e build | |
| - name: Check generation | |
| run: | | |
| exactly_one() { | |
| value=$(find dist -iname $1 | wc -l) | |
| if [ $value -ne 1 ]; then | |
| echo "Found $value instances of $1, not 1" | |
| return 1 | |
| else | |
| echo "Found exactly 1 instance of $value" | |
| fi | |
| } | |
| # Check that exactly one tarball and one wheel are created | |
| exactly_one '*.tar.gz' | |
| exactly_one '*.whl' | |
| - name: Upload distributions | |
| uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7 | |
| with: | |
| name: dist | |
| path: dist/ | |
| if-no-files-found: error |