Skip to content

Commit

Permalink
Initial Windows CI setup. (#2669)
Browse files Browse the repository at this point in the history
More work towards #2658.
  • Loading branch information
jsirois authored Feb 11, 2025
1 parent 731e966 commit eb6ee9d
Show file tree
Hide file tree
Showing 16 changed files with 692 additions and 362 deletions.
127 changes: 109 additions & 18 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,20 @@ concurrency:
# Queue on all branches and tags, but only cancel overlapping PR burns.
cancel-in-progress: ${{ github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/') }}
jobs:
org-check:
setup:
name: Check GitHub Organization
if: github.repository_owner == 'pex-tool'
runs-on: ubuntu-24.04
outputs:
pex_test_posargs: ${{ env._PEX_TEST_POS_ARGS }}
steps:
- name: Noop
run: "true"
if: false
run: |
echo "This is a dummy step that will never run."
checks:
name: tox -e format-check,lint-check,typecheck,enum-check,vendor-check,package,docs
needs: org-check
name: "Unix: tox -e format-check,lint-check,typecheck,enum-check,vendor-check,package,docs"
needs: setup
runs-on: ubuntu-24.04
steps:
- name: Checkout Pex
Expand Down Expand Up @@ -65,19 +69,16 @@ jobs:
# N.B.: The name of this job key (linux-tests) is depended on by scrips/build_cache_image.py. In
# particular, the tox-env matrix list is used to ensure the cache covers all Linux CI jobs.
linux-tests:
name: ./dtox.sh -e ${{ matrix.tox-env }} ${{ matrix.pex-test-pos-args }}
needs: org-check
name: "Linux: ./dtox.sh -e ${{ matrix.tox-env }} ${{ matrix.pex-test-pos-args }}"
needs: setup
runs-on: ubuntu-24.04
strategy:
matrix:
include:
# Unit tests:
# -----------
- tox-env: py27-pip20
- tox-env: py38-pip22_3_1
- tox-env: py311-pip23_3_2
- tox-env: py313-pip25_0_1
- tox-env: py314-pip25_0_1
- tox-env: pypy310-pip24_3_1

# Integration tests, split most into two shards:
Expand All @@ -91,11 +92,6 @@ jobs:
- tox-env: py38-pip22_3_1-integration
pex-test-pos-args: --shard 2/2

- tox-env: py311-pip23_3_2-integration
pex-test-pos-args: --shard 1/2
- tox-env: py311-pip23_3_2-integration
pex-test-pos-args: --shard 2/2

- tox-env: py313-pip25_0_1-integration
pex-test-pos-args: --shard 1/2
- tox-env: py313-pip25_0_1-integration
Expand Down Expand Up @@ -144,21 +140,24 @@ jobs:
./dtox.sh -e ${{ matrix.tox-env }} -- \
${{ env._PEX_TEST_POS_ARGS }} ${{ matrix.pex-test-pos-args }}
mac-tests:
name: tox -e ${{ matrix.tox-env }} ${{ matrix.pex-test-pos-args }}
needs: org-check
name: "Mac: tox -e ${{ matrix.tox-env }} ${{ matrix.pex-test-pos-args }}"
needs: setup
runs-on: macos-13
strategy:
matrix:
include:
- python-version: [ 3, 13 ]
tox-env: py313-pip25_0_1
cache: true
tox-env-python: python3.11
- python-version: [ 3, 13 ]
tox-env: py313-pip25_0_1-integration
cache: true
tox-env-python: python3.11
pex-test-pos-args: --shard 1/2
- python-version: [ 3, 13 ]
tox-env: py313-pip25_0_1-integration
cache: true
tox-env-python: python3.11
pex-test-pos-args: --shard 2/2
steps:
Expand All @@ -175,12 +174,14 @@ jobs:
- name: Expose Pythons
uses: pex-tool/actions/expose-pythons@c53dadd8b410bbd66480de91067e9e45d2b3af38
- name: Restore Cached Pyenv Interpreters
if: matrix.cache
id: restore-pyenv-interpreters
uses: actions/cache/restore@v4
with:
path: ${{ env._PEX_TEST_DEV_ROOT }}/pyenv
key: macos-13-${{ runner.arch }}-pex-test-dev-root-pyenv-v1
- name: Restore Cached Devpi Server
if: matrix.cache
id: restore-devpi-server
uses: actions/cache/restore@v4
with:
Expand All @@ -206,22 +207,112 @@ jobs:
${{ matrix.tox-env }} -- ${{ env._PEX_TEST_POS_ARGS }} ${{ matrix.pex-test-pos-args }}
- name: Cache Pyenv Interpreters
uses: actions/cache/save@v4
if: github.ref == 'refs/heads/main'
if: matrix.cache && github.ref == 'refs/heads/main'
with:
path: ${{ env._PEX_TEST_DEV_ROOT }}/pyenv
key: ${{ steps.restore-pyenv-interpreters.outputs.cache-primary-key }}
- name: Cache Devpi Server
uses: actions/cache/save@v4
if: github.ref == 'refs/heads/main'
if: matrix.cache && github.ref == 'refs/heads/main'
with:
path: ${{ env._PEX_TEST_DEV_ROOT }}/devpi
key: ${{ steps.restore-devpi-server.outputs.cache-primary-key }}
windows-tests:
name: "Windows: tox -e ${{ matrix.tox-env }} ${{ matrix.pex-test-pos-args }}"
needs: setup
runs-on: windows-2022
continue-on-error: true
strategy:
matrix:
include:
# N.B.: We use 3.11 to work around issues building typed-ast for the typecheck step.
# There is a Windows wheel for CPython 3.11, but no higher CPython version.
- python-version: [ 3, 11 ]
tox-env: typecheck,package,docs
- python-version: [ 3, 13 ]
tox-env: py313-pip25_0
artifact-name: unit
cache: true
pex-test-pos-args: ${{ needs.setup.outputs.pex_test_posargs }} --junit-xml ../dist/test-results/unit.xml
- python-version: [ 3, 13 ]
tox-env: py313-pip25_0-integration
artifact-name: integration-1
cache: true
pex-test-pos-args: --shard 1/2 ${{ needs.setup.outputs.pex_test_posargs }} --junit-xml ../dist/test-results/integration-1.xml
- python-version: [ 3, 13 ]
tox-env: py313-pip25_0-integration
artifact-name: integration-2
cache: true
pex-test-pos-args: --shard 2/2 ${{ needs.setup.outputs.pex_test_posargs }} --junit-xml ../dist/test-results/integration-2.xml
steps:
- name: Checkout Pex
uses: actions/checkout@v4
with:
# We need branches and tags for some ITs.
fetch-depth: 0
path: repo
- name: Setup Python ${{ join(matrix.python-version, '.') }}
uses: actions/setup-python@v5
with:
python-version: "${{ join(matrix.python-version, '.') }}"
- name: Restore Cached Devpi Server
if: matrix.cache
id: restore-devpi-server
uses: actions/cache/restore@v4
with:
path: ${{ env._PEX_TEST_DEV_ROOT }}/devpi
# We're using a key suffix / restore-keys prefix trick here to get an updatable cache.
# See: https://github.com/actions/cache/blob/main/tips-and-workarounds.md#update-a-cache
key: windows-2022-${{ runner.arch }}-${{ matrix.tox-env }}-pex-test-dev-root-devpi-v1-${{ github.run_id }}
restore-keys: windows-2022-${{ runner.arch }}-${{ matrix.tox-env }}-pex-test-dev-root-devpi-v1
# Some ITs need this for VCS URLs of the form git+ssh://[email protected]/...
- name: Setup SSH Agent
uses: webfactory/[email protected]
env:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
if: env.SSH_PRIVATE_KEY != ''
with:
ssh-private-key: ${{ env.SSH_PRIVATE_KEY }}
- name: Run Tests
uses: pex-tool/actions/run-tox@c53dadd8b410bbd66480de91067e9e45d2b3af38
with:
path: repo/tox.ini
tox-env: >-
${{ matrix.tox-env }} -- ${{ matrix.pex-test-pos-args }}
- uses: actions/upload-artifact@v4
if: always() && matrix.artifact-name
with:
name: test-results-${{ matrix.artifact-name }}
path: |
dist/test-results/
.gitignore
- name: Cache Devpi Server
uses: actions/cache/save@v4
if: matrix.cache && github.ref == 'refs/heads/main'
with:
path: ${{ env._PEX_TEST_DEV_ROOT }}/devpi
key: ${{ steps.restore-devpi-server.outputs.cache-primary-key }}
windows-reports:
needs: windows-tests
if: always()
runs-on: ubuntu-24.04
steps:
- uses: actions/download-artifact@v4
with:
merge-multiple: true
- uses: actions/upload-artifact@v4
with:
name: test-results
path: |
dist/test-results/
.gitignore
final-status:
name: Gather Final Status
needs:
- checks
- linux-tests
- mac-tests
- windows-reports
runs-on: ubuntu-24.04
steps:
- name: Check Non-Success
Expand Down
7 changes: 5 additions & 2 deletions pex/commands/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
from pex.cache import access as cache_access
from pex.common import environment_as, safe_mkdtemp, safe_open
from pex.compatibility import shlex_quote
from pex.os import LINUX
from pex.result import Error, Ok, Result
from pex.subprocess import subprocess_daemon_kwargs
from pex.typing import TYPE_CHECKING, Generic, cast
from pex.variables import ENV, Variables
from pex.version import __version__
Expand Down Expand Up @@ -57,8 +59,9 @@ def try_run_program(
):
# type: (...) -> Result
cmd = [program] + list(args)
kwargs = dict(subprocess_daemon_kwargs() if disown else {}, **kwargs)
try:
process = subprocess.Popen(cmd, preexec_fn=os.setsid if disown else None, **kwargs)
process = subprocess.Popen(cmd, **kwargs)
if not disown and process.wait() != 0:
return Error(
str(
Expand Down Expand Up @@ -91,7 +94,7 @@ def try_open_file(
url = None # type: Optional[str]
if open_program:
opener = open_program
elif "Linux" == os.uname()[0]:
elif LINUX:
opener = "xdg-open"
url = "https://www.freedesktop.org/wiki/Software/xdg-utils/"
else:
Expand Down
21 changes: 11 additions & 10 deletions pex/docs/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

from pex.cache.dirs import CacheDir
from pex.common import safe_open
from pex.os import kill
from pex.subprocess import subprocess_daemon_kwargs
from pex.typing import TYPE_CHECKING
from pex.version import __version__

Expand Down Expand Up @@ -165,25 +167,24 @@ def launch(
args=[sys.executable, "-m", http_server_module, str(port)],
env=env,
cwd=document_root,
preexec_fn=os.setsid,
bufsize=1,
stdout=fp.fileno(),
stderr=subprocess.STDOUT,
**subprocess_daemon_kwargs()
)

pidfile = Pidfile.record(server_log=log, pid=process.pid, timeout=timeout)
if not pidfile:
try:
os.kill(process.pid, signal.SIGKILL)
kill(process.pid)
except OSError as e:
if e.errno != errno.ESRCH: # No such process.
raise LaunchError(
log,
additional_msg=(
"Also failed to kill the partially launched server at pid {pid}: "
"{err}".format(pid=process.pid, err=e)
),
)
raise LaunchError(
log,
additional_msg=(
"Also failed to kill the partially launched server at pid {pid}: "
"{err}".format(pid=process.pid, err=e)
),
)
raise LaunchError(log)
return LaunchResult(server_info=pidfile.server_info, already_running=False)

Expand Down
9 changes: 6 additions & 3 deletions pex/fs/_posix.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ def _lock_api(style):
# type: (FileLockStyle.Value) -> Callable[[int, int], None]

return cast(
"Callable[[int, int], None]", fcntl.flock if style is FileLockStyle.BSD else fcntl.lockf
"Callable[[int, int], None]",
fcntl.flock if style is FileLockStyle.BSD else fcntl.lockf, # type: ignore[attr-defined]
)

@classmethod
Expand All @@ -30,7 +31,9 @@ def acquire(
):
# type: (...) -> PosixFileLock

cls._lock_api(style)(fd, fcntl.LOCK_EX if exclusive else fcntl.LOCK_SH)
cls._lock_api(style)(
fd, fcntl.LOCK_EX if exclusive else fcntl.LOCK_SH # type: ignore[attr-defined]
)
return cls(locked_fd=fd, unlock=lambda: cls.release_lock(fd, style=style))

@classmethod
Expand All @@ -41,4 +44,4 @@ def release_lock(
):
# type: (...) -> None

cls._lock_api(style)(fd, fcntl.LOCK_UN)
cls._lock_api(style)(fd, fcntl.LOCK_UN) # type: ignore[attr-defined]
Loading

0 comments on commit eb6ee9d

Please sign in to comment.