dev-setup 1.0.0__tar.gz → 1.2.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.
- dev_setup-1.2.0/.claude/settings.local.json +9 -0
- dev_setup-1.2.0/CHANGELOG.md +42 -0
- {dev_setup-1.0.0 → dev_setup-1.2.0}/PKG-INFO +59 -21
- {dev_setup-1.0.0 → dev_setup-1.2.0}/README.md +58 -20
- dev_setup-1.2.0/app-design-output/recommendations.md +100 -0
- dev_setup-1.2.0/dev/Dockerfile +38 -0
- dev_setup-1.2.0/dev/Makefile +19 -0
- dev_setup-1.2.0/dev/docker-compose.yml +14 -0
- dev_setup-1.2.0/dev-setup +66 -0
- dev_setup-1.2.0/install.sh +67 -0
- {dev_setup-1.0.0 → dev_setup-1.2.0}/pyproject.toml +11 -3
- dev_setup-1.2.0/src/dev_setup/__init__.py +6 -0
- {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/base.py +18 -0
- {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/cli.py +2 -0
- {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/commands/add_cmd.py +5 -4
- {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/commands/delete_cmd.py +1 -6
- dev_setup-1.2.0/src/dev_setup/commands/docs_cmd.py +34 -0
- {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/commands/help_cmd.py +1 -0
- {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/commands/install_cmd.py +5 -1
- {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/commands/list_cmd.py +2 -1
- {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/commands/remove_cmd.py +5 -1
- {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/generic.py +30 -28
- {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/packages/aws_cli.py +3 -10
- {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/packages/docker.py +3 -10
- dev_setup-1.2.0/src/dev_setup/packages/gh.py +53 -0
- dev_setup-1.2.0/src/dev_setup/packages/go.py +107 -0
- {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/packages/htop.py +3 -10
- dev_setup-1.2.0/src/dev_setup/packages/java.py +42 -0
- dev_setup-1.2.0/src/dev_setup/packages/mkcert.py +71 -0
- {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/packages/nvm.py +1 -0
- dev_setup-1.2.0/src/dev_setup/packages/ollama.py +53 -0
- {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/packages/php.py +3 -9
- dev_setup-1.2.0/src/dev_setup/packages/pi_agent.py +62 -0
- dev_setup-1.2.0/src/dev_setup/packages/ruby.py +114 -0
- {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/packages/saml2aws.py +3 -11
- {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/packages/starship.py +3 -10
- {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/packages/uv_tool.py +3 -9
- {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/registry.py +16 -2
- {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/ui.py +2 -1
- dev_setup-1.2.0/uv.lock +510 -0
- dev_setup-1.0.0/dev-setup +0 -26
- dev_setup-1.0.0/install.sh +0 -25
- dev_setup-1.0.0/src/dev_setup/__init__.py +0 -1
- dev_setup-1.0.0/uv.lock +0 -117
- {dev_setup-1.0.0 → dev_setup-1.2.0}/.gitignore +0 -0
- {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/__main__.py +0 -0
- {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/commands/__init__.py +0 -0
- {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/packages/__init__.py +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
## v1.2.0 (2026-06-19)
|
|
2
|
+
|
|
3
|
+
### Feat
|
|
4
|
+
|
|
5
|
+
- add docker-compose.yml to dev/ sandbox
|
|
6
|
+
- add dev/ sandbox with Dockerfile for local install testing
|
|
7
|
+
- add docs command to open tool documentation in the browser
|
|
8
|
+
- add Go, Java, and Ruby builtin tools under languages category
|
|
9
|
+
- add gh, mkcert, ollama, and pi-coding-agent builtin tools
|
|
10
|
+
- add --verbose/-v flag to stream install/remove output
|
|
11
|
+
|
|
12
|
+
### Docs
|
|
13
|
+
|
|
14
|
+
- add Prerequisites section to README
|
|
15
|
+
|
|
16
|
+
### Chore
|
|
17
|
+
|
|
18
|
+
- add commitizen dev dep and unify version source
|
|
19
|
+
|
|
20
|
+
## v1.1.0 (2026-06-18)
|
|
21
|
+
|
|
22
|
+
### Feat
|
|
23
|
+
|
|
24
|
+
- add aws-cli and saml2aws built-in packages
|
|
25
|
+
- add bash type for multi-step custom installs
|
|
26
|
+
- add help_cmd field to all tools, display in list output
|
|
27
|
+
- enhance project metadata in pyproject.toml
|
|
28
|
+
|
|
29
|
+
### Refactor
|
|
30
|
+
|
|
31
|
+
- improve maintainability and extensibility for new tool additions
|
|
32
|
+
- switch from uv-run to pip/pipx install pattern
|
|
33
|
+
|
|
34
|
+
### Docs
|
|
35
|
+
|
|
36
|
+
- add comprehensive README
|
|
37
|
+
|
|
38
|
+
## v1.0.0 (2026-06-17)
|
|
39
|
+
|
|
40
|
+
### Feat
|
|
41
|
+
|
|
42
|
+
- initial Python implementation of dev-setup CLI
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dev-setup
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Development environment setup CLI for Linux
|
|
5
5
|
Project-URL: Repository, https://github.com/thesawdawg/dev-setup-py
|
|
6
6
|
Author-email: Sawyer <sawyerksu@gmail.com>
|
|
@@ -30,29 +30,78 @@ A Python-based CLI for managing your Linux development environment. Install, rem
|
|
|
30
30
|
|
|
31
31
|
---
|
|
32
32
|
|
|
33
|
-
##
|
|
33
|
+
## Prerequisites
|
|
34
|
+
|
|
35
|
+
| Requirement | Notes |
|
|
36
|
+
|-------------|-------|
|
|
37
|
+
| **OS** | Ubuntu 20.04+ or Debian 11+ (amd64) |
|
|
38
|
+
| **Python** | 3.11 or later |
|
|
39
|
+
| **curl** | Used by script-based installers (Docker, NVM, uv, etc.) |
|
|
40
|
+
| **sudo** | Required for tools that write to system paths (`/usr/local/bin`, apt packages) |
|
|
41
|
+
| **ca-certificates** | For HTTPS downloads — present on most systems by default |
|
|
34
42
|
|
|
35
|
-
|
|
43
|
+
These are available on any standard Ubuntu/Debian install. On a fresh minimal image, run:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
sudo apt-get install -y python3 python3-pip curl ca-certificates sudo
|
|
47
|
+
```
|
|
36
48
|
|
|
37
|
-
|
|
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
|
|
49
|
+
**Optional** — only needed when using specific install types:
|
|
40
50
|
|
|
41
|
-
|
|
51
|
+
| Requirement | When |
|
|
52
|
+
|-------------|------|
|
|
53
|
+
| `git` | `git`-type custom packages (`dev-setup add` → git) |
|
|
54
|
+
| `node` / `npm` | `npm`-type custom packages |
|
|
55
|
+
| `uv` | Running from source via `./dev-setup` (auto-installed if missing) |
|
|
42
56
|
|
|
43
57
|
---
|
|
44
58
|
|
|
45
59
|
## Installation
|
|
46
60
|
|
|
61
|
+
### From PyPI (recommended)
|
|
62
|
+
|
|
63
|
+
The simplest install — no git clone required, Python 3.11+ is the only prerequisite:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# pipx gives the tool its own isolated environment (preferred)
|
|
67
|
+
pipx install dev-setup
|
|
68
|
+
|
|
69
|
+
# or plain pip
|
|
70
|
+
pip install dev-setup
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
After install, `dev-setup` is available as a command. Run `dev-setup --help` to verify.
|
|
74
|
+
|
|
75
|
+
### From source (development)
|
|
76
|
+
|
|
47
77
|
```bash
|
|
48
78
|
git clone <repo-url> ~/dev-setup-py
|
|
49
79
|
cd ~/dev-setup-py
|
|
50
|
-
bash install.sh
|
|
80
|
+
bash install.sh # installs from PyPI via pipx or pip
|
|
51
81
|
```
|
|
52
82
|
|
|
53
|
-
|
|
83
|
+
Or to run directly from the cloned repo without installing:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
./dev-setup list # creates a .venv on first run, then stays fast
|
|
87
|
+
```
|
|
54
88
|
|
|
55
|
-
|
|
89
|
+
The `./dev-setup` bash script requires Python 3.11+ and creates a local `.venv` automatically. On Debian/Ubuntu, if `python3-venv` is not installed, it falls back to `uv venv` if uv is available.
|
|
90
|
+
|
|
91
|
+
For editable development installs:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
pip install -e .
|
|
95
|
+
dev-setup list
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## How it works
|
|
101
|
+
|
|
102
|
+
When installed from PyPI (via `pip` or `pipx`), the `dev-setup` command is a standard Python entry point — Python is the only runtime dependency. The `[project.scripts]` entry in `pyproject.toml` maps `dev-setup` directly to `dev_setup.__main__:main`.
|
|
103
|
+
|
|
104
|
+
The bash `./dev-setup` script in the repo is a convenience runner for the git-clone workflow. It creates a `.venv` using `python3 -m venv` (falling back to `uv venv` on systems where `python3-venv` is a separate package) and installs the project in editable mode on first run.
|
|
56
105
|
|
|
57
106
|
---
|
|
58
107
|
|
|
@@ -338,17 +387,6 @@ class MyTool(Tool):
|
|
|
338
387
|
|
|
339
388
|
---
|
|
340
389
|
|
|
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
390
|
## Dependencies
|
|
353
391
|
|
|
354
392
|
| Package | Version | Purpose |
|
|
@@ -4,29 +4,78 @@ A Python-based CLI for managing your Linux development environment. Install, rem
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
| Requirement | Notes |
|
|
10
|
+
|-------------|-------|
|
|
11
|
+
| **OS** | Ubuntu 20.04+ or Debian 11+ (amd64) |
|
|
12
|
+
| **Python** | 3.11 or later |
|
|
13
|
+
| **curl** | Used by script-based installers (Docker, NVM, uv, etc.) |
|
|
14
|
+
| **sudo** | Required for tools that write to system paths (`/usr/local/bin`, apt packages) |
|
|
15
|
+
| **ca-certificates** | For HTTPS downloads — present on most systems by default |
|
|
8
16
|
|
|
9
|
-
|
|
17
|
+
These are available on any standard Ubuntu/Debian install. On a fresh minimal image, run:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
sudo apt-get install -y python3 python3-pip curl ca-certificates sudo
|
|
21
|
+
```
|
|
10
22
|
|
|
11
|
-
|
|
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
|
|
23
|
+
**Optional** — only needed when using specific install types:
|
|
14
24
|
|
|
15
|
-
|
|
25
|
+
| Requirement | When |
|
|
26
|
+
|-------------|------|
|
|
27
|
+
| `git` | `git`-type custom packages (`dev-setup add` → git) |
|
|
28
|
+
| `node` / `npm` | `npm`-type custom packages |
|
|
29
|
+
| `uv` | Running from source via `./dev-setup` (auto-installed if missing) |
|
|
16
30
|
|
|
17
31
|
---
|
|
18
32
|
|
|
19
33
|
## Installation
|
|
20
34
|
|
|
35
|
+
### From PyPI (recommended)
|
|
36
|
+
|
|
37
|
+
The simplest install — no git clone required, Python 3.11+ is the only prerequisite:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# pipx gives the tool its own isolated environment (preferred)
|
|
41
|
+
pipx install dev-setup
|
|
42
|
+
|
|
43
|
+
# or plain pip
|
|
44
|
+
pip install dev-setup
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
After install, `dev-setup` is available as a command. Run `dev-setup --help` to verify.
|
|
48
|
+
|
|
49
|
+
### From source (development)
|
|
50
|
+
|
|
21
51
|
```bash
|
|
22
52
|
git clone <repo-url> ~/dev-setup-py
|
|
23
53
|
cd ~/dev-setup-py
|
|
24
|
-
bash install.sh
|
|
54
|
+
bash install.sh # installs from PyPI via pipx or pip
|
|
25
55
|
```
|
|
26
56
|
|
|
27
|
-
|
|
57
|
+
Or to run directly from the cloned repo without installing:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
./dev-setup list # creates a .venv on first run, then stays fast
|
|
61
|
+
```
|
|
28
62
|
|
|
29
|
-
|
|
63
|
+
The `./dev-setup` bash script requires Python 3.11+ and creates a local `.venv` automatically. On Debian/Ubuntu, if `python3-venv` is not installed, it falls back to `uv venv` if uv is available.
|
|
64
|
+
|
|
65
|
+
For editable development installs:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pip install -e .
|
|
69
|
+
dev-setup list
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## How it works
|
|
75
|
+
|
|
76
|
+
When installed from PyPI (via `pip` or `pipx`), the `dev-setup` command is a standard Python entry point — Python is the only runtime dependency. The `[project.scripts]` entry in `pyproject.toml` maps `dev-setup` directly to `dev_setup.__main__:main`.
|
|
77
|
+
|
|
78
|
+
The bash `./dev-setup` script in the repo is a convenience runner for the git-clone workflow. It creates a `.venv` using `python3 -m venv` (falling back to `uv venv` on systems where `python3-venv` is a separate package) and installs the project in editable mode on first run.
|
|
30
79
|
|
|
31
80
|
---
|
|
32
81
|
|
|
@@ -312,17 +361,6 @@ class MyTool(Tool):
|
|
|
312
361
|
|
|
313
362
|
---
|
|
314
363
|
|
|
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
364
|
## Dependencies
|
|
327
365
|
|
|
328
366
|
| Package | Version | Purpose |
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# dev-setup — Maintainability Review
|
|
2
|
+
|
|
3
|
+
_Generated 2026-06-18_
|
|
4
|
+
|
|
5
|
+
## Goal
|
|
6
|
+
Lower the cost of adding new tools to the core PyPI package build.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Findings
|
|
11
|
+
|
|
12
|
+
### 1. Builtin boilerplate: every package file duplicates the same `is_installed` / `get_version` logic [HIGH]
|
|
13
|
+
|
|
14
|
+
Seven of eight builtin package files implement the same two-liner:
|
|
15
|
+
```python
|
|
16
|
+
def is_installed(self) -> bool:
|
|
17
|
+
return shutil.which(self.key) is not None
|
|
18
|
+
|
|
19
|
+
def get_version(self) -> str:
|
|
20
|
+
r = subprocess.run([self.key, "--version"], capture_output=True, text=True)
|
|
21
|
+
return r.stdout.strip().splitlines()[0] if r.returncode == 0 else ""
|
|
22
|
+
```
|
|
23
|
+
Adding a new tool means copy-pasting this pattern. Errors diverge silently (`aws_cli` and `saml2aws` check `stderr` fallback; `docker` skips `splitlines()`).
|
|
24
|
+
|
|
25
|
+
**Fix:** Add `WhichTool(Tool)` to `base.py` — a shared mixin that provides these two methods. Each builtin that passes `shutil.which` inherits from `WhichTool` instead of `Tool` and only overrides what's truly unique (install/remove logic). `NvmTool` keeps its own `is_installed()` since it checks a file path, not `which`.
|
|
26
|
+
|
|
27
|
+
**Effort:** Low. Touches 7 files, each loses 8–12 lines.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
### 2. Hardcoded category list in `list_cmd.py` silently drops new tools [HIGH]
|
|
32
|
+
|
|
33
|
+
`list_cmd.py:34` iterates `("core", "tools", "custom")`. A builtin with any other `category` value is collected into `by_cat` but never rendered. Verified: a tool with `category = "cloud"` is invisible to `dev-setup list`.
|
|
34
|
+
|
|
35
|
+
**Fix:** Derive iteration order dynamically using a priority dict — `core=0`, `custom=999`, all others sorted by name in between.
|
|
36
|
+
|
|
37
|
+
**Effort:** 4-line change.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
### 3. `add_cmd` and `delete_cmd` mutate private registry state directly [MEDIUM]
|
|
42
|
+
|
|
43
|
+
`add_cmd.py:120-123` and `delete_cmd.py:39-42` both reach into `registry._registry` and `registry._order` directly. Any internal registry refactor (e.g., ordering strategy change) silently breaks these commands.
|
|
44
|
+
|
|
45
|
+
**Fix:** Add public `register(tool)` and `deregister(key)` functions to `registry.py`. Commands use those; internals stay internal.
|
|
46
|
+
|
|
47
|
+
**Effort:** Low.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
### 4. Version is defined in two places and is out of sync [MEDIUM]
|
|
52
|
+
|
|
53
|
+
`src/dev_setup/__init__.py` says `1.0.0`; `pyproject.toml` says `1.1.0`. Additionally, `ui.py:64` hardcodes the string `"v1.0.0"` in `print_banner()`.
|
|
54
|
+
|
|
55
|
+
**Fix:** Single source of truth: update `__init__.py` to `1.1.0`, and have `print_banner()` read from `from dev_setup import __version__`.
|
|
56
|
+
|
|
57
|
+
_Note: for a fully automated solution, `pyproject.toml` can be set to `dynamic = ["version"]` with `[tool.hatch.version] path = "src/dev_setup/__init__.py"`, eliminating the need to update two files on release. That's a separate change._
|
|
58
|
+
|
|
59
|
+
**Effort:** 2 lines.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
### 5. `CUSTOM_DIR` / `_CUSTOM_DIR` duplicated in `generic.py` [LOW]
|
|
64
|
+
|
|
65
|
+
`generic.py` exports both `CUSTOM_DIR` and `_CUSTOM_DIR` (identical). `registry.py` imports the underscore form. This is confusing — two names for the same path.
|
|
66
|
+
|
|
67
|
+
**Fix:** Remove `_CUSTOM_DIR`, update `registry.py` to import `CUSTOM_DIR`.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
### 6. `import shutil as _shutil` inside `GenericTool.remove()` shadows a top-level import [LOW]
|
|
72
|
+
|
|
73
|
+
`generic.py` imports `shutil` at the top of the file, then re-imports it as `_shutil` inside the `git` branch of `remove()`. The local shadowing is unnecessary.
|
|
74
|
+
|
|
75
|
+
**Fix:** Use the module-level `shutil` directly.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## What "adding a new builtin" looks like after fixes
|
|
80
|
+
|
|
81
|
+
Before (current): ~50 lines per file including copy-pasted `is_installed`, `get_version`, and `shutil`/`subprocess` imports.
|
|
82
|
+
|
|
83
|
+
After: ~30 lines. A simple tool like `htop` becomes:
|
|
84
|
+
```python
|
|
85
|
+
class HtopTool(WhichTool):
|
|
86
|
+
key = "htop"
|
|
87
|
+
name = "htop"
|
|
88
|
+
description = "Interactive process and resource monitor"
|
|
89
|
+
category = "tools"
|
|
90
|
+
install_type = "apt"
|
|
91
|
+
help_cmd = "man htop"
|
|
92
|
+
|
|
93
|
+
def install(self) -> Optional[str]:
|
|
94
|
+
... # only the unique install logic
|
|
95
|
+
|
|
96
|
+
def remove(self) -> None:
|
|
97
|
+
... # only the unique remove logic
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
New tools with unusual categories won't vanish from `list`. New categories appear automatically without touching `list_cmd.py`.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
FROM ubuntu:24.04
|
|
2
|
+
|
|
3
|
+
ENV DEBIAN_FRONTEND=noninteractive \
|
|
4
|
+
TZ=UTC \
|
|
5
|
+
LANG=C.UTF-8
|
|
6
|
+
|
|
7
|
+
# System packages needed to bootstrap dev-setup and run its installers
|
|
8
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
9
|
+
python3.12 \
|
|
10
|
+
python3.12-venv \
|
|
11
|
+
python3-pip \
|
|
12
|
+
curl \
|
|
13
|
+
git \
|
|
14
|
+
sudo \
|
|
15
|
+
ca-certificates \
|
|
16
|
+
gnupg \
|
|
17
|
+
lsb-release \
|
|
18
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
19
|
+
|
|
20
|
+
# Non-root user with passwordless sudo — mirrors a real developer workstation
|
|
21
|
+
RUN useradd -m -s /bin/bash developer \
|
|
22
|
+
&& echo "developer ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
|
|
23
|
+
|
|
24
|
+
USER developer
|
|
25
|
+
WORKDIR /home/developer
|
|
26
|
+
|
|
27
|
+
# Install the pre-built wheel (built by 'make build' before docker build runs)
|
|
28
|
+
COPY --chown=developer:developer dist/dev_setup-*.whl /tmp/
|
|
29
|
+
|
|
30
|
+
RUN pip install --user --break-system-packages /tmp/dev_setup-*.whl \
|
|
31
|
+
&& rm /tmp/dev_setup-*.whl
|
|
32
|
+
|
|
33
|
+
ENV PATH="/home/developer/.local/bin:$PATH"
|
|
34
|
+
|
|
35
|
+
# Pre-create config dir so the tool has somewhere to write on first run
|
|
36
|
+
RUN mkdir -p /home/developer/.config/dev-setup/packages
|
|
37
|
+
|
|
38
|
+
CMD ["/bin/bash", "--login"]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
ROOT := ..
|
|
2
|
+
|
|
3
|
+
.PHONY: build run shell clean
|
|
4
|
+
|
|
5
|
+
# Build a fresh wheel from source, then build the Docker image.
|
|
6
|
+
# Clean dist first so the glob in the Dockerfile matches exactly one file.
|
|
7
|
+
build:
|
|
8
|
+
cd $(ROOT) && rm -f dist/*.whl dist/*.tar.gz && uv build
|
|
9
|
+
docker compose build
|
|
10
|
+
|
|
11
|
+
# Drop into an interactive shell as the 'developer' user
|
|
12
|
+
run: build
|
|
13
|
+
docker compose run --rm sandbox
|
|
14
|
+
|
|
15
|
+
shell: run
|
|
16
|
+
|
|
17
|
+
# Remove the built image and named volume
|
|
18
|
+
clean:
|
|
19
|
+
docker compose down --rmi local --volumes 2>/dev/null || true
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
services:
|
|
2
|
+
sandbox:
|
|
3
|
+
build:
|
|
4
|
+
context: ..
|
|
5
|
+
dockerfile: dev/Dockerfile
|
|
6
|
+
image: dev-setup-sandbox
|
|
7
|
+
stdin_open: true
|
|
8
|
+
tty: true
|
|
9
|
+
volumes:
|
|
10
|
+
# Persist custom packages added via 'dev-setup add' across container runs
|
|
11
|
+
- dev-setup-config:/home/developer/.config/dev-setup
|
|
12
|
+
|
|
13
|
+
volumes:
|
|
14
|
+
dev-setup-config:
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Local runner for development / git-clone installs.
|
|
3
|
+
# For production: pip install dev-setup or pipx install dev-setup
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
DEVSETUP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
+
VENV="$DEVSETUP_DIR/.venv"
|
|
8
|
+
|
|
9
|
+
# Common locations where pip/pipx/uv install binaries
|
|
10
|
+
export PATH="$HOME/.local/bin:$HOME/.cargo/bin:$PATH"
|
|
11
|
+
|
|
12
|
+
_find_python() {
|
|
13
|
+
local py
|
|
14
|
+
for py in python3.13 python3.12 python3.11 python3; do
|
|
15
|
+
if command -v "$py" &>/dev/null; then
|
|
16
|
+
if "$py" -c "import sys; exit(0 if sys.version_info >= (3,11) else 1)" 2>/dev/null; then
|
|
17
|
+
echo "$py"
|
|
18
|
+
return 0
|
|
19
|
+
fi
|
|
20
|
+
fi
|
|
21
|
+
done
|
|
22
|
+
echo " ✖ Python 3.11+ is required." >&2
|
|
23
|
+
echo " Install from https://www.python.org/downloads/ or via your package manager." >&2
|
|
24
|
+
exit 1
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
_setup_venv() {
|
|
28
|
+
local python="$1"
|
|
29
|
+
echo " ❯ Creating virtual environment..."
|
|
30
|
+
|
|
31
|
+
if "$python" -m venv "$VENV" &>/dev/null; then
|
|
32
|
+
"$VENV/bin/pip" install --quiet -e "$DEVSETUP_DIR"
|
|
33
|
+
echo " ✔ Ready"
|
|
34
|
+
return 0
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# python3 -m venv may leave a partial directory behind on failure — clean it up
|
|
38
|
+
rm -rf "$VENV"
|
|
39
|
+
|
|
40
|
+
# Debian/Ubuntu ships python3 without python3-venv by default.
|
|
41
|
+
# uv can create venvs with a bundled pip, bypassing ensurepip entirely.
|
|
42
|
+
if command -v uv &>/dev/null; then
|
|
43
|
+
echo " ❯ python3-venv unavailable — falling back to uv venv..."
|
|
44
|
+
uv venv "$VENV" --quiet
|
|
45
|
+
VIRTUAL_ENV="$VENV" uv pip install -e "$DEVSETUP_DIR" --quiet
|
|
46
|
+
echo " ✔ Ready"
|
|
47
|
+
return 0
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
echo " ✖ Could not create virtual environment." >&2
|
|
51
|
+
echo "" >&2
|
|
52
|
+
echo " On Debian/Ubuntu, install the missing package:" >&2
|
|
53
|
+
echo " sudo apt install python3-venv" >&2
|
|
54
|
+
echo "" >&2
|
|
55
|
+
echo " Or install the tool directly (no venv needed):" >&2
|
|
56
|
+
echo " pip install dev-setup or pipx install dev-setup" >&2
|
|
57
|
+
exit 1
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
PYTHON=$(_find_python)
|
|
61
|
+
|
|
62
|
+
if [ ! -f "$VENV/bin/python" ] || ! "$VENV/bin/python" -c "import dev_setup" 2>/dev/null; then
|
|
63
|
+
_setup_venv "$PYTHON"
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
exec "$VENV/bin/python" -m dev_setup "$@"
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Installs dev-setup from PyPI using pipx (preferred) or pip.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
PACKAGE="dev-setup"
|
|
6
|
+
|
|
7
|
+
_find_python() {
|
|
8
|
+
local py
|
|
9
|
+
for py in python3.13 python3.12 python3.11 python3; do
|
|
10
|
+
if command -v "$py" &>/dev/null; then
|
|
11
|
+
if "$py" -c "import sys; exit(0 if sys.version_info >= (3,11) else 1)" 2>/dev/null; then
|
|
12
|
+
echo "$py"
|
|
13
|
+
return 0
|
|
14
|
+
fi
|
|
15
|
+
fi
|
|
16
|
+
done
|
|
17
|
+
echo " ✖ Python 3.11+ is required." >&2
|
|
18
|
+
echo " Install from https://www.python.org/downloads/ or via your package manager." >&2
|
|
19
|
+
exit 1
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
_ensure_local_bin_on_path() {
|
|
23
|
+
local dir="$HOME/.local/bin"
|
|
24
|
+
local bashrc="$HOME/.bashrc"
|
|
25
|
+
if ! grep -qF '.local/bin' "$bashrc" 2>/dev/null; then
|
|
26
|
+
printf '\n# dev-setup\nexport PATH="%s:$PATH"\n' "$dir" >> "$bashrc"
|
|
27
|
+
echo " ✔ Added $dir to PATH in $bashrc"
|
|
28
|
+
NEED_RELOAD=1
|
|
29
|
+
fi
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
PYTHON=$(_find_python)
|
|
33
|
+
NEED_RELOAD=0
|
|
34
|
+
echo " ✔ Python: $($PYTHON --version)"
|
|
35
|
+
|
|
36
|
+
# --- pipx path (preferred: isolated env, clean uninstall) ---
|
|
37
|
+
if command -v pipx &>/dev/null || "$PYTHON" -m pipx --version &>/dev/null 2>&1; then
|
|
38
|
+
PIPX=$(command -v pipx 2>/dev/null || echo "$PYTHON -m pipx")
|
|
39
|
+
echo " ❯ Installing $PACKAGE via pipx..."
|
|
40
|
+
$PIPX install "$PACKAGE"
|
|
41
|
+
echo ""
|
|
42
|
+
echo " ✔ Done. Run: dev-setup --help"
|
|
43
|
+
exit 0
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# --- bootstrap pipx then use it ---
|
|
47
|
+
if "$PYTHON" -m pip --version &>/dev/null 2>&1; then
|
|
48
|
+
echo " ❯ pipx not found — installing pipx first..."
|
|
49
|
+
"$PYTHON" -m pip install --user pipx --quiet
|
|
50
|
+
"$PYTHON" -m pipx ensurepath --quiet 2>/dev/null || true
|
|
51
|
+
_ensure_local_bin_on_path
|
|
52
|
+
|
|
53
|
+
echo " ❯ Installing $PACKAGE via pipx..."
|
|
54
|
+
"$PYTHON" -m pipx install "$PACKAGE"
|
|
55
|
+
echo ""
|
|
56
|
+
echo " ✔ Done. Run: dev-setup --help"
|
|
57
|
+
[ "$NEED_RELOAD" -eq 1 ] && echo " (run 'source ~/.bashrc' first if the command isn't found)"
|
|
58
|
+
exit 0
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# --- last resort: pip --user ---
|
|
62
|
+
echo " ❯ Installing $PACKAGE via pip..."
|
|
63
|
+
"$PYTHON" -m pip install --user "$PACKAGE" --quiet
|
|
64
|
+
_ensure_local_bin_on_path
|
|
65
|
+
echo ""
|
|
66
|
+
echo " ✔ Done. Run: dev-setup --help"
|
|
67
|
+
[ "$NEED_RELOAD" -eq 1 ] && echo " (run 'source ~/.bashrc' first if the command isn't found)"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "dev-setup"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.2.0"
|
|
4
4
|
description = "Development environment setup CLI for Linux"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -42,5 +42,13 @@ build-backend = "hatchling.build"
|
|
|
42
42
|
[tool.hatch.build.targets.wheel]
|
|
43
43
|
packages = ["src/dev_setup"]
|
|
44
44
|
|
|
45
|
-
[
|
|
46
|
-
|
|
45
|
+
[dependency-groups]
|
|
46
|
+
dev = [
|
|
47
|
+
"commitizen>=3.0",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[tool.commitizen]
|
|
51
|
+
name = "cz_conventional_commits"
|
|
52
|
+
version_provider = "pep621"
|
|
53
|
+
tag_format = "v$version"
|
|
54
|
+
update_changelog_on_bump = true
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import shutil
|
|
3
4
|
import subprocess
|
|
4
5
|
from abc import ABC, abstractmethod
|
|
5
6
|
from pathlib import Path
|
|
@@ -14,6 +15,7 @@ class Tool(ABC):
|
|
|
14
15
|
install_type: str = "unknown"
|
|
15
16
|
builtin: bool = True
|
|
16
17
|
help_cmd: str = ""
|
|
18
|
+
docs_url: str = ""
|
|
17
19
|
|
|
18
20
|
@abstractmethod
|
|
19
21
|
def is_installed(self) -> bool: ...
|
|
@@ -32,6 +34,22 @@ class Tool(ABC):
|
|
|
32
34
|
return ""
|
|
33
35
|
|
|
34
36
|
|
|
37
|
+
class WhichTool(Tool):
|
|
38
|
+
"""Base for tools whose presence is detected via PATH lookup (shutil.which)."""
|
|
39
|
+
|
|
40
|
+
def is_installed(self) -> bool:
|
|
41
|
+
return shutil.which(self.key) is not None
|
|
42
|
+
|
|
43
|
+
def get_version(self) -> str:
|
|
44
|
+
if not shutil.which(self.key):
|
|
45
|
+
return ""
|
|
46
|
+
r = subprocess.run([self.key, "--version"], capture_output=True, text=True)
|
|
47
|
+
if r.returncode != 0:
|
|
48
|
+
return ""
|
|
49
|
+
out = r.stdout.strip() or r.stderr.strip()
|
|
50
|
+
return out.splitlines()[0] if out else ""
|
|
51
|
+
|
|
52
|
+
|
|
35
53
|
def run_bash(cmd: str, **kwargs) -> subprocess.CompletedProcess:
|
|
36
54
|
return subprocess.run(["bash", "-c", cmd], **kwargs)
|
|
37
55
|
|
|
@@ -25,6 +25,7 @@ def _register_commands() -> None:
|
|
|
25
25
|
from dev_setup.commands.remove_cmd import remove_cmd
|
|
26
26
|
from dev_setup.commands.add_cmd import add_cmd
|
|
27
27
|
from dev_setup.commands.delete_cmd import delete_cmd
|
|
28
|
+
from dev_setup.commands.docs_cmd import docs_cmd
|
|
28
29
|
|
|
29
30
|
cli.add_command(list_cmd, "list")
|
|
30
31
|
cli.add_command(install_cmd, "install")
|
|
@@ -33,6 +34,7 @@ def _register_commands() -> None:
|
|
|
33
34
|
cli.add_command(add_cmd, "add")
|
|
34
35
|
cli.add_command(delete_cmd, "delete")
|
|
35
36
|
cli.add_command(delete_cmd, "rm")
|
|
37
|
+
cli.add_command(docs_cmd, "docs")
|
|
36
38
|
|
|
37
39
|
|
|
38
40
|
_register_commands()
|