dev-setup 1.0.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.
Files changed (30) hide show
  1. dev_setup-1.0.0/.gitignore +10 -0
  2. dev_setup-1.0.0/PKG-INFO +358 -0
  3. dev_setup-1.0.0/README.md +332 -0
  4. dev_setup-1.0.0/dev-setup +26 -0
  5. dev_setup-1.0.0/install.sh +25 -0
  6. dev_setup-1.0.0/pyproject.toml +46 -0
  7. dev_setup-1.0.0/src/dev_setup/__init__.py +1 -0
  8. dev_setup-1.0.0/src/dev_setup/__main__.py +9 -0
  9. dev_setup-1.0.0/src/dev_setup/base.py +78 -0
  10. dev_setup-1.0.0/src/dev_setup/cli.py +38 -0
  11. dev_setup-1.0.0/src/dev_setup/commands/__init__.py +0 -0
  12. dev_setup-1.0.0/src/dev_setup/commands/add_cmd.py +185 -0
  13. dev_setup-1.0.0/src/dev_setup/commands/delete_cmd.py +44 -0
  14. dev_setup-1.0.0/src/dev_setup/commands/help_cmd.py +46 -0
  15. dev_setup-1.0.0/src/dev_setup/commands/install_cmd.py +96 -0
  16. dev_setup-1.0.0/src/dev_setup/commands/list_cmd.py +57 -0
  17. dev_setup-1.0.0/src/dev_setup/commands/remove_cmd.py +50 -0
  18. dev_setup-1.0.0/src/dev_setup/generic.py +302 -0
  19. dev_setup-1.0.0/src/dev_setup/packages/__init__.py +0 -0
  20. dev_setup-1.0.0/src/dev_setup/packages/aws_cli.py +73 -0
  21. dev_setup-1.0.0/src/dev_setup/packages/docker.py +93 -0
  22. dev_setup-1.0.0/src/dev_setup/packages/htop.py +53 -0
  23. dev_setup-1.0.0/src/dev_setup/packages/nvm.py +84 -0
  24. dev_setup-1.0.0/src/dev_setup/packages/php.py +65 -0
  25. dev_setup-1.0.0/src/dev_setup/packages/saml2aws.py +100 -0
  26. dev_setup-1.0.0/src/dev_setup/packages/starship.py +65 -0
  27. dev_setup-1.0.0/src/dev_setup/packages/uv_tool.py +68 -0
  28. dev_setup-1.0.0/src/dev_setup/registry.py +78 -0
  29. dev_setup-1.0.0/src/dev_setup/ui.py +97 -0
  30. dev_setup-1.0.0/uv.lock +117 -0
