runspec-linux-core 0.3.4__tar.gz → 0.3.5__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.4 → runspec_linux_core-0.3.5}/CHANGELOG.md +19 -0
  2. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/PKG-INFO +1 -1
  3. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/pyproject.toml +1 -1
  4. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/runspec_linux_core/venvs.py +14 -0
  5. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/tests/test_venvs.py +42 -0
  6. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/.gitignore +0 -0
  7. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/runspec_linux_core/__init__.py +0 -0
  8. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/runspec_linux_core/_paths.py +0 -0
  9. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/runspec_linux_core/commands.py +0 -0
  10. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/runspec_linux_core/containers.py +0 -0
  11. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/runspec_linux_core/errors.py +0 -0
  12. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/runspec_linux_core/files.py +0 -0
  13. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/runspec_linux_core/logs.py +0 -0
  14. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/runspec_linux_core/nc.py +0 -0
  15. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/runspec_linux_core/network.py +0 -0
  16. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/runspec_linux_core/packages.py +0 -0
  17. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/runspec_linux_core/power.py +0 -0
  18. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/runspec_linux_core/security.py +0 -0
  19. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/runspec_linux_core/services.py +0 -0
  20. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/runspec_linux_core/sudoers.py +0 -0
  21. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/runspec_linux_core/system.py +0 -0
  22. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/tests/__init__.py +0 -0
  23. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/tests/test_commands.py +0 -0
  24. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/tests/test_nc_send.py +0 -0
  25. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/tests/test_network.py +0 -0
  26. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/tests/test_packages.py +0 -0
  27. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/tests/test_paths.py +0 -0
  28. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/tests/test_power.py +0 -0
  29. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/tests/test_preconditions.py +0 -0
  30. {runspec_linux_core-0.3.4 → runspec_linux_core-0.3.5}/tests/test_sudoers.py +0 -0
@@ -1,5 +1,24 @@
1
1
  # runspec-linux-core Changelog
2
2
 
3
+ ## [0.3.5] — 2026-07-08
4
+
5
+ Apply the same umask-neutralising treatment to `install_into_venv` that `0.3.4`
6
+ added to `create_venv`. After the `pip install`, the venv tree is now
7
+ normalised with `chmod -R u=rwX,go=rX <venv>` so pip's new directories are
8
+ `755` regardless of the caller's umask.
9
+
10
+ The trigger was a `sudo pip install --upgrade runspec-linux` under a host
11
+ where sudo/PAM had switched to a `027` login umask: pip's new
12
+ `site-packages/runspec_linux/` and `site-packages/runspec_linux_core/`
13
+ directories landed at `750`, and `runspec local` (walking `.dist-info` via
14
+ `importlib.metadata`) stopped seeing the packs for any user but the owner.
15
+ The equivalent bug for the venv root was fixed at build time in `0.3.4`; this
16
+ release closes the same gap at upgrade time.
17
+
18
+ If `<venv>/logs` exists (created by `create_venv`), its `2775` setgid mode is
19
+ restored after the wide chmod — the `test -d` guard skips the restore for a
20
+ venv built some other way. When `run_as` is set the new steps escalate too.
21
+
3
22
  ## [0.3.4] — 2026-07-07
4
23
 
