Skip to main content

Development environment setup CLI for Linux

Project description

dev-setup

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.


Installation

From PyPI (recommended)

The simplest install — no git clone required, Python 3.11+ is the only prerequisite:

# pipx gives the tool its own isolated environment (preferred)
pipx install dev-setup

# or plain pip
pip install dev-setup

After install, dev-setup is available as a command. Run dev-setup --help to verify.

From source (development)

git clone <repo-url> ~/dev-setup-py
cd ~/dev-setup-py
bash install.sh   # installs from PyPI via pipx or pip

Or to run directly from the cloned repo without installing:

./dev-setup list   # creates a .venv on first run, then stays fast

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.

For editable development installs:

pip install -e .
dev-setup list

How it works

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.

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.


Commands

list

Show all available packages with their install status, type, version, and help command.

dev-setup list                    # all packages
dev-setup list core               # core category only
dev-setup list tools              # tools category only
dev-setup list custom             # custom/user-added packages only
dev-setup list --installed        # only installed packages
dev-setup list --available        # only packages not yet installed

Output columns: status (✔/✘), package key, description, install type, version (if installed), help command.


install

Install one or more packages by key, or launch an interactive multi-select picker.

dev-setup install docker nvm      # install specific packages
dev-setup install                 # interactive picker (Space to toggle, Enter to confirm)

The interactive picker shows all available packages with their current install status and lets you select multiple at once before confirming.


remove

Uninstall an installed package. Always asks for confirmation before proceeding.

dev-setup remove htop
dev-setup uninstall htop          # alias

add

Guided wizard to register a new custom package. Supports six install types:

Type What it does
npm npm install -g <package>
pip uv tool install <package> (falls back to pip3 install --user)
apt sudo apt-get install -y <packages>
git git clone --depth=1 <url> with optional post-clone and pre-remove commands
script curl -fsSL <url> | sh — single-URL convenience script
bash Arbitrary multi-step bash — opens $EDITOR for install and remove scripts
dev-setup add

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/.

bash type

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.

Example JSON for a bash-type custom package:

{
  "name": "batcat",
  "description": "Modern cat with syntax highlighting",
  "category": "custom",
  "type": "bash",
  "check_cmd": "bat",
  "help_cmd": "bat --help",
  "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",
  "remove_script": "sudo dpkg -r bat"
}

delete

Remove a custom package from the registry. Built-in packages cannot be deleted.

dev-setup delete my-tool
dev-setup rm my-tool              # alias

Asks for confirmation, then deletes the JSON file from ~/.config/dev-setup/packages/.


Built-in packages

Core

These are the foundation tools — install them on every machine.

Key Name Description Help
docker Docker Container runtime + docker compose plugin docker --help
nvm NVM + Node LTS Node Version Manager + latest Node LTS nvm help
uv uv Astral Python package and project manager uv --help

Tools

Optional utilities you may want on some machines.

Key Name Description Help
aws AWS CLI Amazon Web Services CLI v2 aws help
htop htop Interactive process and resource monitor man htop
php PHP 8.4 PHP 8.4 + common extensions via ondrej/php PPA php --help
saml2aws saml2aws SAML → AWS STS credentials CLI (Versent) saml2aws --help
starship Starship Fast, cross-shell customizable prompt starship --help

Custom packages

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.

JSON fields

Field Required Description
name yes Display name shown in list
description no Short description shown in list
category no custom (default), core, or tools
type yes npm, pip, apt, git, script, or bash
check_cmd no Binary name checked with which to detect install status
help_cmd no Command shown in list under the package entry
npm_name npm npm package name
pip_name pip PyPI package name
apt_packages apt Space-separated list of apt packages
git_url git Repository URL to clone
git_install_cmd git Bash command run inside the cloned repo after clone
git_remove_cmd git Bash command run inside the repo before deletion
script_url script URL passed to curl -fsSL … | sh
install_script bash Full bash script to run on install
remove_script bash Full bash script to run on remove

Examples

npm package:

{
  "name": "Prettier",
  "description": "Opinionated code formatter",
  "type": "npm",
  "npm_name": "prettier",
  "check_cmd": "prettier",
  "help_cmd": "prettier --help"
}

pip package:

{
  "name": "httpie",
  "description": "Human-friendly HTTP client",
  "type": "pip",
  "pip_name": "httpie",
  "check_cmd": "http",
  "help_cmd": "http --help"
}

apt package:

{
  "name": "ripgrep",
  "description": "Fast recursive search tool",
  "type": "apt",
  "apt_packages": "ripgrep",
  "check_cmd": "rg",
  "help_cmd": "rg --help"
}

Multi-step bash install:

{
  "name": "saml2aws (custom)",
  "description": "SAML-to-AWS credential helper",
  "type": "bash",
  "check_cmd": "saml2aws",
  "help_cmd": "saml2aws --help",
  "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",
  "remove_script": "sudo rm -f /usr/local/bin/saml2aws"
}

