pacman-apt-wrapper 0.1.0__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Luki Hackwell ( NL )
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,102 @@
1
+ Metadata-Version: 2.4
2
+ Name: pacman-apt-wrapper
3
+ Version: 0.1.0
4
+ Summary: Pacman-style wrapper for apt-get/apt-cache/dpkg-query
5
+ Author: Pacman Apt Wrapper Contributors
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/networkluki/pacman-apt-wrapper
8
+ Project-URL: Source, https://github.com/networkluki/pacman-apt-wrapper
9
+ Project-URL: Issues, https://github.com/networkluki/pacman-apt-wrapper/issues
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: System Administrators
13
+ Classifier: Operating System :: POSIX :: Linux
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Topic :: System :: Software Distribution
16
+ Classifier: Topic :: System :: Systems Administration
17
+ Requires-Python: >=3.9
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Dynamic: license-file
21
+
22
+ # pacman-apt (A pacman-like CLI compatibility layer for linux systems)
23
+
24
+ ## Abstract
25
+ `pacman-apt` provides a small subset of Arch Linux `pacman`-style commands on
26
+ linux system by translating them into native `apt-get` / `apt-cache` / `dpkg-query`
27
+ invocations. The goal is consistent operator muscle-memory across distributions,
28
+ not to replace the underlying package manager.
29
+
30
+ ## Motivation
31
+ Operators and developers frequently work across multiple Linux distributions.
32
+ Command surface area differences (e.g. `pacman -Syu` vs `apt update && apt upgrade`)
33
+ increase cognitive load and can lead to mistakes during routine maintenance.
34
+
35
+ This project reduces that friction by offering a minimal, explicit translation layer.
36
+
37
+ ## Scope
38
+ ### Goals
39
+ - Provide a pacman-like UX for common workflows on linux system:
40
+ - `-Syu`, `-S`, `-R`, `-Rns`, `-Ss`, `-Qi`, `-Q`
41
+ - Prefer stable, script-friendly Debian tooling (`apt-get`, `apt-cache`, `dpkg-query`)
42
+ - Transparent execution: print commands before running them
43
+
44
+ ### Non-goals
45
+ - Not a package manager: no repository handling, no dependency resolution
46
+ - Not a full `pacman` implementation
47
+ - Not intended for Arch Linux (where `pacman` already exists)
48
+
49
+ ## Design Overview
50
+ `pacman-apt` parses a small flag subset and maps it to linux tooling:
51
+
52
+ | pacman-style | linux backend |
53
+ |---|---|
54
+ | `pacman -Syu` | `apt-get update && apt-get dist-upgrade -y` |
55
+ | `pacman -S <pkg...>` | `apt-get install -y <pkg...>` |
56
+ | `pacman -R <pkg...>` | `apt-get remove -y <pkg...>` |
57
+ | `pacman -Rns <pkg...>` | `apt-get purge -y <pkg...> && apt-get autoremove --purge -y` |
58
+ | `pacman -Ss <query>` | `apt-cache search <query>` |
59
+ | `pacman -Qi <pkg>` | `apt-cache show <pkg>` (metadata; see Limitations) |
60
+ | `pacman -Q` | `dpkg-query -W -f='${binary:Package}\t${Version}\n'` |
61
+
62
+ For design rationale, see `docs/design.md`.
63
+
64
+ ## Security Model
65
+ - No `shell=True`; all subprocess calls use explicit argument arrays
66
+ - Conservative privilege handling (uses `sudo` when not root)
67
+ - Commands are echoed before execution for auditability
68
+
69
+ See `docs/security.md` for the threat model and assumptions.
70
+
71
+ ## Compatibility
72
+ - Debian: stable
73
+ - Ubuntu: LTS
74
+
75
+ (Exact versions may vary; see `docs/compatibility.md`.)
76
+
77
+ ## Installation
78
+ ### Install with pip
79
+ From a local checkout:
80
+ ```bash
81
+ python -m pip install .
82
+ ```
83
+
84
+ For development, use an editable install:
85
+ ```bash
86
+ python -m pip install -e .
87
+ ```
88
+
89
+ Both pip installs create these commands in the active Python environment:
90
+ - `pacman`
91
+ - `pacman-apt`
92
+
93
+ If your Python environment's script directory is not on `PATH`, run the module
94
+ entrypoint directly instead:
95
+ ```bash
96
+ python -m pacman_apt.cli --help
97
+ ```
98
+
99
+ ### Manual (single-file)
100
+ ```bash
101
+ sudo install -m 0755 pacman /usr/local/bin/pacman
102
+ ```
@@ -0,0 +1,81 @@
1
+ # pacman-apt (A pacman-like CLI compatibility layer for linux systems)
2
+
3
+ ## Abstract
4
+ `pacman-apt` provides a small subset of Arch Linux `pacman`-style commands on
5
+ linux system by translating them into native `apt-get` / `apt-cache` / `dpkg-query`
6
+ invocations. The goal is consistent operator muscle-memory across distributions,
7
+ not to replace the underlying package manager.
8
+
9
+ ## Motivation
10
+ Operators and developers frequently work across multiple Linux distributions.
11
+ Command surface area differences (e.g. `pacman -Syu` vs `apt update && apt upgrade`)
12
+ increase cognitive load and can lead to mistakes during routine maintenance.
13
+
14
+ This project reduces that friction by offering a minimal, explicit translation layer.
15
+
16
+ ## Scope
17
+ ### Goals
18
+ - Provide a pacman-like UX for common workflows on linux system:
19
+ - `-Syu`, `-S`, `-R`, `-Rns`, `-Ss`, `-Qi`, `-Q`
20
+ - Prefer stable, script-friendly Debian tooling (`apt-get`, `apt-cache`, `dpkg-query`)
21
+ - Transparent execution: print commands before running them
22
+
23
+ ### Non-goals
24
+ - Not a package manager: no repository handling, no dependency resolution
25
+ - Not a full `pacman` implementation
26
+ - Not intended for Arch Linux (where `pacman` already exists)
27
+
28
+ ## Design Overview
29
+ `pacman-apt` parses a small flag subset and maps it to linux tooling:
30
+
31
+ | pacman-style | linux backend |
32
+ |---|---|
33
+ | `pacman -Syu` | `apt-get update && apt-get dist-upgrade -y` |
34
+ | `pacman -S <pkg...>` | `apt-get install -y <pkg...>` |
35
+ | `pacman -R <pkg...>` | `apt-get remove -y <pkg...>` |
36
+ | `pacman -Rns <pkg...>` | `apt-get purge -y <pkg...> && apt-get autoremove --purge -y` |
37
+ | `pacman -Ss <query>` | `apt-cache search <query>` |
38
+ | `pacman -Qi <pkg>` | `apt-cache show <pkg>` (metadata; see Limitations) |
39
+ | `pacman -Q` | `dpkg-query -W -f='${binary:Package}\t${Version}\n'` |
40
+
41
+ For design rationale, see `docs/design.md`.
42
+
43
+ ## Security Model
44
+ - No `shell=True`; all subprocess calls use explicit argument arrays
45
+ - Conservative privilege handling (uses `sudo` when not root)
46
+ - Commands are echoed before execution for auditability
47
+
48
+ See `docs/security.md` for the threat model and assumptions.
49
+
50
+ ## Compatibility
51
+ - Debian: stable
52
+ - Ubuntu: LTS
53
+
54
+ (Exact versions may vary; see `docs/compatibility.md`.)
55
+
56
+ ## Installation
57
+ ### Install with pip
58
+ From a local checkout:
59
+ ```bash
60
+ python -m pip install .
61
+ ```
62
+
63
+ For development, use an editable install:
64
+ ```bash
65
+ python -m pip install -e .
66
+ ```
67
+
68
+ Both pip installs create these commands in the active Python environment:
69
+ - `pacman`
70
+ - `pacman-apt`
71
+
72
+ If your Python environment's script directory is not on `PATH`, run the module
73
+ entrypoint directly instead:
74
+ ```bash
75
+ python -m pacman_apt.cli --help
76
+ ```
77
+
78
+ ### Manual (single-file)
79
+ ```bash
80
+ sudo install -m 0755 pacman /usr/local/bin/pacman
81
+ ```
@@ -0,0 +1,38 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pacman-apt-wrapper"
7
+ version = "0.1.0"
8
+ description = "Pacman-style wrapper for apt-get/apt-cache/dpkg-query"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Pacman Apt Wrapper Contributors" }]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Environment :: Console",
17
+ "Intended Audience :: System Administrators",
18
+ "Operating System :: POSIX :: Linux",
19
+ "Programming Language :: Python :: 3",
20
+ "Topic :: System :: Software Distribution",
21
+ "Topic :: System :: Systems Administration",
22
+ ]
23
+
24
+ [project.urls]
25
+ Homepage = "https://github.com/networkluki/pacman-apt-wrapper"
26
+ Source = "https://github.com/networkluki/pacman-apt-wrapper"
27
+ Issues = "https://github.com/networkluki/pacman-apt-wrapper/issues"
28
+
29
+ [project.scripts]
30
+ pacman-apt = "pacman_apt.cli:main"
31
+ pacman = "pacman_apt.cli:main"
32
+
33
+ [tool.setuptools.packages.find]
34
+ where = ["src"]
35
+
36
+ [tool.pytest.ini_options]
37
+ addopts = "-q"
38
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,4 @@
1
+ """Pacman-style wrapper for apt-get tooling."""
2
+
3
+ __all__ = ["__version__"]
4
+ __version__ = "0.1.0"
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ pacman (Debian/Ubuntu wrapper)
4
+
5
+ Implements a small subset of pacman-style flags by translating to:
6
+ - apt-get (stable scripting)
7
+ - apt-cache (search/show)
8
+ - dpkg-query (installed list)
9
+
10
+ Supported:
11
+ pacman -Syu
12
+ pacman -S <pkg...>
13
+ pacman -R <pkg...>
14
+ pacman -Rns <pkg...>
15
+ pacman -Ss <query...>
16
+ pacman -Qi <pkg...>
17
+ pacman -Q
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import os
23
+ import shlex
24
+ import shutil
25
+ import subprocess
26
+ import sys
27
+ from typing import List, Tuple
28
+
29
+
30
+ def die(msg: str, code: int = 2) -> None:
31
+ print(f"error: {msg}", file=sys.stderr)
32
+ raise SystemExit(code)
33
+
34
+
35
+ def ensure_tools() -> None:
36
+ for tool in ("apt-get", "apt-cache", "dpkg-query"):
37
+ if shutil.which(tool) is None:
38
+ die(f"Required tool not found in PATH: {tool}")
39
+
40
+
41
+ def split_args(argv: List[str]) -> Tuple[List[str], List[str]]:
42
+ flags: List[str] = []
43
+ rest: List[str] = []
44
+ for arg in argv:
45
+ if arg.startswith("-") and len(arg) > 1:
46
+ flags.append(arg)
47
+ else:
48
+ rest.append(arg)
49
+ return flags, rest
50
+
51
+
52
+ def has_flag(flags: List[str], needle: str) -> bool:
53
+ # Pacman combines flags like -Syu
54
+ for flag in flags:
55
+ if flag.startswith("--"):
56
+ continue
57
+ if needle in flag[1:]:
58
+ return True
59
+ return False
60
+
61
+
62
+ def as_root(cmd: List[str]) -> List[str]:
63
+ # Auto-sudo for operations that modify the system
64
+ if os.geteuid() == 0:
65
+ return cmd
66
+ if shutil.which("sudo") is None:
67
+ die("This operation requires root, and sudo is not available.")
68
+ return ["sudo", "--"] + cmd
69
+
70
+
71
+ def run(cmd: List[str]) -> int:
72
+ print("+ " + " ".join(shlex.quote(c) for c in cmd))
73
+ process = subprocess.run(cmd, check=False)
74
+ return int(process.returncode)
75
+
76
+
77
+ def main(argv: List[str] | None = None) -> int:
78
+ ensure_tools()
79
+
80
+ if argv is None:
81
+ argv = sys.argv[1:]
82
+
83
+ if not argv or argv[0] in ("-h", "--help"):
84
+ print(
85
+ "pacman (Debian/Ubuntu wrapper)\n\n"
86
+ "Examples:\n\n"
87
+ " pacman -Syu\n\n"
88
+ " pacman -S htop curl\n\n"
89
+ " pacman -R htop\n\n"
90
+ " pacman -Rns htop\n\n"
91
+ " pacman -Ss nginx\n\n"
92
+ " pacman -Qi nginx\n\n"
93
+ " pacman -Q\n"
94
+ )
95
+ return 0
96
+
97
+ flags, args = split_args(argv)
98
+
99
+ # pacman -Syu
100
+ if has_flag(flags, "S") and has_flag(flags, "y") and has_flag(flags, "u"):
101
+ cmds = [
102
+ as_root(["apt-get", "update"]),
103
+ as_root(["apt-get", "dist-upgrade", "-y"]),
104
+ ]
105
+ for cmd in cmds:
106
+ rc = run(cmd)
107
+ if rc != 0:
108
+ return rc
109
+ return 0
110
+
111
+ # pacman -S <pkgs...>
112
+ if has_flag(flags, "S") and args and not has_flag(flags, "u"):
113
+ return run(as_root(["apt-get", "install", "-y"] + args))
114
+
115
+ # pacman -R / -Rns <pkgs...>
116
+ if has_flag(flags, "R") and args:
117
+ purge = has_flag(flags, "n") and has_flag(flags, "s")
118
+ if purge:
119
+ # purge + autoremove --purge to mimic "remove + deps + config"
120
+ rc = run(as_root(["apt-get", "purge", "-y"] + args))
121
+ if rc != 0:
122
+ return rc
123
+ return run(as_root(["apt-get", "autoremove", "--purge", "-y"]))
124
+ return run(as_root(["apt-get", "remove", "-y"] + args))
125
+
126
+ # pacman -Ss <query...>
127
+ if has_flag(flags, "Ss"):
128
+ if not args:
129
+ die("Search requires a query (e.g. pacman -Ss nginx)")
130
+ return run(["apt-cache", "search"] + args)
131
+
132
+ # pacman -Qi <pkg...>
133
+ if has_flag(flags, "Qi"):
134
+ if not args:
135
+ die("Info requires package name(s) (e.g. pacman -Qi nginx)")
136
+ # apt-cache show shows package metadata (not strictly installed-state)
137
+ for pkg in args:
138
+ rc = run(["apt-cache", "show", pkg])
139
+ if rc != 0:
140
+ return rc
141
+ return 0
142
+
143
+ # pacman -Q (list installed)
144
+ if has_flag(flags, "Q") and not args:
145
+ return run(["dpkg-query", "-W", "-f=${binary:Package}\t${Version}\\n"])
146
+
147
+ die(f"Unsupported flags/args: flags={flags!r} args={args!r}")
148
+ return 2
149
+
150
+
151
+ if __name__ == "__main__":
152
+ raise SystemExit(main())
@@ -0,0 +1,102 @@
1
+ Metadata-Version: 2.4
2
+ Name: pacman-apt-wrapper
3
+ Version: 0.1.0
4
+ Summary: Pacman-style wrapper for apt-get/apt-cache/dpkg-query
5
+ Author: Pacman Apt Wrapper Contributors
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/networkluki/pacman-apt-wrapper
8
+ Project-URL: Source, https://github.com/networkluki/pacman-apt-wrapper
9
+ Project-URL: Issues, https://github.com/networkluki/pacman-apt-wrapper/issues
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: System Administrators
13
+ Classifier: Operating System :: POSIX :: Linux
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Topic :: System :: Software Distribution
16
+ Classifier: Topic :: System :: Systems Administration
17
+ Requires-Python: >=3.9
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Dynamic: license-file
21
+
22
+ # pacman-apt (A pacman-like CLI compatibility layer for linux systems)
23
+
24
+ ## Abstract
25
+ `pacman-apt` provides a small subset of Arch Linux `pacman`-style commands on
26
+ linux system by translating them into native `apt-get` / `apt-cache` / `dpkg-query`
27
+ invocations. The goal is consistent operator muscle-memory across distributions,
28
+ not to replace the underlying package manager.
29
+
30
+ ## Motivation
31
+ Operators and developers frequently work across multiple Linux distributions.
32
+ Command surface area differences (e.g. `pacman -Syu` vs `apt update && apt upgrade`)
33
+ increase cognitive load and can lead to mistakes during routine maintenance.
34
+
35
+ This project reduces that friction by offering a minimal, explicit translation layer.
36
+
37
+ ## Scope
38
+ ### Goals
39
+ - Provide a pacman-like UX for common workflows on linux system:
40
+ - `-Syu`, `-S`, `-R`, `-Rns`, `-Ss`, `-Qi`, `-Q`
41
+ - Prefer stable, script-friendly Debian tooling (`apt-get`, `apt-cache`, `dpkg-query`)
42
+ - Transparent execution: print commands before running them
43
+
44
+ ### Non-goals
45
+ - Not a package manager: no repository handling, no dependency resolution
46
+ - Not a full `pacman` implementation
47
+ - Not intended for Arch Linux (where `pacman` already exists)
48
+
49
+ ## Design Overview
50
+ `pacman-apt` parses a small flag subset and maps it to linux tooling:
51
+
52
+ | pacman-style | linux backend |
53
+ |---|---|
54
+ | `pacman -Syu` | `apt-get update && apt-get dist-upgrade -y` |
55
+ | `pacman -S <pkg...>` | `apt-get install -y <pkg...>` |
56
+ | `pacman -R <pkg...>` | `apt-get remove -y <pkg...>` |
57
+ | `pacman -Rns <pkg...>` | `apt-get purge -y <pkg...> && apt-get autoremove --purge -y` |
58
+ | `pacman -Ss <query>` | `apt-cache search <query>` |
59
+ | `pacman -Qi <pkg>` | `apt-cache show <pkg>` (metadata; see Limitations) |
60
+ | `pacman -Q` | `dpkg-query -W -f='${binary:Package}\t${Version}\n'` |
61
+
62
+ For design rationale, see `docs/design.md`.
63
+
64
+ ## Security Model
65
+ - No `shell=True`; all subprocess calls use explicit argument arrays
66
+ - Conservative privilege handling (uses `sudo` when not root)
67
+ - Commands are echoed before execution for auditability
68
+
69
+ See `docs/security.md` for the threat model and assumptions.
70
+
71
+ ## Compatibility
72
+ - Debian: stable
73
+ - Ubuntu: LTS
74
+
75
+ (Exact versions may vary; see `docs/compatibility.md`.)
76
+
77
+ ## Installation
78
+ ### Install with pip
79
+ From a local checkout:
80
+ ```bash
81
+ python -m pip install .
82
+ ```
83
+
84
+ For development, use an editable install:
85
+ ```bash
86
+ python -m pip install -e .
87
+ ```
88
+
89
+ Both pip installs create these commands in the active Python environment:
90
+ - `pacman`
91
+ - `pacman-apt`
92
+
93
+ If your Python environment's script directory is not on `PATH`, run the module
94
+ entrypoint directly instead:
95
+ ```bash
96
+ python -m pacman_apt.cli --help
97
+ ```
98
+
99
+ ### Manual (single-file)
100
+ ```bash
101
+ sudo install -m 0755 pacman /usr/local/bin/pacman
102
+ ```
@@ -0,0 +1,12 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/pacman_apt/__init__.py
5
+ src/pacman_apt/cli.py
6
+ src/pacman_apt_wrapper.egg-info/PKG-INFO
7
+ src/pacman_apt_wrapper.egg-info/SOURCES.txt
8
+ src/pacman_apt_wrapper.egg-info/dependency_links.txt
9
+ src/pacman_apt_wrapper.egg-info/entry_points.txt
10
+ src/pacman_apt_wrapper.egg-info/top_level.txt
11
+ tests/test_cli.py
12
+ tests/test_packaging.py
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ pacman = pacman_apt.cli:main
3
+ pacman-apt = pacman_apt.cli:main
@@ -0,0 +1,62 @@
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+ from pathlib import Path
5
+
6
+ PROJECT_ROOT = Path(__file__).resolve().parents[1]
7
+ sys.path.insert(0, str(PROJECT_ROOT / "src"))
8
+
9
+ import pacman_apt.cli as cli # noqa: E402
10
+
11
+
12
+ def test_split_args_separates_flags_and_args():
13
+ flags, args = cli.split_args(["-Syu", "htop", "-R", "curl"])
14
+ assert flags == ["-Syu", "-R"]
15
+ assert args == ["htop", "curl"]
16
+
17
+
18
+ def test_has_flag_combined():
19
+ assert cli.has_flag(["-Syu"], "S") is True
20
+ assert cli.has_flag(["-Syu"], "y") is True
21
+ assert cli.has_flag(["-Syu"], "u") is True
22
+ assert cli.has_flag(["-Syu"], "R") is False
23
+
24
+
25
+ def test_main_help_prints_usage(monkeypatch, capsys):
26
+ monkeypatch.setattr(cli, "ensure_tools", lambda: None)
27
+ exit_code = cli.main(["--help"])
28
+ captured = capsys.readouterr()
29
+ assert exit_code == 0
30
+ assert "pacman (Debian/Ubuntu wrapper)" in captured.out
31
+
32
+
33
+ def test_main_search_requires_query(monkeypatch):
34
+ monkeypatch.setattr(cli, "ensure_tools", lambda: None)
35
+ errors = {}
36
+
37
+ def fake_die(msg: str, code: int = 2) -> None:
38
+ errors["msg"] = msg
39
+ errors["code"] = code
40
+ raise SystemExit(code)
41
+
42
+ monkeypatch.setattr(cli, "die", fake_die)
43
+ try:
44
+ cli.main(["-Ss"])
45
+ except SystemExit as exc:
46
+ assert exc.code == 2
47
+ assert "Search requires a query" in errors["msg"]
48
+
49
+
50
+ def test_main_install_routes_to_apt_get(monkeypatch):
51
+ monkeypatch.setattr(cli, "ensure_tools", lambda: None)
52
+ monkeypatch.setattr(cli, "as_root", lambda cmd: cmd)
53
+ called = {}
54
+
55
+ def fake_run(cmd):
56
+ called["cmd"] = cmd
57
+ return 0
58
+
59
+ monkeypatch.setattr(cli, "run", fake_run)
60
+ exit_code = cli.main(["-S", "htop", "curl"])
61
+ assert exit_code == 0
62
+ assert called["cmd"] == ["apt-get", "install", "-y", "htop", "curl"]
@@ -0,0 +1,21 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ PROJECT_ROOT = Path(__file__).resolve().parents[1]
6
+ PYPROJECT = PROJECT_ROOT / "pyproject.toml"
7
+
8
+
9
+ def test_pip_install_exposes_pacman_command():
10
+ pyproject = PYPROJECT.read_text(encoding="utf-8")
11
+
12
+ assert "[project.scripts]" in pyproject
13
+ assert 'pacman = "pacman_apt.cli:main"' in pyproject
14
+ assert 'pacman-apt = "pacman_apt.cli:main"' in pyproject
15
+
16
+
17
+ def test_setuptools_uses_src_package_layout():
18
+ pyproject = PYPROJECT.read_text(encoding="utf-8")
19
+
20
+ assert "[tool.setuptools.packages.find]" in pyproject
21
+ assert 'where = ["src"]' in pyproject