runspec-linux-core 0.3.3__tar.gz → 0.3.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/CHANGELOG.md +18 -0
  2. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/PKG-INFO +2 -2
  3. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/pyproject.toml +2 -2
  4. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/runspec_linux_core/venvs.py +13 -6
  5. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/tests/test_venvs.py +7 -3
  6. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/.gitignore +0 -0
  7. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/runspec_linux_core/__init__.py +0 -0
  8. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/runspec_linux_core/_paths.py +0 -0
  9. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/runspec_linux_core/commands.py +0 -0
  10. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/runspec_linux_core/containers.py +0 -0
  11. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/runspec_linux_core/errors.py +0 -0
  12. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/runspec_linux_core/files.py +0 -0
  13. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/runspec_linux_core/logs.py +0 -0
  14. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/runspec_linux_core/nc.py +0 -0
  15. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/runspec_linux_core/network.py +0 -0
  16. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/runspec_linux_core/packages.py +0 -0
  17. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/runspec_linux_core/power.py +0 -0
  18. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/runspec_linux_core/security.py +0 -0
  19. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/runspec_linux_core/services.py +0 -0
  20. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/runspec_linux_core/sudoers.py +0 -0
  21. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/runspec_linux_core/system.py +0 -0
  22. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/tests/__init__.py +0 -0
  23. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/tests/test_commands.py +0 -0
  24. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/tests/test_nc_send.py +0 -0
  25. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/tests/test_network.py +0 -0
  26. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/tests/test_packages.py +0 -0
  27. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/tests/test_paths.py +0 -0
  28. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/tests/test_power.py +0 -0
  29. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/tests/test_preconditions.py +0 -0
  30. {runspec_linux_core-0.3.3 → runspec_linux_core-0.3.4}/tests/test_sudoers.py +0 -0
@@ -1,5 +1,23 @@
1
1
  # runspec-linux-core Changelog
2
2
 
3
+ ## [0.3.4] — 2026-07-07
4
+
5
+ Fix `create_venv` producing an unreadable `750` venv directory when the caller's
6
+ umask is `027` (typical under `sudo -n -u <svc>` with a PAM-set login umask).
7
+ `python -m venv` honours the current umask, so a shared service-account venv
8
+ was landing owner-only instead of the `755` a stock `python -m venv <dest>`
9
+ produces under the default `022` umask, locking every non-owner console out —
10
+ contrary to the "shared between several consoles" use case the helper is built
11
+ for.
12
+
13
+ After the venv-creation step, `create_venv` now runs
14
+ `chmod -R u=rwX,go=rX <dest>` to normalise the tree to `755` on directories and
15
+ `644` on files while preserving the `+x` on real executables in `bin/` (capital
16
+ `X` only sets execute on directories and files that already have some execute
17
+ bit). The subsequent `chmod 2775 <dest>/logs` still gets the last word on the
18
+ logs directory, and `chgrp -R <group>` (when passed) still runs. When `run_as`
19
+ is set the new chmod runs as that user too.
20
+
3
21
  ## [0.3.3] — 2026-06-22
4
22
 
5
23
  Add `commands.py` — the pure logic behind the new `runspec-linux` `which`
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: runspec-linux-core
3
- Version: 0.3.3
3
+ Version: 0.3.4
4
4
  Summary: Pure-Python Linux system-admin helpers — the importable core behind runspec-linux (no runspec dependency, no runnables)
5
5
  Requires-Python: >=3.10
6
6
  Provides-Extra: dev
7
7
  Requires-Dist: mypy; extra == 'dev'
8
8
  Requires-Dist: pytest>=8.0; extra == 'dev'
9
- Requires-Dist: ruff; extra == 'dev'
9
+ Requires-Dist: ruff==0.15.20; extra == 'dev'
@@ -4,14 +4,14 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "runspec-linux-core"
7
- version = "0.3.3"
7
+ version = "0.3.4"
8
8
  requires-python = ">=3.10"
9
9
  description = "Pure-Python Linux system-admin helpers — the importable core behind runspec-linux (no runspec dependency, no runnables)"
10
10
  dependencies = []
11
11
 
12
12
  [project.optional-dependencies]
13
13
  dev = [
14
- "ruff",
14
+ "ruff==0.15.20",
15
15
  "mypy",
16
16
  "pytest>=8.0",
17
17
  ]
@@ -92,12 +92,17 @@ def create_venv(
92
92
  ) -> dict:
