Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
8dba30b
feat: support running Sessions as a jobRunAsUser on macOS
andychoquette Jul 16, 2026
16d0a13
test: run POSIX cross-user impersonation tests on macOS in CI
andychoquette Jul 17, 2026
efe96e6
Merge branch 'mainline' into macos-support
andychoquette Jul 22, 2026
ea87e9c
Merge branch 'mainline' into macos-support
andychoquette Jul 27, 2026
e40eee5
fix: wait for the stdout-filter thread in test_def_via_stdout_fails_s…
andychoquette Jul 27, 2026
449baa0
Merge remote-tracking branch 'origin/mainline' into macos-support
andychoquette Jul 29, 2026
1ef2e51
Merge branch 'mainline' into macos-support
andychoquette Aug 4, 2026
843cd05
Merge branch 'mainline' into macos-support
andychoquette Aug 7, 2026
bdcffd5
test: address review feedback on the macOS cross-user test scaffolding
andychoquette Aug 10, 2026
c016ea6
fix: raise an actionable error when no interpreter is reachable on macOS
andychoquette Aug 10, 2026
90e82a3
Merge branch 'mainline' into macos-support
andychoquette Aug 10, 2026
1538c4b
ci: pin virtualenv<21 on Python 3.9 for the macOS cross-user job
andychoquette Aug 10, 2026
330877b
fix(scripts): narrow the permission grant and remove the TMPDIR override
andychoquette Aug 10, 2026
da69789
test: validate TEST_USER, split permission tests, cover sudo -i quoting
andychoquette Aug 10, 2026
ae80d91
fix: restore SIGPIPE/SIGXFSZ in the shim, harden TMPDIR creation
andychoquette Aug 10, 2026
600aedc
fix(scripts): make --cleanup-only actually remove a leftover temp root
andychoquette Aug 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .github/workflows/macos_cross_user_test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: macOS Cross-User Tests

# Runs the POSIX user-impersonation tests (which xfail when the OPENJD_TEST_SUDO_*
# environment variables are unset) on a macOS runner. This exercises the real
# `sudo -u <user> -i <python> -I -c <setsid shim>` cross-user path end to end:
# process launch as another user, new-process-group creation, signalling, and
# process-tree termination.
#
# The provisioning, the test run and the teardown all live in
# scripts/run_macos_sudo_tests.sh, so a developer can reproduce this job on their
# own Mac with one command (`hatch run cross-user-test-macos`). This job is
# deliberately a thin wrapper around that script: anything it did that the script
# does not would be something a developer cannot reproduce.
#
# This job covers ONLY the cross-user tests. The rest of the suite already runs on
# macos-latest across the same Python matrix in code_quality.yml, so re-running it
# here would duplicate that coverage.
#
# Runs on every PR rather than behind a paths filter: the cross-user path can be
# broken from more places than a file list can enumerate (session setup, tempdir
# handling, signalling), and a filtered job that misses those reads as a pass.

on:
workflow_dispatch:
pull_request:
branches: [ mainline, release ]

jobs:
macos-cross-user:
name: Python ${{ matrix.python-version }}
runs-on: macos-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
# Matches code_quality.yml: requires-python is >=3.9, and the boundary
# versions are where an interpreter-specific difference in the setsid shim
# or in sys._base_executable resolution would surface.
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14']
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install hatch
# virtualenv 21 removed virtualenv.discovery.builtin.propose_interpreters, which
# the hatch version resolvable on 3.9 still calls, so `hatch env create` fails with
# "Environment `default` is incompatible". Pin it for 3.9 only and leave 3.10+ on
# current virtualenv. Same constraint the other workflows in this repo use.
run: pip install --upgrade hatch 'virtualenv<21; python_version < "3.10"'

- name: Provision, run cross-user tests, and tear down
# --keep skips the teardown: the runner is throwaway, so leaving the
# environment in place costs nothing and keeps a failed run inspectable.
run: bash scripts/run_macos_sudo_tests.sh --keep
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,15 @@ with passwordless `sudo` by, for example, adding a rule like follows to your
host ALL=(actions) NOPASSWD: ALL
```

On MacOS, the impersonated command is launched under a small Python shim because macOS
lacks the `setsid(1)` utility. The shim runs with the base interpreter behind the Python
that is running this library (for a virtual environment, the interpreter the venv was
created from) provided that interpreter is reachable and executable by other users;
otherwise it falls back to the operating system's `/usr/bin/python3`, which resolves to a
working interpreter only when the Xcode Command Line Tools (or Xcode) are present
(`xcode-select --install`). No separate Python installation is required when the base
interpreter is usable.

#### Impersonating a User: Windows Systems

To run an impersonated Session on Windows Systems modify the "Running a Session" example
Expand Down
6 changes: 6 additions & 0 deletions hatch.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ pre-install-commands = [
sync = "pip install -r requirements-testing.txt"
test = "pytest --cov-config pyproject.toml {args}"
typing = "mypy {args:src test}"
# Cross-user (jobRunAsUser impersonation) tests. These need a provisioned
# user/group/sudoers environment, so they go through the platform's setup script
# rather than pytest alone: Linux uses a throwaway container, macOS provisions the
# host and cleans up after itself.
cross-user-test = "bash scripts/run_sudo_tests.sh {args}"
cross-user-test-macos = "bash scripts/run_macos_sudo_tests.sh {args}"
style = [
"ruff check {args:.}",
"black --check --diff {args:.}",
Expand Down
Loading
Loading