5
24
  Fix `create_venv` producing an unreadable `750` venv directory when the caller's
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: runspec-linux-core
3
- Version: 0.3.4
3
+ Version: 0.3.5
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "runspec-linux-core"
7
- version = "0.3.4"
7
+ version = "0.3.5"
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 = []
@@ -157,6 +157,14 @@ def install_into_venv(
157
157
  path to a directory with a ``pyproject.toml`` (or a wheel/sdist), installed
158
158
  editable when ``editable`` is set. At least one of ``packages``/``project`` is
159
159
  required. Raises ValueError on bad input, CommandError on a failed install.
160
+
161
+ After the install, the venv tree is normalised to ``u=rwX,go=rX`` (the same
162
+ step ``create_venv`` runs at build time) so pip's new directories are ``755``
163
+ regardless of the caller's umask — a ``sudo`` upgrade under a PAM-set ``027``
164
+ login umask otherwise lands site-packages entries at ``750`` and hides the
165
+ packs from ``runspec local`` / ``importlib.metadata`` for anyone but the
166
+ owner. If ``<venv>/logs`` exists (``create_venv`` leaves it there), its
167
+ ``2775`` setgid mode is restored after the wide chmod strips it.
160
168
  """
161
169
  from runspec_linux_core.packages import _validate_packages
162
170
 
@@ -187,6 +195,12 @@ def install_into_venv(
187
195
  cmd = _become([venv_python, "-m", "pip", "install", *opts, *target], run_as, become_method)
188
196
  result = _run(cmd)
189
197
  _check(result, "pip install", run_as)
198
+
199
+ _check(_run(_become(["chmod", "-R", "u=rwX,go=rX", venv], run_as, become_method)), "chmod venv", run_as)
200
+ logs_dir = os.path.join(venv, "logs")
201
+ if _run(_become(["test", "-d", logs_dir], run_as, become_method)).returncode == 0:
202
+ _check(_run(_become(["chmod", "2775", logs_dir], run_as, become_method)), "chmod logs", run_as)
203
+
190
204
  return {
191
205
  "venv": venv,
192
206
  "packages": names,
@@ -161,6 +161,48 @@ def test_install_into_venv_editable_project(tmp_path, monkeypatch: pytest.Monkey
161
161
  assert cmd[-2:] == ["-e", str(proj)]
162
162
 
163
163
 
164
+ def test_install_into_venv_normalises_perms(tmp_path, monkeypatch: pytest.MonkeyPatch) -> None:
165
+ """After the pip install, the venv tree is chmodded so sudo's umask can't hide
166
+ site-packages entries at 750 from `runspec local` / `importlib.metadata`."""
167
+ venv = _fake_venv(tmp_path, monkeypatch)
168
+ calls = _record(monkeypatch)
169
+
170
+ venvs.install_into_venv(venv, "httpx", upgrade=True, run_as="svc")
171
+
172
+ assert ["sudo", "-n", "-u", "svc", "chmod", "-R", "u=rwX,go=rX", venv] in calls
173
+ # logs_dir exists (the default stub returns 0 for `test -d`), so the setgid
174
+ # mode is restored after the wide chmod would have stripped it.
175
+ assert ["sudo", "-n", "-u", "svc", "test", "-d", f"{venv}/logs"] in calls
176
+ assert ["sudo", "-n", "-u", "svc", "chmod", "2775", f"{venv}/logs"] in calls
177
+ # And the chmod steps run *after* the pip install (order matters — pip
178
+ # creates the dirs we're normalising).
179
+ pip_i = next(i for i, c in enumerate(calls) if "pip" in c and "install" in c)
180
+ chmod_i = next(i for i, c in enumerate(calls) if "chmod" in c and "u=rwX,go=rX" in c)
181
+ assert chmod_i > pip_i
182
+
183
+
184
+ def test_install_into_venv_skips_logs_restore_when_absent(tmp_path, monkeypatch: pytest.MonkeyPatch) -> None:
185
+ """A venv without a logs/ dir (not built by create_venv) gets the wide chmod
186
+ but no 2775 restore — the test -d guard skips the logs step."""
187
+ venv = _fake_venv(tmp_path, monkeypatch)
188
+ calls: list[list[str]] = []
189
+
190
+ def fake_run(argv: list[str]) -> subprocess.CompletedProcess:
191
+ calls.append(argv)
192
+ # Only the `test -d <venv>/logs` probe fails; everything else succeeds.
193
+ if argv[:2] == ["test", "-d"]:
194
+ return _completed(returncode=1)
195
+ return _completed("Successfully installed foo\n")
196
+
197
+ monkeypatch.setattr(venvs, "_run", fake_run)
198
+
199
+ venvs.install_into_venv(venv, "httpx")
200
+
201
+ assert ["chmod", "-R", "u=rwX,go=rX", venv] in calls
202
+ assert ["test", "-d", f"{venv}/logs"] in calls
203
+ assert not any(c[:2] == ["chmod", "2775"] for c in calls)
204
+
205
+
164
206
  def test_install_into_venv_requires_a_target(tmp_path, monkeypatch: pytest.MonkeyPatch) -> None:
165
207
  venv = _fake_venv(tmp_path, monkeypatch)
166
208
  _record(monkeypatch)