diff --git a/.github/workflows/build-manylinux-container-images.yml b/.github/workflows/build-manylinux-container-images.yml index f9d45c903..b74a79c94 100644 --- a/.github/workflows/build-manylinux-container-images.yml +++ b/.github/workflows/build-manylinux-container-images.yml @@ -48,6 +48,7 @@ jobs: - 2014 - _2_24 # PEP 600 - _2_28 + - _2_31 # Latest manylinux include: - IMAGE: ARCH: x86_64 @@ -59,7 +60,7 @@ jobs: YEAR: 2010 env: - LIBSSH_VERSION: 0.9.6 + LIBSSH_VERSION: 0.10.5 # Updated libssh version PYPA_MANYLINUX_TAG: >- manylinux${{ matrix.YEAR }}_${{ matrix.IMAGE.ARCH }} FULL_IMAGE_NAME: >- diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 70bdd6b84..a6b75bc05 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -164,10 +164,10 @@ jobs: changelog-draft-name-rst: >- ${{ steps.changelog-draft-name.outputs.filename-base }}.rst steps: - - name: Switch to using Python 3.11 by default - uses: actions/setup-python@v5.3.0 + - name: Switch to using Python 3.12 by default + uses: actions/setup-python@v5 with: - python-version: 3.11 + python-version: 3.12 - name: >- Mark the build as untagged '${{ github.event.repository.default_branch @@ -439,10 +439,10 @@ jobs: TOXENV: make-changelog steps: - - name: Switch to using Python 3.11 - uses: actions/setup-python@v5.3.0 + - name: Switch to using Python 3.12 + uses: actions/setup-python@v5 with: - python-version: 3.11 + python-version: 3.12 - name: Grab the source from Git uses: actions/checkout@v4.1.6 @@ -764,16 +764,20 @@ jobs: strategy: matrix: python-version: + - >- + 3.13 + - >- + 3.12 - >- 3.10 - 3.9 runner-vm-os: - - ubuntu-22.04 + - ubuntu-24.04 store-sdist-to-artifact: - false include: - - python-version: 3.12 - runner-vm-os: ubuntu-22.04 + - python-version: 3.13 + runner-vm-os: ubuntu-24.04 store-sdist-to-artifact: true env: @@ -782,7 +786,7 @@ jobs: steps: - name: Switch to using Python ${{ matrix.python-version }} - uses: actions/setup-python@v5.3.0 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} @@ -1226,13 +1230,14 @@ jobs: strategy: matrix: python-version: + #- "3.13" - "3.12" - - "3.11" - - "3.10" - - 3.9 + #- "3.11" + #- "3.10" + #- 3.9 runner-vm-os: - ubuntu-24.04 - - ubuntu-22.04 + #- ubuntu-22.04 dist-type: - binary - source @@ -1253,40 +1258,41 @@ jobs: secrets: codecov-token: ${{ secrets.CODECOV_TOKEN }} - test-macos: - name: ๐Ÿงช Test${{ '' }} # nest jobs under the same sidebar category - needs: - - build-bin-macos - - build-src - - pre-setup # transitive, for accessing settings - strategy: - matrix: - python-version: - - "3.12" - - "3.11" - - "3.10" - - 3.9 - runner-vm-os: - - macos-13 - dist-type: - - binary - - source - - uses: ./.github/workflows/reusable-tests.yml - with: - python-version: ${{ matrix.python-version }} - runner-vm-os: ${{ matrix.runner-vm-os }} - dist-type: ${{ matrix.dist-type }} - release-requested: >- - ${{ needs.pre-setup.outputs.release-requested }} - yolo: ${{ fromJSON(needs.pre-setup.outputs.is-yolo-mode) }} - cache-key-files: ${{ needs.pre-setup.outputs.cache-key-files }} - source-tarball-name: >- - ${{ needs.pre-setup.outputs.sdist-artifact-name }} - dists-artifact-name: >- - ${{ needs.pre-setup.outputs.dists-artifact-name }} - secrets: - codecov-token: ${{ secrets.CODECOV_TOKEN }} + #test-macos: + # name: ๐Ÿงช Test${{ '' }} # nest jobs under the same sidebar category + # needs: + # - build-bin-macos + # - build-src + # - pre-setup # transitive, for accessing settings + # strategy: + # matrix: + # python-version: + # - "3.13" + # - "3.12" + # - "3.11" + # - "3.10" + # - 3.9 + # runner-vm-os: + # - macos-13 + # dist-type: + # - binary + # - source + + # uses: ./.github/workflows/reusable-tests.yml + # with: + # python-version: ${{ matrix.python-version }} + # runner-vm-os: ${{ matrix.runner-vm-os }} + # dist-type: ${{ matrix.dist-type }} + # release-requested: >- + # ${{ needs.pre-setup.outputs.release-requested }} + # yolo: ${{ fromJSON(needs.pre-setup.outputs.is-yolo-mode) }} + # cache-key-files: ${{ needs.pre-setup.outputs.cache-key-files }} + # source-tarball-name: >- + # ${{ needs.pre-setup.outputs.sdist-artifact-name }} + # dists-artifact-name: >- + # ${{ needs.pre-setup.outputs.dists-artifact-name }} + # secrets: + # codecov-token: ${{ secrets.CODECOV_TOKEN }} dist-meta: name: Verify ๐Ÿ๐Ÿ“ฆ metadata @@ -1304,10 +1310,10 @@ jobs: TOXENV: metadata-validation steps: - - name: Switch to using Python 3.11 by default - uses: actions/setup-python@v5.3.0 + - name: Switch to using Python 3.12 by default + uses: actions/setup-python@v5 with: - python-version: 3.11 + python-version: 3.12 - name: Retrieve the project source from an sdist inside the GHA artifact uses: re-actors/checkout-python-sdist@release/v2 @@ -1380,7 +1386,7 @@ jobs: - lint - pre-setup # transitive, for accessing settings - test-linux - - test-macos + #- test-macos runs-on: Ubuntu-latest @@ -1393,7 +1399,7 @@ jobs: allowed-failures: >- ${{ fromJSON(needs.pre-setup.outputs.is-yolo-mode) - && 'build-rpms, lint, test-linux, test-macos' + && 'build-rpms, lint, test-linux' || '' }} jobs: ${{ toJSON(needs) }} @@ -1816,10 +1822,10 @@ jobs: path: dist/ merge-multiple: true - - name: Switch to Python 3.11 - uses: actions/setup-python@v5.3.0 + - name: Switch to Python 3.12 + uses: actions/setup-python@v5 with: - python-version: 3.11 + python-version: 3.12 - name: >- Calculate Python interpreter version hash value for use in the cache key diff --git a/.github/workflows/reusable-tests.yml b/.github/workflows/reusable-tests.yml index 93147fabc..170b95869 100644 --- a/.github/workflows/reusable-tests.yml +++ b/.github/workflows/reusable-tests.yml @@ -1,5 +1,4 @@ --- - name: โ™ฒ ๐Ÿงช on: # yamllint disable-line rule:truthy @@ -53,7 +52,7 @@ env: PROJECT_NAME: ansible-pylibssh PY_COLORS: 1 # Recognized by the `py` package, dependency of `pytest` TOX_PARALLEL_NO_SPINNER: 1 - TOX_TESTENV_PASSENV: >- # Make tox-wrapped tools see color requests + TOX_TESTENV_PASSENV: >- # Make tox-wrapped tools see color requests FORCE_COLOR MYPY_FORCE_COLOR NO_COLOR @@ -62,7 +61,6 @@ env: PYTEST_THEME_MODE TOX_VERSION: tox - jobs: test-linux: name: >- @@ -70,7 +68,6 @@ jobs: | ${{ inputs.dist-type }} dist runs-on: ${{ inputs.runner-vm-os }} - timeout-minutes: 7 continue-on-error: >- @@ -86,223 +83,228 @@ jobs: TOXENV: py steps: - - name: Install build toolchain and openssl headers on Linux - if: >- - inputs.dist-type == 'source' && - runner.os == 'Linux' - run: sudo apt update && sudo apt install build-essential libssl-dev - - name: Install libssh and openssl headers on macOS - if: >- - runner.os == 'macOS' - run: brew install libssh - - name: Install catchsegv and libssh headers on Linux for cythonize+coverage - if: >- - runner.os == 'Linux' - run: sudo apt update && sudo apt install glibc-tools libssh-dev - - name: Switch ๐Ÿ to v${{ inputs.python-version }} - id: python-install - uses: actions/setup-python@v5.3.0 - with: - python-version: ${{ inputs.python-version }} - - - name: Retrieve the project source from an sdist inside the GHA artifact - uses: re-actors/checkout-python-sdist@release/v2 - with: - source-tarball-name: ${{ inputs.source-tarball-name }} - workflow-artifact-name: ${{ inputs.dists-artifact-name }} + - name: Install build toolchain, openssl headers on Linux + if: >- + inputs.dist-type == 'source' && + runner.os == 'Linux' + run: sudo apt update && sudo apt install build-essential libssl-dev + - name: Install libssh and openssl headers on macOS + if: >- + runner.os == 'macOS' + run: brew install libssh + - name: Install catchsegv, libssh headers on Linux cythonize+coverage + if: >- + runner.os == 'Linux' + run: sudo apt update && sudo apt install glibc-tools libssh-dev + - name: Switch ๐Ÿ to v${{ inputs.python-version }} + id: python-install + uses: actions/setup-python@v5 + with: + python-version: ${{ inputs.python-version }} - - name: Figure out if the interpreter ABI is stable - id: py-abi - run: | - from os import environ - from pathlib import Path - from sys import version_info + - name: Retrieve the project source from an sdist inside the GHA artifact + uses: re-actors/checkout-python-sdist@release/v2 + with: + source-tarball-name: ${{ inputs.source-tarball-name }} + workflow-artifact-name: ${{ inputs.dists-artifact-name }} - FILE_APPEND_MODE = 'a' + - name: Figure out if the interpreter ABI is stable + id: py-abi + run: | + from os import environ + from pathlib import Path + from sys import version_info - is_stable_abi = version_info.releaselevel == 'final' + FILE_APPEND_MODE = 'a' - with Path(environ['GITHUB_OUTPUT']).open( - mode=FILE_APPEND_MODE, - ) as outputs_file: - print( - 'is-stable-abi={is_stable_abi}'. - format(is_stable_abi=str(is_stable_abi).lower()), - file=outputs_file, - ) - shell: python - - name: >- - Calculate Python interpreter version hash value - for use in the cache key - if: fromJSON(steps.py-abi.outputs.is-stable-abi) - id: calc-cache-key-py - run: | - from hashlib import sha512 - from os import environ - from pathlib import Path - from sys import version + is_stable_abi = version_info.releaselevel == 'final' - FILE_APPEND_MODE = 'a' + with Path(environ['GITHUB_OUTPUT']).open( + mode=FILE_APPEND_MODE, + ) as outputs_file: + print( + 'is-stable-abi={is_stable_abi}'. + format(is_stable_abi=str(is_stable_abi).lower()), + file=outputs_file, + ) + shell: python + - name: >- + Calculate Python interpreter version hash value + for use in the cache key + if: fromJSON(steps.py-abi.outputs.is-stable-abi) + id: calc-cache-key-py + run: | + from hashlib import sha512 + from os import environ + from pathlib import Path + from sys import version - hash = sha512(version.encode()).hexdigest() + FILE_APPEND_MODE = 'a' - with Path(environ['GITHUB_OUTPUT']).open( - mode=FILE_APPEND_MODE, - ) as outputs_file: - print(f'py-hash-key={hash}', file=outputs_file) - shell: python - - name: Set up pip cache - if: fromJSON(steps.py-abi.outputs.is-stable-abi) - uses: actions/cache@v4 - with: - path: >- - ${{ - runner.os == 'Linux' - && '~/.cache/pip' - || '~/Library/Caches/pip' - }} - key: >- - ${{ runner.os }}-pip-${{ - steps.calc-cache-key-py.outputs.py-hash-key }}-${{ - inputs.cache-key-files }} - restore-keys: | - ${{ runner.os }}-pip-${{ - steps.calc-cache-key-py.outputs.py-hash-key - }}- - ${{ runner.os }}-pip- - ${{ runner.os }}- + hash = sha512(version.encode()).hexdigest() - - name: Upgrade pip with `requires_python` - run: >- - python -m - pip install - --user - --upgrade - --force-reinstall - pip-with-requires-python - - name: Install tox - run: >- - python -m - pip install - --user - '${{ env.TOX_VERSION }}' + with Path(environ['GITHUB_OUTPUT']).open( + mode=FILE_APPEND_MODE, + ) as outputs_file: + print(f'py-hash-key={hash}', file=outputs_file) + shell: python + - name: Set up pip cache + if: fromJSON(steps.py-abi.outputs.is-stable-abi) + uses: actions/cache@v4 + with: + path: >- + ${{ + runner.os == 'Linux' + && '~/.cache/pip' + || '~/Library/Caches/pip' + }} + key: >- + ${{ runner.os }}-pip-${{ + steps.calc-cache-key-py.outputs.py-hash-key }}-${{ + inputs.cache-key-files }} + restore-keys: | + ${{ runner.os }}-pip-${{ + steps.calc-cache-key-py.outputs.py-hash-key + }}- + ${{ runner.os }}-pip- + ${{ runner.os }}- - - name: Download all the dists - uses: actions/download-artifact@v4 - with: - pattern: ${{ needs.pre-setup.outputs.dists-artifact-name }}* - path: dist/ - merge-multiple: true + - name: Upgrade pip with `requires_python` + run: >- + python -m + pip install + --user + --upgrade + --force-reinstall + pip-with-requires-python + - name: Install tox + run: >- + python -m + pip install + --user + '${{ env.TOX_VERSION }}' - - name: Determine pre-compiled compatible wheel - env: - # NOTE: When `pip` is forced to colorize output piped into `jq`, - # NOTE: the latter can't parse it. So we're overriding the color - # NOTE: preference here via https://no-color.org. - # NOTE: Setting `FORCE_COLOR` to any value (including 0, an empty - # NOTE: string, or a "YAML null" `~`) doesn't have any effect and - # NOTE: `pip` (through its verndored copy of `rich`) treats the - # NOTE: presence of the variable as "force-color" regardless. - # - # NOTE: This doesn't actually work either, so we'll resort to unsetting - # NOTE: in the Bash script. - # NOTE: Ref: https://github.com/Textualize/rich/issues/2622 - NO_COLOR: 1 - if: inputs.dist-type == 'binary' - id: wheel-file - run: > - echo -n path= | tee -a "${GITHUB_OUTPUT}" + - name: Download all the dists + uses: actions/download-artifact@v4 + with: + pattern: ${{ needs.pre-setup.outputs.dists-artifact-name }}* + path: dist/ + merge-multiple: true + - name: Debug - List downloaded dist files + run: | + echo "Contents of dist/ directory:" + ls -la dist/ || echo "No dist directory found" + echo -e "\nLooking for wheel files:" + find dist/ -name '*.whl' -ls || echo "No wheel files found" + echo -e "\nLooking for source distributions:" + find dist/ -name '*.tar.gz' -ls || echo "No source dist found" - unset FORCE_COLOR + - name: Determine pre-compiled compatible wheel + env: + # NOTE: When `pip` is forced to colorize output piped into `jq`, + # NOTE: the latter can't parse it. So we're overriding the color + # NOTE: preference here via https://no-color.org. + # NOTE: Setting `FORCE_COLOR` to any value (including 0, an empty + # NOTE: string, or a "YAML null" `~`) doesn't have any effect and + # NOTE: `pip` (through its vendored copy of `rich`) treats the + # NOTE: presence of the variable as "force-color" regardless. + # + # NOTE: This doesn't actually work either, so we'll resort to + # NOTE: unsetting in the Bash script. + # NOTE: Ref: https://github.com/Textualize/rich/issues/2622 + NO_COLOR: 1 + if: inputs.dist-type == 'binary' + id: wheel-file + run: > + echo -n path= | tee -a "${GITHUB_OUTPUT}" + pwd + whoami + unset FORCE_COLOR - python - -X utf8 - -u -I - -m pip install - --find-links=./dist - --no-index - '${{ env.PROJECT_NAME }}' - --force-reinstall - --no-color - --no-deps - --only-binary=:all: - --dry-run - --report=- - --quiet - | jq --raw-output .install[].download_info.url - | sed -e 's#^file://##' -e 's#%2B#+#' - | tee -a "${GITHUB_OUTPUT}" - shell: bash - - name: >- - Pre-populate tox env: - ${{ env.TOXENV }} - run: >- - python -m - tox - --parallel auto - --parallel-live - --skip-missing-interpreters false - --installpkg '${{ - inputs.dist-type == 'binary' - && steps.wheel-file.outputs.path - || format('dist/{0}', inputs.source-tarball-name) - }}' - --notest + python + -X utf8 + -u -I + -m pip install + --find-links=./dist + --no-index + '${{ env.PROJECT_NAME }}' + --force-reinstall + --no-color + --no-deps + --only-binary=:all: + --dry-run + --report=- + --quiet + shell: bash - - name: Configure tox to run pytest under catchsegv - if: runner.os == 'Linux' - run: | - from __future__ import print_function - import os - with open(os.environ['GITHUB_ENV'], 'a') as env_file: - env_file.write('CATCHSEGV_BINARY=catchsegv\n') - shell: python + - name: >- + Pre-populate tox env: + ${{ env.TOXENV }} + run: >- + python -m + tox + --parallel auto + --parallel-live + --skip-missing-interpreters false + --installpkg '${{ + inputs.dist-type == 'binary' + && steps.wheel-file.outputs.path + || format('dist/{0}', inputs.source-tarball-name) + }}' + --notest - - name: Run the testing - run: >- - python -m - tox - --parallel auto - --parallel-live - --skip-missing-interpreters false - -qq - - name: Produce markdown test summary from JUnit - if: always() - uses: test-summary/action@v2.4 - with: - paths: .test-results/pytest/results.xml - - name: Re-run the failing tests with maximum verbosity - if: failure() - run: >- # `exit 1` makes sure that the job remains red with flaky runs - python -m - tox - --parallel auto - --parallel-live - --skip-missing-interpreters false - -vvvvv - --skip-pkg-install - -- - --no-cov -vvvvv --lf - && exit 1 - shell: bash - - name: Send coverage data to Codecov - uses: codecov/codecov-action@v5.1.2 - with: - files: .test-results/pytest/cov.xml - flags: >- - CI-GHA, - OS-${{ - runner.os - }}, - VM-${{ - inputs.runner-vm-os - }}, - Py-${{ - steps.python-install.outputs.python-version - }} - token: ${{ secrets.codecov-token }} + - name: Configure tox to run pytest under catchsegv + if: runner.os == 'Linux' + run: | + from __future__ import print_function + import os + with open(os.environ['GITHUB_ENV'], 'a') as env_file: + env_file.write('CATCHSEGV_BINARY=catchsegv\n') + shell: python -... + - name: Run the testing + run: >- + python -m + tox + --parallel auto + --parallel-live + --skip-missing-interpreters false + -qq + - name: Produce markdown test summary from JUnit + if: always() + uses: test-summary/action@v2.4 + with: + paths: .test-results/pytest/results.xml + - name: Re-run the failing tests with maximum verbosity + if: failure() + run: >- # `exit 1` makes sure that the job remains red with flaky runs + python -m + tox + --parallel auto + --parallel-live + --skip-missing-interpreters false + -vvvvv + --skip-pkg-install + -- + --no-cov -vvvvv --lf + && exit 1 + shell: bash + - name: Send coverage data to Codecov + uses: codecov/codecov-action@v5.1.2 + with: + files: .test-results/pytest/cov.xml + flags: >- + CI-GHA, + OS-${{ + runner.os + }}, + VM-${{ + inputs.runner-vm-os + }}, + Py-${{ + steps.python-install.outputs.python-version + }} + token: ${{ secrets.codecov-token }} diff --git a/.gitignore b/.gitignore index 1d13e1daf..3869bca9e 100644 --- a/.gitignore +++ b/.gitignore @@ -396,3 +396,4 @@ $RECYCLE.BIN/ # setuptools-scm write_to: /src/pylibsshext/_scm_version.py +.ansible