diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 60bcbe4..78d43d9 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -140,7 +140,12 @@ jobs: - name: Smoke-test Odoo launcher run: | version="${{ matrix.odoo_version }}" - major="${version%%.*}" + if [[ "$version" == "master" ]]; then + # master tracks a moving target (e.g. 19.3); read the real major from release.py + major=$(python3 -c "exec(open('$HOME/code/odoo/odoo/master/odoo/release.py').read()); print(version_info[0])") + else + major="${version%%.*}" + fi odoo-v${major} --workers=2 --stop-after-init test-oca: @@ -245,7 +250,7 @@ jobs: - name: Create Odoo virtual environment run: | - addons_path=$(odoo-addons-path --addons-dir "~/code/oca/${{ matrix.odoo_version }}/*") + addons_path=$(odoo-addons-path ~/code/odoo/odoo/${{ matrix.odoo_version }} --addons-dir "~/code/oca/${{ matrix.odoo_version }}/*") odoo-venv create --verbose ${{ matrix.odoo_version }} \ --preset ci \ --odoo-dir ~/code/odoo/odoo/${{ matrix.odoo_version }} \ diff --git a/odoo_venv/assets/launcher.sh.template b/odoo_venv/assets/launcher.sh.template index 85fa357..54e4bbd 100644 --- a/odoo_venv/assets/launcher.sh.template +++ b/odoo_venv/assets/launcher.sh.template @@ -11,7 +11,7 @@ # Usage: # 1. Edit variables below (e.g., DATABASE="mydb", DEV_MODE="all") # 2. Run: odoo-v{version} -# 3. Script activates venv and builds command: odoo -d mydb --dev all +# 3. Script activates venv and builds command: python $VENV_DIR/bin/odoo -d mydb --dev all set -uo pipefail @@ -79,7 +79,10 @@ add_arg() { [[ -n "$$2" ]] && CMD+=("$$1" "$$2"); } add_flag() { [[ "$$2" == "true" ]] && CMD+=("$$1"); } build_command() { - CMD=("odoo") + # Use 'python bin/odoo' instead of 'bin/odoo' directly to bypass broken shebang. + # uv pip install -e writes a temporary build path as the shebang in bin/odoo, + # which breaks after installation. Running it through python ignores the shebang. + CMD=("python" "$$VENV_DIR/bin/odoo") # Database add_arg "-d" "$$DATABASE" diff --git a/odoo_venv/cli/main.py b/odoo_venv/cli/main.py index d6b4c9d..15e098e 100644 --- a/odoo_venv/cli/main.py +++ b/odoo_venv/cli/main.py @@ -409,7 +409,7 @@ def create( ) if create_launcher_flag: - create_launcher(odoo_version, venv_dir_path, force=True) + create_launcher(odoo_version, venv_dir_path, odoo_dir=odoo_dir_path, force=True) def _is_uv_venv(venv_dir: Path) -> bool: @@ -705,10 +705,14 @@ def compare( @app.command() def create_odoo_launcher( - odoo_version: Annotated[str, typer.Argument(help="Odoo version, e.g: 19.0")], + odoo_version: Annotated[str, typer.Argument(help="Odoo version, e.g: 19.0 or master")], venv_dir: Annotated[str, typer.Option(help="Path to the virtual environment.")], + odoo_dir: Annotated[ + str | None, typer.Option(help="Path to Odoo source (required for non-numeric versions like 'master').") + ] = None, force: Annotated[bool, typer.Option(help="Overwrite existing launcher script.")] = False, ): """Generate a launcher script in ~/.local/bin/ for the Odoo environment""" venv_dir_path = Path(venv_dir).expanduser().resolve() - create_launcher(odoo_version, venv_dir_path, force=force) + odoo_dir_path = Path(odoo_dir).expanduser().resolve() if odoo_dir else None + create_launcher(odoo_version, venv_dir_path, odoo_dir=odoo_dir_path, force=force) diff --git a/odoo_venv/launcher.py b/odoo_venv/launcher.py index 80c2dd6..d693f94 100644 --- a/odoo_venv/launcher.py +++ b/odoo_venv/launcher.py @@ -6,18 +6,47 @@ from string import Template import typer +from odoo_addons_path import get_odoo_version LAUNCHER_DIR = Path("~/.local/bin").expanduser() TEMPLATE_PATH = Path(__file__).parent / "assets" / "launcher.sh.template" -def create_launcher(odoo_version: str, venv_dir: str | Path, force: bool = False) -> Path: +def _resolve_major_version(odoo_version: str, odoo_dir: Path | None) -> str: + """Resolve the major version number from the Odoo version string. + + When odoo_version is non-numeric (e.g. "master"), detect the real + version from the Odoo source via ``get_odoo_version``. + """ + major = odoo_version.split(".")[0] + if major.isdigit(): + return major + + # Non-numeric branch name (e.g. "master") — detect from source + if odoo_dir is not None: + detected = get_odoo_version(addons_path="", odoo_dir=odoo_dir) + if detected: + return detected.split(".")[0] + + typer.secho( + f"Cannot determine major version from '{odoo_version}'. " + "Pass --odoo-dir so the version can be read from odoo/release.py.", + fg=typer.colors.RED, + err=True, + ) + sys.exit(1) + + +def create_launcher( + odoo_version: str, venv_dir: str | Path, *, odoo_dir: Path | None = None, force: bool = False +) -> Path: """ Generate a bash launcher script that auto-activates the venv and runs Odoo. Args: - odoo_version: Odoo version string (e.g., "19.0") + odoo_version: Odoo version string (e.g., "19.0" or "master") venv_dir: Path to the virtual environment + odoo_dir: Path to Odoo source (needed to resolve non-numeric versions like "master") force: Overwrite existing launcher if True Returns: @@ -26,8 +55,7 @@ def create_launcher(odoo_version: str, venv_dir: str | Path, force: bool = False Raises: SystemExit: If file exists and force=False, or on write errors """ - # Extract major version for script name - major_version = odoo_version.split(".")[0] + major_version = _resolve_major_version(odoo_version, odoo_dir) script_name = f"odoo-v{major_version}" output_path = LAUNCHER_DIR / script_name diff --git a/pyproject.toml b/pyproject.toml index 440b74a..c33d847 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ dependencies = [ "typing_extensions", "tomli", "packaging>=25.0", - "odoo-addons-path>=1.1.0", + "odoo-addons-path>=1.2.0", ] [dependency-groups] diff --git a/uv.lock b/uv.lock index 315da22..555ea3d 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.10" [[package]] @@ -130,15 +130,15 @@ wheels = [ [[package]] name = "odoo-addons-path" -version = "1.1.0" +version = "1.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyyaml" }, { name = "typer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ab/d7/581000ca4c9a9f0995e9258a9eec8d7f70a84c0df2b0265bab6e55e816b4/odoo_addons_path-1.1.0.tar.gz", hash = "sha256:3c316fcc54edd51fa8a621bfceca364c59893c2f2ac20146132df5b6160e98c6", size = 68618, upload-time = "2026-02-24T06:20:32.364Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/2d/d2f39b81b20a4864a0153748d39bb7bb7959bfae386fc759fe3674061ae7/odoo_addons_path-1.2.0.tar.gz", hash = "sha256:62d8f33a52b0b055cb5ca8880b0eb42c5db260af161c77f2fd7956754e0d4b3e", size = 70713, upload-time = "2026-03-24T03:23:12.369Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/df/4ca0a251587c7711f6c709efc5382efbf82a08b0504d39574d2743c5ed24/odoo_addons_path-1.1.0-py3-none-any.whl", hash = "sha256:5b3206c5a2a56890c237a49506790e9af1f3d5e5b0a1158fbc7996b7b05599cc", size = 19605, upload-time = "2026-02-24T06:20:30.809Z" }, + { url = "https://files.pythonhosted.org/packages/fc/46/6977491ca3665f1379baf38c6b03424cb6d1dbb9122832e24f916a81d4a2/odoo_addons_path-1.2.0-py3-none-any.whl", hash = "sha256:bc869ca58b7449a5594f00852bcb62f87b66f0fad3b889bf66eddf4485bd2f67", size = 20858, upload-time = "2026-03-24T03:23:11.287Z" }, ] [[package]] @@ -169,7 +169,7 @@ dev = [ [package.metadata] requires-dist = [ - { name = "odoo-addons-path", specifier = ">=1.1.0" }, + { name = "odoo-addons-path", specifier = ">=1.2.0" }, { name = "packaging", specifier = ">=25.0" }, { name = "tomli" }, { name = "typer" },