@@ -0,0 +1,10 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ *.pyo
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ .pytest_cache/
9
+ .mypy_cache/
10
+ .ruff_cache/
@@ -0,0 +1,358 @@
1
+ Metadata-Version: 2.4
2
+ Name: dev-setup
3
+ Version: 1.0.0
4
+ Summary: Development environment setup CLI for Linux
5
+ Project-URL: Repository, https://github.com/thesawdawg/dev-setup-py
6
+ Author-email: Sawyer <sawyerksu@gmail.com>
7
+ License: MIT
8
+ Keywords: cli,developer,devtools,linux,setup
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: POSIX :: Linux
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Topic :: System :: Installation/Setup
20
+ Classifier: Topic :: Utilities
21
+ Requires-Python: >=3.11
22
+ Requires-Dist: click>=8.1
23
+ Requires-Dist: questionary>=2.0
24
+ Requires-Dist: rich>=13.0
25
+ Description-Content-Type: text/markdown
26
+
27
+ # dev-setup
28
+
29
+ A Python-based CLI for managing your Linux development environment. Install, remove, and track developer tools from a single command — with an interactive picker, a guided wizard for adding custom packages, and a consistent Rich terminal UI.
30
+
31
+ ---
32
+
33
+ ## How it works
34
+
35
+ The entry point is a thin bash wrapper (`./dev-setup`) that bootstraps Python automatically:
36
+
37
+ 1. Checks for `uv` on `PATH`; installs it via the official installer if missing
38
+ 2. Runs `uv run --project <dir> python -m dev_setup` — uv provisions Python 3.11+ if needed
39
+ 3. All further logic (commands, UI, installs) is pure Python
40
+
41
+ You never need to manually install Python or manage a virtualenv. The first invocation after a fresh clone may take a few seconds to resolve dependencies; every run after that is instant.
42
+
43
+ ---
44
+
45
+ ## Installation
46
+
47
+ ```bash
48
+ git clone <repo-url> ~/dev-setup-py
49
+ cd ~/dev-setup-py
50
+ bash install.sh
51
+ ```
52
+
53
+ `install.sh` creates a symlink at `~/.local/bin/dev-setup` and ensures `~/.local/bin` is on `PATH` in `~/.bashrc`. Open a new terminal (or run `source ~/.bashrc`) and you're done.
54
+
55
+ > **Note:** Installing this tool symlinks `~/.local/bin/dev-setup`, which will replace an existing bash `dev-setup` at that path if you have one.
56
+
57
+ ---
58
+
59
+ ## Commands
60
+
61
+ ### `list`
62
+
63
+ Show all available packages with their install status, type, version, and help command.
64
+
65
+ ```bash
66
+ dev-setup list # all packages
67
+ dev-setup list core # core category only
68
+ dev-setup list tools # tools category only
69
+ dev-setup list custom # custom/user-added packages only
70
+ dev-setup list --installed # only installed packages
71
+ dev-setup list --available # only packages not yet installed
72
+ ```
73
+
74
+ Output columns: status (✔/✘), package key, description, install type, version (if installed), help command.
75
+
76
+ ---
77
+
78
+ ### `install`
79
+
80
+ Install one or more packages by key, or launch an interactive multi-select picker.
81
+
82
+ ```bash
83
+ dev-setup install docker nvm # install specific packages
84
+ dev-setup install # interactive picker (Space to toggle, Enter to confirm)
85
+ ```
86
+
87
+ The interactive picker shows all available packages with their current install status and lets you select multiple at once before confirming.
88
+
89
+ ---
90
+
91
+ ### `remove`
92
+
93
+ Uninstall an installed package. Always asks for confirmation before proceeding.
94
+
95
+ ```bash
96
+ dev-setup remove htop
97
+ dev-setup uninstall htop # alias
98
+ ```
99
+
100
+ ---
101
+
102
+ ### `add`
103
+
104
+ Guided wizard to register a new custom package. Supports six install types:
105
+
106
+ | Type | What it does |
107
+ |------|-------------|
108
+ | `npm` | `npm install -g <package>` |
109
+ | `pip` | `uv tool install <package>` (falls back to `pip3 install --user`) |
110
+ | `apt` | `sudo apt-get install -y <packages>` |
111
+ | `git` | `git clone --depth=1 <url>` with optional post-clone and pre-remove commands |
112
+ | `script` | `curl -fsSL <url> \| sh` — single-URL convenience script |
113
+ | `bash` | Arbitrary multi-step bash — opens `$EDITOR` for install and remove scripts |
114
+
115
+ ```bash
116
+ dev-setup add
117
+ ```
118
+
119
+ The wizard collects type-specific fields, then prompts for a help command (e.g. `tool --help`). Packages are saved as JSON files in `~/.config/dev-setup/packages/`.
120
+
121
+ #### `bash` type
122
+
123
+ For tools like AWS CLI or saml2aws that require multiple download/extract/install steps, choose the `bash` type. The wizard opens `$EDITOR` twice — once for the install script and once for the optional remove script — with a `#!/usr/bin/env bash / set -euo pipefail` template pre-filled.
124
+
125
+ Example JSON for a `bash`-type custom package:
126
+
127
+ ```json
128
+ {
129
+ "name": "batcat",
130
+ "description": "Modern cat with syntax highlighting",
131
+ "category": "custom",
132
+ "type": "bash",
133
+ "check_cmd": "bat",
134
+ "help_cmd": "bat --help",
135
+ "install_script": "set -euo pipefail\nVER=$(curl -s https://api.github.com/repos/sharkdp/bat/releases/latest | grep tag_name | cut -d'\"' -f4 | sed 's/v//')\ncurl -fsSL \"https://github.com/sharkdp/bat/releases/download/v${VER}/bat_${VER}_amd64.deb\" -o /tmp/bat.deb\nsudo dpkg -i /tmp/bat.deb && rm /tmp/bat.deb",
136
+ "remove_script": "sudo dpkg -r bat"
137
+ }
138
+ ```
139
+
140
+ ---
141
+
142
+ ### `delete`
143
+
144
+ Remove a custom package from the registry. Built-in packages cannot be deleted.
145
+
146
+ ```bash
147
+ dev-setup delete my-tool
148
+ dev-setup rm my-tool # alias
149
+ ```
150
+
151
+ Asks for confirmation, then deletes the JSON file from `~/.config/dev-setup/packages/`.
152
+
153
+ ---
154
+
155
+ ## Built-in packages
156
+
157
+ ### Core
158
+
159
+ These are the foundation tools — install them on every machine.
160
+
161
+ | Key | Name | Description | Help |
162
+ |-----|------|-------------|------|
163
+ | `docker` | Docker | Container runtime + docker compose plugin | `docker --help` |
164
+ | `nvm` | NVM + Node LTS | Node Version Manager + latest Node LTS | `nvm help` |
165
+ | `uv` | uv | Astral Python package and project manager | `uv --help` |
166
+
167
+ ### Tools
168
+
169
+ Optional utilities you may want on some machines.
170
+
171
+ | Key | Name | Description | Help |
172
+ |-----|------|-------------|------|
173
+ | `aws` | AWS CLI | Amazon Web Services CLI v2 | `aws help` |
174
+ | `htop` | htop | Interactive process and resource monitor | `man htop` |
175
+ | `php` | PHP 8.4 | PHP 8.4 + common extensions via ondrej/php PPA | `php --help` |
176
+ | `saml2aws` | saml2aws | SAML → AWS STS credentials CLI (Versent) | `saml2aws --help` |
177
+ | `starship` | Starship | Fast, cross-shell customizable prompt | `starship --help` |
178
+
179
+ ---
180
+
181
+ ## Custom packages
182
+
183
+ Custom packages live in `~/.config/dev-setup/packages/` as JSON files. Each file is named `<key>.json`. You can create them via `dev-setup add` or write them by hand.
184
+
185
+ ### JSON fields
186
+
187
+ | Field | Required | Description |
188
+ |-------|----------|-------------|
189
+ | `name` | yes | Display name shown in `list` |
190
+ | `description` | no | Short description shown in `list` |
191
+ | `category` | no | `custom` (default), `core`, or `tools` |
192
+ | `type` | yes | `npm`, `pip`, `apt`, `git`, `script`, or `bash` |
193
+ | `check_cmd` | no | Binary name checked with `which` to detect install status |
194
+ | `help_cmd` | no | Command shown in `list` under the package entry |
195
+ | `npm_name` | npm | npm package name |
196
+ | `pip_name` | pip | PyPI package name |
197
+ | `apt_packages` | apt | Space-separated list of apt packages |
198
+ | `git_url` | git | Repository URL to clone |
199
+ | `git_install_cmd` | git | Bash command run inside the cloned repo after clone |
200
+ | `git_remove_cmd` | git | Bash command run inside the repo before deletion |
201
+ | `script_url` | script | URL passed to `curl -fsSL … \| sh` |
202
+ | `install_script` | bash | Full bash script to run on install |
203
+ | `remove_script` | bash | Full bash script to run on remove |
204
+
205
+ ### Examples
206
+
207
+ **npm package:**
208
+ ```json
209
+ {
210
+ "name": "Prettier",
211
+ "description": "Opinionated code formatter",
212
+ "type": "npm",
213
+ "npm_name": "prettier",
214
+ "check_cmd": "prettier",
215
+ "help_cmd": "prettier --help"
216
+ }
217
+ ```
218
+
219
+ **pip package:**
220
+ ```json
221
+ {
222
+ "name": "httpie",
223
+ "description": "Human-friendly HTTP client",
224
+ "type": "pip",
225
+ "pip_name": "httpie",
226
+ "check_cmd": "http",
227
+ "help_cmd": "http --help"
228
+ }
229
+ ```
230
+
231
+ **apt package:**
232
+ ```json
233
+ {
234
+ "name": "ripgrep",
235
+ "description": "Fast recursive search tool",
236
+ "type": "apt",
237
+ "apt_packages": "ripgrep",
238
+ "check_cmd": "rg",
239
+ "help_cmd": "rg --help"
240
+ }
241
+ ```
242
+
243
+ **Multi-step bash install:**
244
+ ```json
245
+ {
246
+ "name": "saml2aws (custom)",
247
+ "description": "SAML-to-AWS credential helper",
248
+ "type": "bash",
249
+ "check_cmd": "saml2aws",
250
+ "help_cmd": "saml2aws --help",
251
+ "install_script": "set -euo pipefail\nVER=$(curl -s https://api.github.com/repos/Versent/saml2aws/releases/latest | grep tag_name | cut -d'v' -f2 | cut -d'\"' -f1)\ncurl -fsSL \"https://github.com/Versent/saml2aws/releases/download/v${VER}/saml2aws_${VER}_linux_amd64.tar.gz\" | tar -xz -C /tmp\nsudo mv /tmp/saml2aws /usr/local/bin/saml2aws\nsudo chmod +x /usr/local/bin/saml2aws",
252
+ "remove_script": "sudo rm -f /usr/local/bin/saml2aws"
253
+ }
254
+ ```
255
+
256
+ ---
257
+
258
+ ## Architecture
259
+
260
+ ```
261
+ dev-setup-py/
262
+ ├── dev-setup # Bash entry point — bootstraps uv, then exec's Python
263
+ ├── install.sh # Symlinks dev-setup into ~/.local/bin
264
+ ├── pyproject.toml # Python project (hatchling, requires-python >=3.11)
265
+ └── src/
266
+ └── dev_setup/
267
+ ├── __main__.py # python -m dev_setup entry point
268
+ ├── cli.py # Click group, command registration
269
+ ├── base.py # Tool ABC, patch_bashrc / remove_bashrc_block utilities
270
+ ├── registry.py # Auto-discovers Tool subclasses via pkgutil, loads custom JSON
271
+ ├── generic.py # GenericTool — handles all 6 custom install types
272
+ ├── ui.py # Rich console helpers, questionary wrappers, styled prompts
273
+ ├── commands/
274
+ │ ├── list_cmd.py
275
+ │ ├── install_cmd.py
276
+ │ ├── remove_cmd.py
277
+ │ ├── add_cmd.py
278
+ │ └── delete_cmd.py
279
+ └── packages/ # Built-in Tool subclasses — one file per tool
280
+ ├── docker.py
281
+ ├── nvm.py
282
+ ├── uv_tool.py
283
+ ├── aws_cli.py
284
+ ├── saml2aws.py
285
+ ├── php.py
286
+ ├── starship.py
287
+ └── htop.py
288
+ ```
289
+
290
+ ### Adding a new built-in tool
291
+
292
+ Create one file in `src/dev_setup/packages/`. The registry auto-discovers any class that subclasses `Tool` and has a non-empty `key` — no registration arrays to update.
293
+
294
+ ```python
295
+ # src/dev_setup/packages/my_tool.py
296
+ import shutil, subprocess
297
+ from typing import Optional
298
+ from dev_setup.base import Tool
299
+
300
+ class MyTool(Tool):
301
+ key = "mytool"
302
+ name = "My Tool"
303
+ description = "Does something useful"
304
+ category = "tools" # "core", "tools", or "custom"
305
+ install_type = "script"
306
+ help_cmd = "mytool --help"
307
+
308
+ def is_installed(self) -> bool:
309
+ return shutil.which("mytool") is not None
310
+
311
+ def get_version(self) -> str:
312
+ r = subprocess.run(["mytool", "--version"], capture_output=True, text=True)
313
+ return r.stdout.strip() if r.returncode == 0 else ""
314
+
315
+ def install(self) -> Optional[str]:
316
+ from dev_setup import ui
317
+ with ui.spinner("Installing My Tool..."):
318
+ subprocess.run(["bash", "-c", "curl -fsSL https://example.com/install.sh | sh"],
319
+ check=True, capture_output=True)
320
+ if not self.is_installed():
321
+ raise RuntimeError("mytool binary not found after install")
322
+ return self.get_version()
323
+
324
+ def remove(self) -> None:
325
+ from dev_setup import ui
326
+ with ui.spinner("Removing My Tool..."):
327
+ subprocess.run(["sudo", "rm", "-f", "/usr/local/bin/mytool"],
328
+ check=True, capture_output=True)
329
+ ```
330
+
331
+ ### Key design decisions
332
+
333
+ - **uv owns Python provisioning.** The bash wrapper only guarantees uv is present; Python version and virtualenv management is delegated entirely to `uv run`.
334
+ - **Registry is auto-discovery.** `pkgutil.iter_modules` scans `packages/` for `Tool` subclasses — adding a built-in is a single file drop-in.
335
+ - **Custom packages are plain JSON.** No executable files in the registry; scripts are stored as strings and written to a temp file at install time, giving bash full parsing fidelity.
336
+ - **`install()` raises on failure.** Tools raise `RuntimeError` or `subprocess.CalledProcessError`; command handlers catch and report them. No `InstallResult` enum to check.
337
+ - **UI is import-isolated.** Package classes do `from dev_setup import ui` inside method bodies, keeping `is_installed()` and `get_version()` side-effect free and testable without terminal output.
338
+
339
+ ---
340
+
341
+ ## Requirements
342
+
343
+ - Debian/Ubuntu Linux (apt-based; htop and php fall back to yum/dnf/pacman for detection)
344
+ - `curl` (for bootstrapping uv and install scripts)
345
+ - `git` (for `git`-type custom packages)
346
+ - `sudo` access (Docker, PHP, saml2aws, htop installs write to system paths)
347
+
348
+ Python 3.11+ is provisioned automatically by uv if not already present.
349
+
350
+ ---
351
+
352
+ ## Dependencies
353
+
354
+ | Package | Version | Purpose |
355
+ |---------|---------|---------|
356
+ | `click` | ≥ 8.1 | CLI command dispatch, `--help` generation, editor integration |
357
+ | `rich` | ≥ 13.0 | Terminal UI — panels, tables, spinners, styled text |
358
+ | `questionary` | ≥ 2.0 | Interactive prompts — multi-select, confirm, text input |
@@ -0,0 +1,332 @@
1
+ # dev-setup
2
+
3
+ A Python-based CLI for managing your Linux development environment. Install, remove, and track developer tools from a single command — with an interactive picker, a guided wizard for adding custom packages, and a consistent Rich terminal UI.
4
+
5
+ ---
6
+
7
+ ## How it works
8
+
9
+ The entry point is a thin bash wrapper (`./dev-setup`) that bootstraps Python automatically:
10
+
11
+ 1. Checks for `uv` on `PATH`; installs it via the official installer if missing
12
+ 2. Runs `uv run --project <dir> python -m dev_setup` — uv provisions Python 3.11+ if needed
13
+ 3. All further logic (commands, UI, installs) is pure Python
14
+
15
+ You never need to manually install Python or manage a virtualenv. The first invocation after a fresh clone may take a few seconds to resolve dependencies; every run after that is instant.
16
+
17
+ ---
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ git clone <repo-url> ~/dev-setup-py
23
+ cd ~/dev-setup-py
24
+ bash install.sh
25
+ ```
26
+
27
+ `install.sh` creates a symlink at `~/.local/bin/dev-setup` and ensures `~/.local/bin` is on `PATH` in `~/.bashrc`. Open a new terminal (or run `source ~/.bashrc`) and you're done.
28
+
29
+ > **Note:** Installing this tool symlinks `~/.local/bin/dev-setup`, which will replace an existing bash `dev-setup` at that path if you have one.
30
+
31
+ ---
32
+
33
+ ## Commands
34
+
35
+ ### `list`
36
+
37
+ Show all available packages with their install status, type, version, and help command.
38
+
39
+ ```bash
40
+ dev-setup list # all packages
41
+ dev-setup list core # core category only
42
+ dev-setup list tools # tools category only
43
+ dev-setup list custom # custom/user-added packages only
44
+ dev-setup list --installed # only installed packages
45
+ dev-setup list --available # only packages not yet installed
46
+ ```
47
+
48
+ Output columns: status (✔/✘), package key, description, install type, version (if installed), help command.
49
+
50
+ ---
51
+
52
+ ### `install`
53
+
54
+ Install one or more packages by key, or launch an interactive multi-select picker.
55
+
56
+ ```bash
57
+ dev-setup install docker nvm # install specific packages
58
+ dev-setup install # interactive picker (Space to toggle, Enter to confirm)
59
+ ```
60
+
61
+ The interactive picker shows all available packages with their current install status and lets you select multiple at once before confirming.
62
+
63
+ ---
64
+
65
+ ### `remove`
66
+
67
+ Uninstall an installed package. Always asks for confirmation before proceeding.
68
+
69
+ ```bash
70
+ dev-setup remove htop
71
+ dev-setup uninstall htop # alias
72
+ ```
73
+
74
+ ---
75
+
76
+ ### `add`
77
+
78
+ Guided wizard to register a new custom package. Supports six install types:
79
+
80
+ | Type | What it does |
81
+ |------|-------------|
82
+ | `npm` | `npm install -g <package>` |
83
+ | `pip` | `uv tool install <package>` (falls back to `pip3 install --user`) |
84
+ | `apt` | `sudo apt-get install -y <packages>` |
85
+ | `git` | `git clone --depth=1 <url>` with optional post-clone and pre-remove commands |
86
+ | `script` | `curl -fsSL <url> \| sh` — single-URL convenience script |
87
+ | `bash` | Arbitrary multi-step bash — opens `$EDITOR` for install and remove scripts |
88
+
89
+ ```bash
90
+ dev-setup add
91
+ ```
92
+
93
+ The wizard collects type-specific fields, then prompts for a help command (e.g. `tool --help`). Packages are saved as JSON files in `~/.config/dev-setup/packages/`.
94
+
95
+ #### `bash` type
96
+
97
+ For tools like AWS CLI or saml2aws that require multiple download/extract/install steps, choose the `bash` type. The wizard opens `$EDITOR` twice — once for the install script and once for the optional remove script — with a `#!/usr/bin/env bash / set -euo pipefail` template pre-filled.
98
+
99
+ Example JSON for a `bash`-type custom package:
100
+
101
+ ```json
102
+ {
103
+ "name": "batcat",
104
+ "description": "Modern cat with syntax highlighting",
105
+ "category": "custom",
106
+ "type": "bash",
107
+ "check_cmd": "bat",
108
+ "help_cmd": "bat --help",
109
+ "install_script": "set -euo pipefail\nVER=$(curl -s https://api.github.com/repos/sharkdp/bat/releases/latest | grep tag_name | cut -d'\"' -f4 | sed 's/v//')\ncurl -fsSL \"https://github.com/sharkdp/bat/releases/download/v${VER}/bat_${VER}_amd64.deb\" -o /tmp/bat.deb\nsudo dpkg -i /tmp/bat.deb && rm /tmp/bat.deb",
110
+ "remove_script": "sudo dpkg -r bat"
111
+ }
112
+ ```
113
+
114
+ ---
115
+
116
+ ### `delete`
117
+
118
+ Remove a custom package from the registry. Built-in packages cannot be deleted.
119
+
120
+ ```bash
121
+ dev-setup delete my-tool
122
+ dev-setup rm my-tool # alias
123
+ ```
124
+
125
+ Asks for confirmation, then deletes the JSON file from `~/.config/dev-setup/packages/`.
126
+
127
+ ---
128
+
129
+ ## Built-in packages
130
+
131
+ ### Core
132
+
133
+ These are the foundation tools — install them on every machine.
134
+
135
+ | Key | Name | Description | Help |
136
+ |-----|------|-------------|------|
137
+ | `docker` | Docker | Container runtime + docker compose plugin | `docker --help` |
138
+ | `nvm` | NVM + Node LTS | Node Version Manager + latest Node LTS | `nvm help` |
139
+ | `uv` | uv | Astral Python package and project manager | `uv --help` |
140
+
141
+ ### Tools
142
+
143
+ Optional utilities you may want on some machines.
144
+
145
+ | Key | Name | Description | Help |
146
+ |-----|------|-------------|------|
147
+ | `aws` | AWS CLI | Amazon Web Services CLI v2 | `aws help` |
148
+ | `htop` | htop | Interactive process and resource monitor | `man htop` |
149
+ | `php` | PHP 8.4 | PHP 8.4 + common extensions via ondrej/php PPA | `php --help` |
150
+ | `saml2aws` | saml2aws | SAML → AWS STS credentials CLI (Versent) | `saml2aws --help` |
151
+ | `starship` | Starship | Fast, cross-shell customizable prompt | `starship --help` |
152
+
153
+ ---
154
+
155
+ ## Custom packages
156
+
157
+ Custom packages live in `~/.config/dev-setup/packages/` as JSON files. Each file is named `<key>.json`. You can create them via `dev-setup add` or write them by hand.
158
+
159
+ ### JSON fields
160
+
161
+ | Field | Required | Description |
162
+ |-------|----------|-------------|
163
+ | `name` | yes | Display name shown in `list` |
164
+ | `description` | no | Short description shown in `list` |
165
+ | `category` | no | `custom` (default), `core`, or `tools` |
166
+ | `type` | yes | `npm`, `pip`, `apt`, `git`, `script`, or `bash` |
167
+ | `check_cmd` | no | Binary name checked with `which` to detect install status |
168
+ | `help_cmd` | no | Command shown in `list` under the package entry |
169
+ | `npm_name` | npm | npm package name |
170
+ | `pip_name` | pip | PyPI package name |
171
+ | `apt_packages` | apt | Space-separated list of apt packages |
172
+ | `git_url` | git | Repository URL to clone |
173
+ | `git_install_cmd` | git | Bash command run inside the cloned repo after clone |
174
+ | `git_remove_cmd` | git | Bash command run inside the repo before deletion |
175
+ | `script_url` | script | URL passed to `curl -fsSL … \| sh` |
176
+ | `install_script` | bash | Full bash script to run on install |
177
+ | `remove_script` | bash | Full bash script to run on remove |
178
+
179
+ ### Examples
180
+
181
+ **npm package:**
182
+ ```json
183
+ {
184
+ "name": "Prettier",
185
+ "description": "Opinionated code formatter",
186
+ "type": "npm",
187
+ "npm_name": "prettier",
188
+ "check_cmd": "prettier",
189
+ "help_cmd": "prettier --help"
190
+ }
191
+ ```
192
+
193
+ **pip package:**
194
+ ```json
195
+ {
196
+ "name": "httpie",
197
+ "description": "Human-friendly HTTP client",
198
+ "type": "pip",
199
+ "pip_name": "httpie",
200
+ "check_cmd": "http",
201
+ "help_cmd": "http --help"
202
+ }
203
+ ```
204
+
205
+ **apt package:**
206
+ ```json
207
+ {
208
+ "name": "ripgrep",
209
+ "description": "Fast recursive search tool",
210
+ "type": "apt",
211
+ "apt_packages": "ripgrep",
212
+ "check_cmd": "rg",
213
+ "help_cmd": "rg --help"
214
+ }
215
+ ```
216
+
217
+ **Multi-step bash install:**
218
+ ```json
219
+ {
220
+ "name": "saml2aws (custom)",
221
+ "description": "SAML-to-AWS credential helper",
222
+ "type": "bash",
223
+ "check_cmd": "saml2aws",
224
+ "help_cmd": "saml2aws --help",
225
+ "install_script": "set -euo pipefail\nVER=$(curl -s https://api.github.com/repos/Versent/saml2aws/releases/latest | grep tag_name | cut -d'v' -f2 | cut -d'\"' -f1)\ncurl -fsSL \"https://github.com/Versent/saml2aws/releases/download/v${VER}/saml2aws_${VER}_linux_amd64.tar.gz\" | tar -xz -C /tmp\nsudo mv /tmp/saml2aws /usr/local/bin/saml2aws\nsudo chmod +x /usr/local/bin/saml2aws",
226
+ "remove_script": "sudo rm -f /usr/local/bin/saml2aws"
227
+ }
228
+ ```
229
+
230
+ ---
231
+
232
+ ## Architecture
233
+
234
+ ```
235
+ dev-setup-py/
236
+ ├── dev-setup # Bash entry point — bootstraps uv, then exec's Python
237
+ ├── install.sh # Symlinks dev-setup into ~/.local/bin
238
+ ├── pyproject.toml # Python project (hatchling, requires-python >=3.11)
239
+ └── src/
240
+ └── dev_setup/
241
+ ├── __main__.py # python -m dev_setup entry point
242
+ ├── cli.py # Click group, command registration
243
+ ├── base.py # Tool ABC, patch_bashrc / remove_bashrc_block utilities
244
+ ├── registry.py # Auto-discovers Tool subclasses via pkgutil, loads custom JSON
245
+ ├── generic.py # GenericTool — handles all 6 custom install types
246
+ ├── ui.py # Rich console helpers, questionary wrappers, styled prompts
247
+ ├── commands/
248
+ │ ├── list_cmd.py
249
+ │ ├── install_cmd.py
250
+ │ ├── remove_cmd.py
251
+ │ ├── add_cmd.py
252
+ │ └── delete_cmd.py
253
+ └── packages/ # Built-in Tool subclasses — one file per tool
254
+ ├── docker.py
255
+ ├── nvm.py
256
+ ├── uv_tool.py
257
+ ├── aws_cli.py
258
+ ├── saml2aws.py
259
+ ├── php.py
260
+ ├── starship.py
261
+ └── htop.py
262
+ ```
263
+
264
+ ### Adding a new built-in tool
265
+
266
+ Create one file in `src/dev_setup/packages/`. The registry auto-discovers any class that subclasses `Tool` and has a non-empty `key` — no registration arrays to update.
267
+
268
+ ```python
269
+ # src/dev_setup/packages/my_tool.py
270
+ import shutil, subprocess
271
+ from typing import Optional
272
+ from dev_setup.base import Tool
273
+
274
+ class MyTool(Tool):
275
+ key = "mytool"
276
+ name = "My Tool"
277
+ description = "Does something useful"
278
+ category = "tools" # "core", "tools", or "custom"
279
+ install_type = "script"
280
+ help_cmd = "mytool --help"
281
+
282
+ def is_installed(self) -> bool:
283
+ return shutil.which("mytool") is not None
284
+
285
+ def get_version(self) -> str:
286
+ r = subprocess.run(["mytool", "--version"], capture_output=True, text=True)
287
+ return r.stdout.strip() if r.returncode == 0 else ""
288
+
289
+ def install(self) -> Optional[str]:
290
+ from dev_setup import ui
291
+ with ui.spinner("Installing My Tool..."):
292
+ subprocess.run(["bash", "-c", "curl -fsSL https://example.com/install.sh | sh"],
293
+ check=True, capture_output=True)
294
+ if not self.is_installed():
295
+ raise RuntimeError("mytool binary not found after install")
296
+ return self.get_version()
297
+
298
+ def remove(self) -> None:
299
+ from dev_setup import ui
300
+ with ui.spinner("Removing My Tool..."):
301
+ subprocess.run(["sudo", "rm", "-f", "/usr/local/bin/mytool"],
302
+ check=True, capture_output=True)
303
+ ```
304
+
305
+ ### Key design decisions
306
+
307
+ - **uv owns Python provisioning.** The bash wrapper only guarantees uv is present; Python version and virtualenv management is delegated entirely to `uv run`.
308
+ - **Registry is auto-discovery.** `pkgutil.iter_modules` scans `packages/` for `Tool` subclasses — adding a built-in is a single file drop-in.
309
+ - **Custom packages are plain JSON.** No executable files in the registry; scripts are stored as strings and written to a temp file at install time, giving bash full parsing fidelity.
310
+ - **`install()` raises on failure.** Tools raise `RuntimeError` or `subprocess.CalledProcessError`; command handlers catch and report them. No `InstallResult` enum to check.
311
+ - **UI is import-isolated.** Package classes do `from dev_setup import ui` inside method bodies, keeping `is_installed()` and `get_version()` side-effect free and testable without terminal output.
312
+
313
+ ---
314
+
315
+ ## Requirements
316
+
317
+ - Debian/Ubuntu Linux (apt-based; htop and php fall back to yum/dnf/pacman for detection)
318
+ - `curl` (for bootstrapping uv and install scripts)
319
+ - `git` (for `git`-type custom packages)
320
+ - `sudo` access (Docker, PHP, saml2aws, htop installs write to system paths)
321
+
322
+ Python 3.11+ is provisioned automatically by uv if not already present.
323
+
324
+ ---
325
+
326
+ ## Dependencies
327
+
328
+ | Package | Version | Purpose |
329
+ |---------|---------|---------|
330
+ | `click` | ≥ 8.1 | CLI command dispatch, `--help` generation, editor integration |
331
+ | `rich` | ≥ 13.0 | Terminal UI — panels, tables, spinners, styled text |
332
+ | `questionary` | ≥ 2.0 | Interactive prompts — multi-select, confirm, text input |