93
93
  """Create a virtualenv at ``dest`` from a specific ``python`` interpreter.
94
94
 
95
- Always uses ``--symlinks``. Then creates ``<dest>/logs`` mode ``2775`` (setgid,
96
- so new audit-log files inherit the directory's group) and, if ``group`` is set,
97
- ``chgrp -R`` the venv to it — enabling a shared service-account venv that several
98
- consoles read/write. With ``run_as`` set every step runs as that user (so the
99
- venv is owned by it). Raises ValueError on a non-executable interpreter / bad
100
- name, or CommandError on a failed step.
95
+ Always uses ``--symlinks``. Then normalises the tree to ``u=rwX,go=rX`` so
96
+ directories are ``755`` and executables in ``bin/`` keep their ``+x`` matching
97
+ what a plain ``python -m venv`` produces under a default ``022`` umask, regardless
98
+ of the caller's umask (``sudo -n -u <svc>`` often lands at ``027``, which would
99
+ otherwise leave the venv at ``750`` and lock other consoles out). Then creates
100
+ ``<dest>/logs`` mode ``2775`` (setgid, so new audit-log files inherit the
101
+ directory's group) and, if ``group`` is set, ``chgrp -R`` the venv to it —
102
+ enabling a shared service-account venv that several consoles read/write. With
103
+ ``run_as`` set every step runs as that user (so the venv is owned by it). Raises
104
+ ValueError on a non-executable interpreter / bad name, or CommandError on a
105
+ failed step.
101
106
  """
102
107
  if not (os.path.isfile(python) and os.access(python, os.X_OK)):
103
108
  raise ValueError(f"not an executable file: {python!r} (pick a real interpreter, not the system default)")
@@ -114,6 +119,8 @@ def create_venv(
114
119
 
115
120
  _check(_run(_become([python, "-m", "venv", *flags, dest], run_as, become_method)), "venv creation", run_as)
116
121
 
122
+ _check(_run(_become(["chmod", "-R", "u=rwX,go=rX", dest], run_as, become_method)), "chmod venv", run_as)
123
+
117
124
  logs_dir = os.path.join(dest, "logs")
118
125
  _check(_run(_become(["mkdir", "-p", logs_dir], run_as, become_method)), "logs dir", run_as)
119
126
  if group:
@@ -69,9 +69,12 @@ def test_create_venv_uses_symlinks_and_logs(monkeypatch: pytest.MonkeyPatch) ->
69
69
  venv_cmd = calls[0]
70
70
  assert venv_cmd[:4] == [sys.executable, "-m", "venv", "--symlinks"]
71
71
  assert venv_cmd[-1] == "/opt/v1"
72
+ # chmod -R u=rwX,go=rX normalises the tree to 755-dirs/644-files regardless of
73
+ # the caller's umask (a sudo -u <svc> shell often lands at 027 → 750).
74
+ assert calls[1] == ["chmod", "-R", "u=rwX,go=rX", "/opt/v1"]
72
75
  # mkdir -p logs, then chmod 2775 logs (no group → no chgrp step).
73
- assert calls[1] == ["mkdir", "-p", "/opt/v1/logs"]
74
- assert calls[2] == ["chmod", "2775", "/opt/v1/logs"]
76
+ assert calls[2] == ["mkdir", "-p", "/opt/v1/logs"]
77
+ assert calls[3] == ["chmod", "2775", "/opt/v1/logs"]
75
78
  assert result == {
76
79
  "python": sys.executable,
77
80
  "dest": "/opt/v1",
@@ -100,7 +103,8 @@ def test_create_venv_run_as_escalates_every_step(monkeypatch: pytest.MonkeyPatch
100
103
  venvs.create_venv(sys.executable, "/opt/v1", run_as="svc", group="consoles")
101
104
  for cmd in calls:
102
105
  assert cmd[:4] == ["sudo", "-n", "-u", "svc"]
103
- # chgrp -R consoles /opt/v1 runs between mkdir and chmod.
106
+ # The umask-normalising chmod and the chgrp both run as svc too.
107
+ assert ["sudo", "-n", "-u", "svc", "chmod", "-R", "u=rwX,go=rX", "/opt/v1"] in calls
104
108
  assert ["sudo", "-n", "-u", "svc", "chgrp", "-R", "consoles", "/opt/v1"] in calls
105
109
 
106
110