Architecture

dev-setup-py/
├── dev-setup              # Bash entry point — bootstraps uv, then exec's Python
├── install.sh             # Symlinks dev-setup into ~/.local/bin
├── pyproject.toml         # Python project (hatchling, requires-python >=3.11)
└── src/
    └── dev_setup/
        ├── __main__.py    # python -m dev_setup entry point
        ├── cli.py         # Click group, command registration
        ├── base.py        # Tool ABC, patch_bashrc / remove_bashrc_block utilities
        ├── registry.py    # Auto-discovers Tool subclasses via pkgutil, loads custom JSON
        ├── generic.py     # GenericTool — handles all 6 custom install types
        ├── ui.py          # Rich console helpers, questionary wrappers, styled prompts
        ├── commands/
        │   ├── list_cmd.py
        │   ├── install_cmd.py
        │   ├── remove_cmd.py
        │   ├── add_cmd.py
        │   └── delete_cmd.py
        └── packages/      # Built-in Tool subclasses — one file per tool
            ├── docker.py
            ├── nvm.py
            ├── uv_tool.py
            ├── aws_cli.py
            ├── saml2aws.py
            ├── php.py
            ├── starship.py
            └── htop.py

Adding a new built-in tool

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.

# src/dev_setup/packages/my_tool.py
import shutil, subprocess
from typing import Optional
from dev_setup.base import Tool

class MyTool(Tool):
    key         = "mytool"
    name        = "My Tool"
    description = "Does something useful"
    category    = "tools"          # "core", "tools", or "custom"
    install_type = "script"
    help_cmd    = "mytool --help"

    def is_installed(self) -> bool:
        return shutil.which("mytool") is not None

    def get_version(self) -> str:
        r = subprocess.run(["mytool", "--version"], capture_output=True, text=True)
        return r.stdout.strip() if r.returncode == 0 else ""

    def install(self) -> Optional[str]:
        from dev_setup import ui
        with ui.spinner("Installing My Tool..."):
            subprocess.run(["bash", "-c", "curl -fsSL https://example.com/install.sh | sh"],
                           check=True, capture_output=True)
        if not self.is_installed():
            raise RuntimeError("mytool binary not found after install")
        return self.get_version()

    def remove(self) -> None:
        from dev_setup import ui
        with ui.spinner("Removing My Tool..."):
            subprocess.run(["sudo", "rm", "-f", "/usr/local/bin/mytool"],
                           check=True, capture_output=True)

Key design decisions

  • uv owns Python provisioning. The bash wrapper only guarantees uv is present; Python version and virtualenv management is delegated entirely to uv run.
  • Registry is auto-discovery. pkgutil.iter_modules scans packages/ for Tool subclasses — adding a built-in is a single file drop-in.
  • 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.
  • install() raises on failure. Tools raise RuntimeError or subprocess.CalledProcessError; command handlers catch and report them. No InstallResult enum to check.
  • 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.

Requirements

  • Debian/Ubuntu Linux (apt-based; htop and php fall back to yum/dnf/pacman for detection)
  • curl (for bootstrapping uv and install scripts)
  • git (for git-type custom packages)
  • sudo access (Docker, PHP, saml2aws, htop installs write to system paths)

Python 3.11+ is provisioned automatically by uv if not already present.


Dependencies

Package Version Purpose
click ≥ 8.1 CLI command dispatch, --help generation, editor integration
rich ≥ 13.0 Terminal UI — panels, tables, spinners, styled text
questionary ≥ 2.0 Interactive prompts — multi-select, confirm, text input

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dev_setup-1.1.0.tar.gz (21.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dev_setup-1.1.0-py3-none-any.whl (28.0 kB view details)

Uploaded Python 3

File details

Details for the file dev_setup-1.1.0.tar.gz.

File metadata

  • Download URL: dev_setup-1.1.0.tar.gz
  • Upload date:
  • Size: 21.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for dev_setup-1.1.0.tar.gz
Algorithm Hash digest
SHA256 1a978a2a5ba090da127089860f60d3c6e1a06d366e6048d9d363604cf0804362
MD5 23b266f58a1c2fb761ab3b52f2744ac7
BLAKE2b-256 4dffd508afbe4df9d354052d15be321afdad2d05463a8e16519c4587457f8e12

See more details on using hashes here.

File details

Details for the file dev_setup-1.1.0-py3-none-any.whl.

File metadata

  • Download URL: dev_setup-1.1.0-py3-none-any.whl
  • Upload date:
  • Size: 28.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for dev_setup-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4f0cfa3953a7df0112aa6f2daf5539fe204279faccbc61d9187c072a9bea15fc
MD5 eb298005bdf62186d31dc29d3ccafca3
BLAKE2b-256 231144920507a025c64edd5bfb8e9fe25430fda6a4673eeba08f889f36d445ec

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page