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.


Prerequisites

Requirement Notes
OS Ubuntu 20.04+ or Debian 11+ (amd64)
Python 3.11 or later
curl Used by script-based installers (Docker, NVM, uv, etc.)
sudo Required for tools that write to system paths (/usr/local/bin, apt packages)
ca-certificates For HTTPS downloads — present on most systems by default

These are available on any standard Ubuntu/Debian install. On a fresh minimal image, run:

sudo apt-get install -y python3 python3-pip curl ca-certificates sudo

Optional — only needed when using specific install types:

Requirement When
git git-type custom packages (dev-setup add → git)
node / npm npm-type custom packages
uv Running from source via ./dev-setup (auto-installed if missing)

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.

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.4.0.tar.gz (59.0 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.4.0-py3-none-any.whl (37.6 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: dev_setup-1.4.0.tar.gz
  • Upload date:
  • Size: 59.0 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.4.0.tar.gz
Algorithm Hash digest
SHA256 d287c6ac21f7319b0c6adacb4e0db6cf6da3c0e486f27dfe022ac09fd4ebe8a1
MD5 dba548bbcba48451f4c271bbae8cef9a
BLAKE2b-256 d03d8db5f0dc3167e206aff153061f9aeeaa82fed113684e15068e3c5a34aeeb

See more details on using hashes here.

File details

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

File metadata

  • Download URL: dev_setup-1.4.0-py3-none-any.whl
  • Upload date:
  • Size: 37.6 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.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7c79149c54c6174bc3ffd1b28f583138d0095a9212e484633dbb0532b787ab29
MD5 0a11188a561adc5b53638cd51d75adc7
BLAKE2b-256 fbe7073532ca016676d663eb6695600be6abce9c4dbe1caf8293c98208a9f8fd

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