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.
Files changed (48) hide show
  1. dev_setup-1.2.0/.claude/settings.local.json +9 -0
  2. dev_setup-1.2.0/CHANGELOG.md +42 -0
  3. {dev_setup-1.0.0 → dev_setup-1.2.0}/PKG-INFO +59 -21
  4. {dev_setup-1.0.0 → dev_setup-1.2.0}/README.md +58 -20
  5. dev_setup-1.2.0/app-design-output/recommendations.md +100 -0
  6. dev_setup-1.2.0/dev/Dockerfile +38 -0
  7. dev_setup-1.2.0/dev/Makefile +19 -0
  8. dev_setup-1.2.0/dev/docker-compose.yml +14 -0
  9. dev_setup-1.2.0/dev-setup +66 -0
  10. dev_setup-1.2.0/install.sh +67 -0
  11. {dev_setup-1.0.0 → dev_setup-1.2.0}/pyproject.toml +11 -3
  12. dev_setup-1.2.0/src/dev_setup/__init__.py +6 -0
  13. {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/base.py +18 -0
  14. {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/cli.py +2 -0
  15. {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/commands/add_cmd.py +5 -4
  16. {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/commands/delete_cmd.py +1 -6
  17. dev_setup-1.2.0/src/dev_setup/commands/docs_cmd.py +34 -0
  18. {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/commands/help_cmd.py +1 -0
  19. {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/commands/install_cmd.py +5 -1
  20. {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/commands/list_cmd.py +2 -1
  21. {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/commands/remove_cmd.py +5 -1
  22. {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/generic.py +30 -28
  23. {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/packages/aws_cli.py +3 -10
  24. {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/packages/docker.py +3 -10
  25. dev_setup-1.2.0/src/dev_setup/packages/gh.py +53 -0
  26. dev_setup-1.2.0/src/dev_setup/packages/go.py +107 -0
  27. {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/packages/htop.py +3 -10
  28. dev_setup-1.2.0/src/dev_setup/packages/java.py +42 -0
  29. dev_setup-1.2.0/src/dev_setup/packages/mkcert.py +71 -0
  30. {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/packages/nvm.py +1 -0
  31. dev_setup-1.2.0/src/dev_setup/packages/ollama.py +53 -0
  32. {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/packages/php.py +3 -9
  33. dev_setup-1.2.0/src/dev_setup/packages/pi_agent.py +62 -0
  34. dev_setup-1.2.0/src/dev_setup/packages/ruby.py +114 -0
  35. {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/packages/saml2aws.py +3 -11
  36. {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/packages/starship.py +3 -10
  37. {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/packages/uv_tool.py +3 -9
  38. {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/registry.py +16 -2
  39. {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/ui.py +2 -1
  40. dev_setup-1.2.0/uv.lock +510 -0
  41. dev_setup-1.0.0/dev-setup +0 -26
  42. dev_setup-1.0.0/install.sh +0 -25
  43. dev_setup-1.0.0/src/dev_setup/__init__.py +0 -1
  44. dev_setup-1.0.0/uv.lock +0 -117
  45. {dev_setup-1.0.0 → dev_setup-1.2.0}/.gitignore +0 -0
  46. {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/__main__.py +0 -0
  47. {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/commands/__init__.py +0 -0
  48. {dev_setup-1.0.0 → dev_setup-1.2.0}/src/dev_setup/packages/__init__.py +0 -0
@@ -0,0 +1,9 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(uv run *)",
5
+ "Bash(docker build *)",
6
+ "Bash(git -C /home/sawyer/Documents/Projects/ClaudeProjects/dev-setup-py log --format=\"%H %ai %s\")"
7
+ ]
8
+ }
9
+ }
@@ -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.0.0
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
- ## How it works
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
- The entry point is a thin bash wrapper (`./dev-setup`) that bootstraps Python automatically:
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
- 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
49
+ **Optional** only needed when using specific install types:
40
50
 
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.
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
- `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.
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
- > **Note:** Installing this tool symlinks `~/.local/bin/dev-setup`, which will replace an existing bash `dev-setup` at that path if you have one.
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
- ## How it works
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
- The entry point is a thin bash wrapper (`./dev-setup`) that bootstraps Python automatically:
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
- 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
23
+ **Optional** only needed when using specific install types:
14
24
 
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.
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
- `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.
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
- > **Note:** Installing this tool symlinks `~/.local/bin/dev-setup`, which will replace an existing bash `dev-setup` at that path if you have one.
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.0.0"
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
- [tool.uv]
46
- package = true
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
@@ -0,0 +1,6 @@
1
+ from importlib.metadata import version, PackageNotFoundError
2
+
3
+ try:
4
+ __version__ = version("dev-setup")
5
+ except PackageNotFoundError:
6
+ __version__ = "dev"
@@ -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()