diff --git a/README.md b/README.md index 0918daf..d60706d 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,8 @@ On remote server where applications will be deployed: ```bash uv tool install odoo-venv uv tool install odoo-addons-path +uv tool install click-odoo-contrib +uv tool install git-aggregator ``` Sample configuration file: diff --git a/SPEC.md b/SPEC.md index 098e7b1..4402233 100644 --- a/SPEC.md +++ b/SPEC.md @@ -140,7 +140,7 @@ deploy [--config FILE] configure [] [] [--ty - Unit file destination: `~/.config/systemd/user/.service` - Template variables per type: - - **`odoo`**: `instance_name`, `instance_path`, `venv_path`, `addons_path` + - **`odoo`**: `instance_name`, `instance_path`, `venv_path`, `odoo_addons_path` - **`python`**: `instance_name`, `instance_path`, `venv_path`, `exec_start` - **`service`**: `instance_name`, `instance_path`, `exec_start` - After writing the unit file, run: @@ -354,7 +354,7 @@ odoo-myproject-production: db: myproject # Odoo only; defaults to instance_name if omitted # service / python only - exec_start: myapp.main:app # module path for python; verbatim for service + exec_start: python -m myapp.main:app # `python -m module path` or `python file.py` or `fastapi entry` for python; verbatim for service build: npm ci && npm run build # service only # Hooks (update command) @@ -395,9 +395,9 @@ After=network.target postgresql.service [Service] Type=simple WorkingDirectory={{ instance_path }} -ExecStart={{ venv_path }}/bin/python {{ instance_path }}/odoo-bin \ +ExecStart={{ venv_path }}/bin/python {{ venv_path }}/bin/odoo \ --config {{ instance_path }}/config/odoo.conf \ - --addons-path {{ addons_path }} + --addons-path $({{ odoo_addons_path }} {{ instance_path }}) Restart=on-failure RestartSec=5s @@ -415,7 +415,7 @@ After=network.target [Service] Type=simple WorkingDirectory={{ instance_path }} -ExecStart={{ venv_path }}/bin/python -m {{ exec_start }} +ExecStart={{ venv_path }}/bin/{{ exec_start }} Restart=on-failure RestartSec=5s @@ -503,7 +503,8 @@ The following tools must be pre-installed on the target host: | `odoo-venv` | `odoo` deployments | | `odoo-addons-path`| `odoo` deployments | | `uv` | `python` deployments | -| `click-odoo-upgrade` | `odoo` venv (not a local dep of `deploy`) | +| `click-odoo-contrib` | `odoo` venv (not a local dep of `deploy`) | +| `git-aggregator` | `odoo` deployments | For `service` deployments, any additional runtime or toolchain (Node.js, Ruby, Rust, etc.) must also be pre-installed; `deploy` only orchestrates `git pull`, the `build` command, and systemd diff --git a/deploy/command/configure.py b/deploy/command/configure.py index a72d16e..b4aca2a 100644 --- a/deploy/command/configure.py +++ b/deploy/command/configure.py @@ -4,7 +4,6 @@ import click -from deploy.utils.addons import get_addons_path from deploy.utils.config import load_config, resolve_options from deploy.utils.executor import Executor, ExecutorError from deploy.utils.render import render_unit @@ -79,7 +78,8 @@ def configure( # noqa: C901 raise click.ClickException(msg) executor = Executor(eff_ssh_host, ctx.obj["verbose"], ssh_port=eff_ssh_port) - instance_path = f"$HOME/{instance_name}" + home_dir = executor.capture("echo $HOME") + instance_path = f"{home_dir}/{instance_name}" # Step 2: Clone repository if _is_git_repo(executor, instance_path): @@ -94,14 +94,16 @@ def configure( # noqa: C901 except ExecutorError as exc: msg = f"Git clone failed: {exc}" raise click.ClickException(msg) from exc + executor.run( + "if [ -f addons/repos.yaml ]; then cd addons/ && gitaggregate -c repos.yaml; fi", + cwd=instance_path, + ) # Step 3: Set up environment click.echo(f"Setting up {eff_type} environment …") - addons_path: str | None = None try: if eff_type == "odoo": setup_odoo_venv(executor, instance_path) - addons_path = get_addons_path(executor, instance_path) elif eff_type == "python": setup_python_venv(executor, instance_path, force=force) else: # service @@ -116,7 +118,6 @@ def configure( # noqa: C901 # Step 4: Install systemd unit click.echo("Installing systemd unit …") venv_path = f"{instance_path}/.venv" - exec_start: str = opts.get("exec_start", "") template_vars: dict[str, Any] = { "instance_name": instance_name, @@ -124,11 +125,16 @@ def configure( # noqa: C901 } if eff_type == "odoo": template_vars["venv_path"] = venv_path - template_vars["addons_path"] = addons_path - elif eff_type == "python": - template_vars["venv_path"] = venv_path - template_vars["exec_start"] = exec_start + odoo_addons_path = executor.capture("which odoo-addons-path") + template_vars["odoo_addons_path"] = odoo_addons_path else: + exec_start: str = opts.get("exec_start", "") + if not exec_start: + msg = "exec_start is required for service or python type." + msg += " Set it in deploy.yml." + raise click.ClickException(msg) + if eff_type == "python": + template_vars["venv_path"] = venv_path template_vars["exec_start"] = exec_start try: diff --git a/deploy/command/update.py b/deploy/command/update.py index 2a9d064..56c40c3 100644 --- a/deploy/command/update.py +++ b/deploy/command/update.py @@ -69,7 +69,8 @@ def update( # noqa: C901 hooks: dict = opts.get("hooks", {}) executor = Executor(eff_ssh_host, ctx.obj["verbose"], ssh_port=eff_ssh_port) - instance_path = f"$HOME/{instance_name}" + home_dir = executor.capture("echo $HOME") + instance_path = f"{home_dir}/{instance_name}" def run_hooks(hook_name: str) -> bool: """Execute all commands for *hook_name*. Returns True if all succeeded.""" @@ -116,9 +117,10 @@ def run_hooks(hook_name: str) -> bool: try: if eff_type == "odoo": executor.run( - "if [ -e requirements.txt ]; then uv pip install -r requirements.txt; fi", + "if [ -f addons/repos.yaml ]; then cd addons/ && gitaggregate -c repos.yaml; fi", cwd=instance_path, ) + executor.run("odoo-venv update .venv --backup", cwd=instance_path) elif eff_type == "python": setup_python_deps(executor, instance_path) else: # service diff --git a/deploy/templates/odoo.service.j2 b/deploy/templates/odoo.service.j2 index de13add..7f39219 100644 --- a/deploy/templates/odoo.service.j2 +++ b/deploy/templates/odoo.service.j2 @@ -5,9 +5,9 @@ After=network.target postgresql.service [Service] Type=simple WorkingDirectory={{ instance_path }} -ExecStart={{ venv_path }}/bin/python {{ instance_path }}/odoo-bin \ +ExecStart={{ venv_path }}/bin/python {{ venv_path }}/bin/odoo \ --config {{ instance_path }}/config/odoo.conf \ - --addons-path {{ addons_path }} + --addons-path $({{ odoo_addons_path }} {{ instance_path }}) Restart=on-failure RestartSec=5s diff --git a/deploy/templates/python.service.j2 b/deploy/templates/python.service.j2 index 5e6672f..8d981d5 100644 --- a/deploy/templates/python.service.j2 +++ b/deploy/templates/python.service.j2 @@ -5,7 +5,7 @@ After=network.target [Service] Type=simple WorkingDirectory={{ instance_path }} -ExecStart={{ venv_path }}/bin/python -m {{ exec_start }} +ExecStart={{ venv_path }}/bin/{{ exec_start }} Restart=on-failure RestartSec=5s diff --git a/deploy/utils/venv.py b/deploy/utils/venv.py index 1f22aef..524e452 100644 --- a/deploy/utils/venv.py +++ b/deploy/utils/venv.py @@ -6,10 +6,9 @@ def setup_odoo_venv(executor: Executor, instance_path: str) -> None: """Create or update an Odoo virtual environment using ``odoo-venv``.""" executor.run( - f"odoo-venv create --project-dir {instance_path}", + f"odoo-venv create --project-dir {instance_path} --preset project", cwd=instance_path, ) - executor.run("uv pip install click-odoo-contrib", cwd=instance_path) def setup_python_venv(executor: Executor, instance_path: str, force: bool = False) -> None: