Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,7 @@ versions = ["16.0", "17.0", "18.0"]
[tools]
uv = ["odoo-venv", "odoo-addons-path", "pre-commit"]
npm = ["prettier", "eslint"]
system_packages = ["git", "postgresql", "pnpm"]

[[tools.script]]
url = "https://astral.sh/uv/install.sh"
name = "uv installer"
system_packages = ["git", "postgresql"]

[repos]
odoo = ["odoo", "enterprise"]
Expand Down
7 changes: 7 additions & 0 deletions bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ export PATH="$HOME/.local/bin:$PATH"

echo "=== Bootstrap trobz_local ==="

# Check not running as root
if [ "$(id -u)" -eq 0 ]; then
echo "Error: Do not run this script as root."
echo "Please run as a regular user with sudo access."
exit 1
fi

# Check if user can sudo
check_sudo() {
if ! sudo -v &>/dev/null; then
Expand Down
2 changes: 1 addition & 1 deletion docs/codebase-summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Technical overview of the `trobz_local` codebase structure, implementation patte
1. **PostgreSQL Repository Setup**: Idempotent APT repo configuration with GPG verification (Debian/Ubuntu)
2. **Scripts**: Download via wget/curl, execute with /bin/sh
3. **System Packages**: OS-aware (apt-get, pacman, brew) with platform defaults
4. **NPM Packages**: Global via pnpm install -g
4. **NPM Packages**: Global via npm install -g
5. **UV Tools**: Global via uv tool install

**Key Functions**:
Expand Down
16 changes: 6 additions & 10 deletions docs/project-overview-pdr.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ Five-stage installation pipeline:
1. **PostgreSQL Repository** (Debian/Ubuntu only): Setup PGDG APT repository with GPG verification (idempotent)
2. **Shell Scripts**: Download and execute scripts (e.g., uv installer)
3. **System Packages**: OS-aware installation via apt/pacman/brew (runs after PostgreSQL repo setup on Debian/Ubuntu)
4. **NPM Packages**: Global packages via pnpm
4. **NPM Packages**: Global packages via npm
5. **UV Tools**: Python tools via uv tool install

### 4. Virtual Environment Management (`create-venvs`)
Expand Down Expand Up @@ -138,11 +138,7 @@ versions = ["16.0", "17.0", "18.0"]
[tools]
uv = ["odoo-venv", "odoo-addons-path", "pre-commit"]
npm = ["prettier", "eslint"]
system_packages = ["git", "postgresql", "pnpm"]

[[tools.script]]
url = "https://astral.sh/uv/install.sh"
name = "uv installer"
system_packages = ["git", "postgresql"]

[repos]
odoo = ["odoo", "enterprise"]
Expand All @@ -166,7 +162,7 @@ UV tools to install globally via `uv tool install`
- **Examples**: `["odoo-venv", "pre-commit", "black[d]>=24.0"]`

#### `tools.npm` (Optional)
NPM packages to install globally via pnpm
NPM packages to install globally via npm
- **Type**: List of strings
- **Pattern**: `^(@[a-z0-9-~][a-z0-9-._~]*/)?[a-z0-9-~][a-z0-9-._~]*$`
- **Supports**: Scoped (@org/package) and unscoped packages
Expand All @@ -181,8 +177,8 @@ Shell scripts to download and execute
- **Example**:
```toml
[[tools.script]]
url = "https://astral.sh/uv/install.sh"
name = "uv installer"
url = "https://example.com/install.sh"
name = "my installer"
```

#### `tools.system_packages` (Optional)
Expand Down Expand Up @@ -310,7 +306,7 @@ Install tools from five sources in order: PostgreSQL repo, scripts, system packa
1. **PostgreSQL Repository** (Debian/Ubuntu): Setup PGDG APT repository with GPG verification
2. **Scripts**: Download and execute via `wget` or `curl`, then `sh`
3. **System Packages**: OS-aware installation (apt/pacman/brew) - runs after PostgreSQL repo on Debian/Ubuntu
4. **NPM Packages**: Global installation via `pnpm install -g`
4. **NPM Packages**: Global installation via `npm install -g`
5. **UV Tools**: Global installation via `uv tool install`

**Behavior**:
Expand Down
8 changes: 4 additions & 4 deletions docs/system-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ install-tools command
│ └─ Return success/failure boolean
│ 4. install_npm_packages()
│ ├─ Check if pnpm exists
│ └─ Parallel: run_tasks() with pnpm install -g
│ ├─ Check if npm exists
│ └─ Parallel: run_tasks() with npm install -g
│ 5. install_uv_tools()
│ └─ Parallel: run_tasks() with uv tool install
Expand Down Expand Up @@ -354,9 +354,9 @@ install_tools request
│ └─ Execute with sudo
├─ NPM Packages
│ ├─ Check: which pnpm
│ ├─ Check: which npm
│ ├─ For each package:
│ │ └─ pnpm install -g {package}
│ │ └─ npm install -g {package}
│ └─ Parallel via run_tasks()
└─ UV Tools
Expand Down
10 changes: 6 additions & 4 deletions tests/test_install_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ def mock_typer_confirm():


@patch("trobz_local.main.setup_postgresql_repo", return_value=True)
@patch("trobz_local.main.install_system_packages", return_value=True)
@patch("trobz_local.installers.shutil.which")
@patch("trobz_local.main.get_config")
@patch("trobz_local.installers.subprocess.run")
def test_install_uv_tools(mock_subprocess, mock_get_config, mock_which, _mock_pg):
def test_install_uv_tools(mock_subprocess, mock_get_config, mock_which, _mock_sys_pkgs, _mock_pg):
mock_which.return_value = "/usr/bin/uv"
mock_get_config.return_value = {
"tools": {
Expand Down Expand Up @@ -133,10 +134,11 @@ def test_install_npm_packages_npm_missing(mock_subprocess, mock_get_config, mock


@patch("trobz_local.main.setup_postgresql_repo", return_value=True)
@patch("trobz_local.main.install_system_packages", return_value=True)
@patch("trobz_local.installers.shutil.which")
@patch("trobz_local.main.get_config")
@patch("trobz_local.installers.subprocess.run")
def test_install_npm_packages_success(mock_subprocess, mock_get_config, mock_which, _mock_pg):
def test_install_npm_packages_success(mock_subprocess, mock_get_config, mock_which, _mock_sys_pkgs, _mock_pg):
mock_which.return_value = "/usr/bin/npm"
mock_get_config.return_value = {
"tools": {
Expand Down Expand Up @@ -171,7 +173,7 @@ def test_install_system_packages_arch(mock_subprocess, mock_get_config, mock_whi
"uv": [],
"npm": [],
"script": [],
"system_packages": ["pnpm"],
"system_packages": ["git"],
}
}

Expand All @@ -182,7 +184,7 @@ def test_install_system_packages_arch(mock_subprocess, mock_get_config, mock_whi
mock_subprocess.assert_called_once()
call_args = mock_subprocess.call_args[0][0]
assert "pacman" in call_args
assert "pnpm" in call_args
assert "git" in call_args


@patch("trobz_local.installers.get_os_info")
Expand Down
33 changes: 33 additions & 0 deletions trobz_local/assets/odoo_dev.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
versions = ["14.0", "15.0", "16.0", "17.0", "18.0"]

[tools]
uv = [
"odoo-venv",
"odoo-addons-path",
"pre-commit",
]

npm = [
"prettier",
]

[[tools.script]]
name = "uv"
url = "https://astral.sh/uv/install.sh"

[[tools.script]]
name = "nvm"
url = "https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh"

system_packages = []

[repos]
odoo = [
"odoo",
"enterprise",
]

oca = [
"server-tools",
"server-ux",
]
10 changes: 4 additions & 6 deletions trobz_local/installers.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,7 @@ def _run_package_install(cmd: list[str], packages: list[str]) -> bool:
return True


def install_system_packages(packages: list[str], dry_run: bool = False) -> bool:
if not packages:
return True

def install_system_packages(packages: list[str], dry_run: bool = False, install_defaults: bool = True) -> bool:
os_info = get_os_info()
system = os_info["system"]
distro = os_info["distro"]
Expand All @@ -177,8 +174,9 @@ def install_system_packages(packages: list[str], dry_run: bool = False) -> bool:

cmd, default_packages = config

# Merge user packages with default packages
all_packages = list(dict.fromkeys(default_packages + packages))
all_packages = list(dict.fromkeys((default_packages if install_defaults else []) + packages))
if not all_packages:
return True

if dry_run:
typer.echo(f"\n[System packages - would be installed via {cmd[0]}]")
Expand Down
29 changes: 18 additions & 11 deletions trobz_local/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,18 @@ def main(
help="Enable newcomer mode with confirmations and help.",
envvar="NEWCOMER_MODE",
),
yes: bool = typer.Option(
False,
"--yes",
"-y",
help="Skip all confirmations (non-interactive mode).",
),
):
"""
Hi, I'm a CLI to help you setup and manage your local environment for Odoo development.
"""
ctx.ensure_object(dict)
ctx.obj["newcomer"] = newcomer
ctx.obj["newcomer"] = newcomer and not yes
if ctx.invoked_subcommand is None:
_run_init(ctx)

Expand Down Expand Up @@ -281,7 +287,9 @@ def _build_install_message(tools_config: dict) -> str:
return msg


def _run_installers(tools_config: dict, dry_run: bool) -> tuple[list, bool]:
def _run_installers(
tools_config: dict, dry_run: bool, install_default_system_packages: bool = True
) -> tuple[list, bool]:
all_results = []
any_failed = False

Expand All @@ -303,14 +311,12 @@ def _run_installers(tools_config: dict, dry_run: bool) -> tuple[list, bool]:
if not dry_run:
setup_postgresql_repo()

if tools_config.get("system_packages"):
success = install_system_packages(tools_config["system_packages"], dry_run)
if not success:
any_failed = True

all_results.append(
TaskResult(name="system-packages", success=False, message="System package installation failed")
)
success = install_system_packages(tools_config.get("system_packages", []), dry_run, install_default_system_packages)
if not success:
any_failed = True
all_results.append(
TaskResult(name="system-packages", success=False, message="System package installation failed")
)

if tools_config.get("npm"):
results = install_npm_packages(tools_config["npm"], dry_run)
Expand All @@ -331,6 +337,7 @@ def _run_installers(tools_config: dict, dry_run: bool) -> tuple[list, bool]:
def install_tools(
ctx: typer.Context,
dry_run: bool = typer.Option(False, "--dry-run", help="Show what would be installed without executing."),
install_default_system_packages: bool = typer.Option(True, help="Install default OS system packages."),
):
"""
Install tools using uv tool based on config.
Expand All @@ -353,7 +360,7 @@ def install_tools(
msg = _build_install_message(tools_config)
confirm_step(ctx, msg, "install-tools")

all_results, any_failed = _run_installers(tools_config, dry_run)
all_results, any_failed = _run_installers(tools_config, dry_run, install_default_system_packages)

if not dry_run:
if any_failed:
Expand Down
36 changes: 2 additions & 34 deletions trobz_local/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import platform
import re
import shutil
from importlib.resources import files
from pathlib import Path

import git
Expand Down Expand Up @@ -162,40 +163,7 @@ def get_uv_path():


def show_config_instructions():
content = """versions = ["14.0", "15.0", "16.0", "17.0", "18.0"]

[tools]
uv = [
"odoo-venv",
"odoo-addons-path",
"pre-commit",
]

npm = [
"prettier",
]

[[tools.script]]
name = "uv"
url = "https://astral.sh/uv/install.sh"

[[tools.script]]
name = "nvm"
url = "https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh"

system_packages = []

[repos]
odoo = [
"odoo",
"enterprise",
]

oca = [
"server-tools",
"server-ux",
]
"""
content = files("trobz_local").joinpath("assets/odoo_dev.toml").read_text()
typer.secho("Config file not found.", fg=typer.colors.YELLOW)
code_root = get_code_root()
typer.echo(f"Please create {code_root}/config.toml with content like this:")
Expand Down