gOdoo-cli 0.16.0__tar.gz → 0.17.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/PKG-INFO +1 -1
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/addons/godoo_test_succ_mod/__manifest__.py +1 -1
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/__about__.py +1 -1
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/cli.py +16 -2
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/odoo_bin/shell.py +32 -11
- godoo_cli-0.17.0/src/godoo_cli/commands/odoo_bin/shell_scripts/pregenerate_assets.py +34 -0
- godoo_cli-0.17.0/src/godoo_cli/commands/odoo_bin/shell_scripts/render_report.py +122 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/git/git_odoo_addons.py +1 -1
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/helpers/system.py +116 -3
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/.codecovrc +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/.devcontainer/devcontainer.json +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/.env.sample +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/.eslintignore +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/.eslintrc.yml +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/.github/FUNDING.yml +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/.github/workflows/README.md +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/.github/workflows/quality.yml +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/.github/workflows/version-bump.yml +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/.github/workflows/version-publish.yml +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/.gitignore +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/.pre-commit-config.yaml +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/.prettierrc.yml +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/.vscode/launch.json +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/LICENSE +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/README.md +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/addons/.gitkeep +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/addons/godoo_test_succ_mod/__init__.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/addons/godoo_test_succ_mod/models/__init__.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/addons/godoo_test_succ_mod/tests/__init__.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/addons/godoo_test_succ_mod/tests/data.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/addons/godoo_test_succ_mod/tests/test_succ.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/assets/godoo-main-cli.png +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/assets/odoo_logo.png +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/config/nginx-proxy_headers.conf +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/config/nginx.conf +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/config/postgresql.conf +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/config/supervisord.conf +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/docker/Dockerfile +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/docker/cups/Dockerfile +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/docker/cups/config/cups-pdf.conf +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/docker/cups/config/cupsd.conf +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/docker/cups/cups_entrypoint.sh +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/docker/depends_build.sh +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/docker/depends_runtime.sh +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/docker/docker-compose.base.yml +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/docker/docker-compose.devcontainer-traefik.yml +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/docker/docker-compose.devcontainer.yml +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/docker/docker-compose.traefik.yml +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/docker/psql.gpg +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/godoo_src.code-workspace +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/makefile +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/odoo_manifest.yml +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/pyproject.toml +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/scripts/container_requirements.sh +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/scripts/launchodoo.sh +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/scripts/migrations/development/shrink_database.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/scripts/reset_devcontainer.sh +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/__init__.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/__main__.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/cli_common.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/__init__.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/backup/__init__.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/backup/cli.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/backup/dump.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/backup/load.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/backup/pull.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/backup/util.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/config.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/db/__init__.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/db/cli.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/db/passwords.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/db/query.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/odoo_bin/__init__.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/odoo_bin/bootstrap.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/odoo_bin/cli_generate.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/odoo_bin/godoo_test/__init__.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/odoo_bin/godoo_test/cli.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/odoo_bin/godoo_test/test_load_data.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/odoo_bin/godoo_test/test_run.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/odoo_bin/launch.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/odoo_bin/shell_scripts/odoo_banner.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/rpc/__init__.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/rpc/cli.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/rpc/config_parameters.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/rpc/importer.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/rpc/modules.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/rpc/translations.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/source_get.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/git/__init__.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/git/git_repo.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/git/git_url.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/git/zip_download.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/helpers/__init__.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/helpers/cli.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/helpers/modules_git.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/helpers/modules_py.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/helpers/odoo_files.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/helpers/pip.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/models/__init__.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/models/db_connection.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/models/godoo_git_repo.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/models/godoo_manifest.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/models/godoo_models.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/models/godoo_modules.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/tests/test_git_clone.py +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/thirdparty/.gitkeep +0 -0
- {godoo_cli-0.16.0 → godoo_cli-0.17.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gOdoo-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.17.0
|
|
4
4
|
Summary: Wrapper around Odoo-Bin with some convinience RPC functions.
|
|
5
5
|
Project-URL: repository, https://github.com/OpenJKSoftware/gOdoo
|
|
6
6
|
Author-email: Joshua Kreuder <Joshua_Kreuder@outlook.com>
|
|
@@ -38,14 +38,28 @@ def main_callback(
|
|
|
38
38
|
help="Verbose Logging with Error stacktraces",
|
|
39
39
|
),
|
|
40
40
|
] = False,
|
|
41
|
+
log_filter: Annotated[
|
|
42
|
+
Optional[str],
|
|
43
|
+
typer.Option(
|
|
44
|
+
"--log-filter",
|
|
45
|
+
"-lf",
|
|
46
|
+
envvar="GODOO_LOG_FILTER",
|
|
47
|
+
help="Regex pattern to filter log output by logger name (e.g. 'odoo.test.suite')",
|
|
48
|
+
),
|
|
49
|
+
] = None,
|
|
41
50
|
):
|
|
42
|
-
"""Configure the CLI's logging level.
|
|
51
|
+
"""Configure the CLI's logging level and optional log filter.
|
|
43
52
|
|
|
44
53
|
Args:
|
|
45
54
|
verbose: If True, enables verbose logging with error stacktraces.
|
|
46
55
|
Can be set via --verbose flag or GODOO_VERBOSE environment variable.
|
|
56
|
+
log_filter: Optional regex pattern to filter log records by logger name.
|
|
57
|
+
Can be set via --log-filter flag or GODOO_LOG_FILTER environment variable.
|
|
47
58
|
"""
|
|
48
|
-
set_logging(
|
|
59
|
+
set_logging(
|
|
60
|
+
verbose=bool(verbose) if verbose is not None else False,
|
|
61
|
+
log_filter=log_filter,
|
|
62
|
+
)
|
|
49
63
|
|
|
50
64
|
|
|
51
65
|
def main_cli():
|
|
@@ -6,6 +6,7 @@ Odoo environment. It supports both interactive and script-based operations.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import logging
|
|
9
|
+
import subprocess
|
|
9
10
|
import sys
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
from typing import Annotated, Optional
|
|
@@ -103,6 +104,10 @@ def odoo_shell_run_script(
|
|
|
103
104
|
odoo_conf_path: Annotated[Path, CLI.odoo_paths.conf_path],
|
|
104
105
|
db_name: Annotated[str, CLI.database.db_name],
|
|
105
106
|
db_user: Annotated[str, CLI.database.db_user],
|
|
107
|
+
script_args: Annotated[
|
|
108
|
+
Optional[list[str]],
|
|
109
|
+
typer.Argument(help="Arguments to pass to the script"),
|
|
110
|
+
] = None,
|
|
106
111
|
db_host: Annotated[str, CLI.database.db_host] = "",
|
|
107
112
|
db_port: Annotated[int, CLI.database.db_port] = 0,
|
|
108
113
|
db_password: Annotated[str, CLI.database.db_password] = "",
|
|
@@ -111,6 +116,7 @@ def odoo_shell_run_script(
|
|
|
111
116
|
|
|
112
117
|
This function executes a Python script in the Odoo shell environment,
|
|
113
118
|
supporting both configuration file and direct database connection parameters.
|
|
119
|
+
Additional arguments after the script name are passed to the script as script_args list.
|
|
114
120
|
|
|
115
121
|
Returns:
|
|
116
122
|
int: 0 for success, non-zero for failure.
|
|
@@ -121,7 +127,7 @@ def odoo_shell_run_script(
|
|
|
121
127
|
return CLI.returner(1)
|
|
122
128
|
|
|
123
129
|
shell_cmd = f"{odoo_main_path.absolute()!s}/odoo-bin shell --no-http"
|
|
124
|
-
if odoo_conf_path.exists():
|
|
130
|
+
if odoo_conf_path and odoo_conf_path.exists():
|
|
125
131
|
shell_cmd += f" -c {odoo_conf_path.absolute()!s}"
|
|
126
132
|
else:
|
|
127
133
|
LOGGER.warning("No Odoo Config File found at %s", odoo_conf_path)
|
|
@@ -130,8 +136,28 @@ def odoo_shell_run_script(
|
|
|
130
136
|
return CLI.returner(1)
|
|
131
137
|
shell_cmd += f" --db_host={db_host} --db_port={db_port} --database={db_name} --db_user={db_user} --db_password={db_password}"
|
|
132
138
|
|
|
133
|
-
|
|
134
|
-
|
|
139
|
+
# Prepare script with arguments
|
|
140
|
+
script_content = script_path.read_text()
|
|
141
|
+
|
|
142
|
+
# If script_args provided, prepend them as a list variable
|
|
143
|
+
if script_args:
|
|
144
|
+
args_str = repr(list(script_args)) # Safely convert to Python list literal
|
|
145
|
+
script_with_args = f"script_args = {args_str}\n\n{script_content}"
|
|
146
|
+
LOGGER.info("Running Script: %s with args: %s", script_path, script_args)
|
|
147
|
+
else:
|
|
148
|
+
script_with_args = f"script_args = []\n\n{script_content}"
|
|
149
|
+
LOGGER.info("Running Script: %s", script_path)
|
|
150
|
+
|
|
151
|
+
proc = subprocess.Popen(
|
|
152
|
+
shell_cmd,
|
|
153
|
+
shell=True,
|
|
154
|
+
stdin=subprocess.PIPE,
|
|
155
|
+
stdout=sys.stdout,
|
|
156
|
+
stderr=sys.stderr,
|
|
157
|
+
text=True,
|
|
158
|
+
)
|
|
159
|
+
proc.communicate(input=script_with_args)
|
|
160
|
+
return CLI.returner(proc.returncode)
|
|
135
161
|
|
|
136
162
|
|
|
137
163
|
def odoo_pregenerate_assets(godoo_conf: GodooConfig):
|
|
@@ -144,15 +170,10 @@ def odoo_pregenerate_assets(godoo_conf: GodooConfig):
|
|
|
144
170
|
NotImplementedError: If the Odoo version is not supported.
|
|
145
171
|
"""
|
|
146
172
|
odoo_version = odoo_bin_get_version(odoo_main_repo_path=godoo_conf.odoo_install_folder)
|
|
147
|
-
if odoo_version.major == 16:
|
|
148
|
-
pregen_command = "env['ir.qweb']._pregenerate_assets_bundles();env.cr.commit()"
|
|
149
|
-
else:
|
|
150
|
-
msg = f"Odoo Version {odoo_version.raw} not supported in gOdoo for pregenerate_assets"
|
|
151
|
-
LOGGER.error(msg)
|
|
152
|
-
raise NotImplementedError(msg)
|
|
153
173
|
LOGGER.info("Pregenerating Assets for Odoo version %s", odoo_version.raw)
|
|
154
|
-
|
|
155
|
-
|
|
174
|
+
# Use the pregenerate_assets script which attempts multiple internal APIs.
|
|
175
|
+
odoo_shell_run_script(
|
|
176
|
+
script_name="pregenerate_assets",
|
|
156
177
|
odoo_main_path=godoo_conf.odoo_install_folder,
|
|
157
178
|
odoo_conf_path=godoo_conf.odoo_conf_path,
|
|
158
179
|
db_name=godoo_conf.db_name,
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Script used by gOdoo to pregenerate asset bundles in the Odoo filestore.
|
|
2
|
+
|
|
3
|
+
The script tries several internal APIs to be tolerant across multiple
|
|
4
|
+
Odoo versions (16+). It prints which method succeeded or prints
|
|
5
|
+
diagnostic information if all attempts fail.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import contextlib
|
|
9
|
+
|
|
10
|
+
from odoo import api
|
|
11
|
+
|
|
12
|
+
env: api.Environment = env # Just to silence pyright # pyright: ignore # NOQA
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
env["ir.qweb"]._pregenerate_assets_bundles()
|
|
16
|
+
print("pregenerated: ir.qweb._pregenerate_assets_bundles")
|
|
17
|
+
except Exception:
|
|
18
|
+
try:
|
|
19
|
+
env["ir.qweb"]._pregenerate_assets()
|
|
20
|
+
print("pregenerated: ir.qweb._pregenerate_assets")
|
|
21
|
+
except Exception:
|
|
22
|
+
try:
|
|
23
|
+
env["ir.asset"]._pregenerate_bundles()
|
|
24
|
+
print("pregenerated: ir.asset._pregenerate_bundles")
|
|
25
|
+
except Exception:
|
|
26
|
+
print("pregenerate failed; exceptions:")
|
|
27
|
+
import traceback
|
|
28
|
+
|
|
29
|
+
with contextlib.suppress(Exception):
|
|
30
|
+
traceback.print_exc()
|
|
31
|
+
|
|
32
|
+
# Commit any DB changes
|
|
33
|
+
with contextlib.suppress(Exception):
|
|
34
|
+
env.cr.commit()
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""Render an Odoo report to HTML and print to stdout.
|
|
2
|
+
|
|
3
|
+
This script renders a QWeb report for given record IDs and outputs the HTML.
|
|
4
|
+
It is executed in the Odoo shell environment via:
|
|
5
|
+
|
|
6
|
+
godoo shell-script render_report <report_name> <record_id> [<record_id> ...]
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
godoo shell-script render_report sale.report_saleorder 1
|
|
10
|
+
godoo shell-script render_report sale.report_saleorder 1 2 3
|
|
11
|
+
|
|
12
|
+
Flags:
|
|
13
|
+
--keep-base64 Keep base64 content instead of replacing with placeholder
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
import re
|
|
19
|
+
import sys
|
|
20
|
+
from xml.dom import minidom
|
|
21
|
+
|
|
22
|
+
from odoo import api
|
|
23
|
+
|
|
24
|
+
LOGGER = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
env: api.Environment = env # Just to silence pyright # pyright: ignore # NOQA
|
|
27
|
+
|
|
28
|
+
# script_args is injected by godoo shell-script command
|
|
29
|
+
script_args: list[str] = script_args # pyright: ignore # NOQA
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def replace_base64_with_placeholder(html_string: str) -> str:
|
|
33
|
+
"""Replace base64 content with a placeholder.
|
|
34
|
+
|
|
35
|
+
Replaces long base64 strings (data URIs, embedded images, etc.) with
|
|
36
|
+
a placeholder to make output more readable.
|
|
37
|
+
|
|
38
|
+
Parameters
|
|
39
|
+
----------
|
|
40
|
+
html_string : str
|
|
41
|
+
HTML string potentially containing base64 content
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
-------
|
|
45
|
+
str
|
|
46
|
+
HTML string with base64 content replaced by placeholder
|
|
47
|
+
"""
|
|
48
|
+
# Match data URIs with base64 content and replace with placeholder
|
|
49
|
+
# Matches patterns like: data:image/png;base64,... or similar
|
|
50
|
+
pattern = r"data:[^/]+/[^;]+;base64,[A-Za-z0-9+/]+={0,2}"
|
|
51
|
+
replacement = "data:[MIME];base64,[BASE64_CONTENT_PLACEHOLDER]"
|
|
52
|
+
return re.sub(pattern, replacement, html_string)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def render_report(report_name: str, record_ids: list[int], replace_base64: bool = True):
|
|
56
|
+
"""Render an Odoo report to HTML.
|
|
57
|
+
|
|
58
|
+
Parameters
|
|
59
|
+
----------
|
|
60
|
+
report_name : str
|
|
61
|
+
Name of the report to render (e.g., 'sale.report_saleorder')
|
|
62
|
+
record_ids : list[int]
|
|
63
|
+
List of record IDs to render
|
|
64
|
+
replace_base64 : bool, optional
|
|
65
|
+
Replace base64 content with placeholder (True by default)
|
|
66
|
+
"""
|
|
67
|
+
try:
|
|
68
|
+
report_model = env["ir.actions.report"]
|
|
69
|
+
html_content, _ = report_model._render_qweb_html(report_name, record_ids)
|
|
70
|
+
|
|
71
|
+
if html_content:
|
|
72
|
+
# Decode bytes to string
|
|
73
|
+
html_string = html_content.decode("utf-8") if isinstance(html_content, bytes) else html_content
|
|
74
|
+
|
|
75
|
+
# Replace base64 content with placeholder if enabled
|
|
76
|
+
if replace_base64:
|
|
77
|
+
html_string = replace_base64_with_placeholder(html_string)
|
|
78
|
+
|
|
79
|
+
# Try to pretty print the HTML
|
|
80
|
+
try:
|
|
81
|
+
dom = minidom.parseString(html_string)
|
|
82
|
+
pretty_html = dom.toprettyxml(indent=" ")
|
|
83
|
+
# Remove extra blank lines that minidom adds
|
|
84
|
+
print("\n".join([line for line in pretty_html.split("\n") if line.strip()]))
|
|
85
|
+
except Exception:
|
|
86
|
+
# If pretty printing fails, just print the raw HTML
|
|
87
|
+
print(html_string)
|
|
88
|
+
else:
|
|
89
|
+
LOGGER.error("Failed to render report: %s", report_name)
|
|
90
|
+
sys.exit(1)
|
|
91
|
+
|
|
92
|
+
except ValueError:
|
|
93
|
+
LOGGER.exception("Report not found")
|
|
94
|
+
sys.exit(1)
|
|
95
|
+
except Exception:
|
|
96
|
+
LOGGER.exception("Error rendering report %s", report_name)
|
|
97
|
+
sys.exit(1)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# Main execution: Parse script_args and call render_report
|
|
101
|
+
if not script_args:
|
|
102
|
+
LOGGER.error("Usage: godoo shell-script render_report <report_name> <record_id> [<record_id> ...] [--keep-base64]")
|
|
103
|
+
sys.exit(1)
|
|
104
|
+
|
|
105
|
+
if len(script_args) < 2:
|
|
106
|
+
LOGGER.error("Missing arguments. Need at least report_name and one record_id")
|
|
107
|
+
sys.exit(1)
|
|
108
|
+
|
|
109
|
+
# Check for --keep-base64 flag
|
|
110
|
+
keep_base64_flag = "--keep-base64" in script_args
|
|
111
|
+
filtered_args = [arg for arg in script_args if arg != "--keep-base64"]
|
|
112
|
+
|
|
113
|
+
# Now extract report_name and record_ids from filtered args
|
|
114
|
+
if len(filtered_args) < 2:
|
|
115
|
+
LOGGER.error("Missing arguments. Need at least report_name and one record_id")
|
|
116
|
+
sys.exit(1)
|
|
117
|
+
|
|
118
|
+
report_name = filtered_args[0]
|
|
119
|
+
record_ids = [int(rid) for rid in filtered_args[1:]]
|
|
120
|
+
|
|
121
|
+
# render_report with replace_base64 being False if --keep-base64 was passed
|
|
122
|
+
render_report(report_name, record_ids, replace_base64=not keep_base64_flag)
|
|
@@ -69,9 +69,9 @@ def _merge_sources_into_repo(godoo_repo: GodooGitRepo) -> None:
|
|
|
69
69
|
remote_name = f"merge_from_{merge_spec.url.split('/')[-1].replace('.git', '')}"
|
|
70
70
|
remote = _ensure_remote(repo, remote_name, merge_spec.url)
|
|
71
71
|
fetch_ref = merge_spec.ref
|
|
72
|
-
LOGGER.info("Merging ref %s from %s into %s", fetch_ref, merge_spec.url, godoo_repo.ref)
|
|
73
72
|
remote.fetch(fetch_ref)
|
|
74
73
|
merge_target = merge_spec.commit or f"{remote.name}/{merge_spec.branch or fetch_ref}"
|
|
74
|
+
LOGGER.info("Merging ref %s from %s into %s", fetch_ref, merge_spec.url, merge_target)
|
|
75
75
|
repo.git.merge(
|
|
76
76
|
merge_target,
|
|
77
77
|
"--no-ff",
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
"""Helper functions around the host system."""
|
|
2
2
|
|
|
3
|
+
import atexit
|
|
4
|
+
import contextlib
|
|
3
5
|
import datetime
|
|
4
6
|
import logging
|
|
5
7
|
import os
|
|
8
|
+
import re
|
|
6
9
|
import subprocess
|
|
10
|
+
import sys
|
|
11
|
+
import threading
|
|
7
12
|
from pathlib import Path
|
|
8
|
-
from typing import Any, Union
|
|
13
|
+
from typing import Any, Optional, Union
|
|
9
14
|
|
|
10
15
|
import click
|
|
11
16
|
import requests
|
|
@@ -20,6 +25,100 @@ from . import cli as godoo_cli_helpers
|
|
|
20
25
|
LOGGER = logging.getLogger(__name__)
|
|
21
26
|
|
|
22
27
|
|
|
28
|
+
class RegexLogFilter(logging.Filter):
|
|
29
|
+
"""A logging filter that only passes records whose logger name matches a regex pattern."""
|
|
30
|
+
|
|
31
|
+
def __init__(self, pattern: str) -> None:
|
|
32
|
+
"""Compile and store the regex pattern used for filtering."""
|
|
33
|
+
super().__init__()
|
|
34
|
+
self._pattern = re.compile(pattern)
|
|
35
|
+
|
|
36
|
+
def filter(self, record: logging.LogRecord) -> bool:
|
|
37
|
+
"""Return True only if the record's logger name matches the pattern."""
|
|
38
|
+
return bool(self._pattern.search(record.name))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _filter_thread_main(pattern: re.Pattern, r_fd: int, orig_fd: int) -> None:
|
|
42
|
+
"""Read lines from r_fd and write only those matching pattern to orig_fd.
|
|
43
|
+
|
|
44
|
+
Lines that are part of a traceback block (starting with ``Traceback``) are
|
|
45
|
+
always emitted in full, regardless of the primary pattern, so that error
|
|
46
|
+
context is never silently dropped.
|
|
47
|
+
"""
|
|
48
|
+
traceback_start_re = re.compile(rb"^Traceback ")
|
|
49
|
+
in_traceback = False
|
|
50
|
+
|
|
51
|
+
def should_emit(line_bytes: bytes) -> bool:
|
|
52
|
+
nonlocal in_traceback
|
|
53
|
+
if in_traceback:
|
|
54
|
+
# Tracebacks are delimited by an empty line; emit it, then leave traceback mode.
|
|
55
|
+
if not line_bytes.strip():
|
|
56
|
+
in_traceback = False
|
|
57
|
+
return True
|
|
58
|
+
if traceback_start_re.match(line_bytes):
|
|
59
|
+
in_traceback = True
|
|
60
|
+
return True
|
|
61
|
+
return bool(pattern.search(line_bytes.decode("utf-8", errors="replace")))
|
|
62
|
+
|
|
63
|
+
buf = b""
|
|
64
|
+
with os.fdopen(r_fd, "rb") as reader, os.fdopen(orig_fd, "wb") as writer:
|
|
65
|
+
while True:
|
|
66
|
+
chunk = reader.read(4096)
|
|
67
|
+
if not chunk:
|
|
68
|
+
# EOF - flush any unterminated line
|
|
69
|
+
if buf and should_emit(buf):
|
|
70
|
+
writer.write(buf)
|
|
71
|
+
writer.flush()
|
|
72
|
+
break
|
|
73
|
+
buf += chunk
|
|
74
|
+
while b"\n" in buf:
|
|
75
|
+
line, buf = buf.split(b"\n", 1)
|
|
76
|
+
if should_emit(line):
|
|
77
|
+
writer.write(line + b"\n")
|
|
78
|
+
writer.flush()
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _install_fd_filter(pattern: re.Pattern) -> None:
|
|
82
|
+
"""Redirect fd 1 and fd 2 through a pipe and filter each line by regex.
|
|
83
|
+
|
|
84
|
+
Saves the original stdout fd, creates a pipe, points fd 1 and fd 2 at the
|
|
85
|
+
pipe's write end, then starts a thread that reads from the read end
|
|
86
|
+
and forwards only lines matching *pattern* to the original stdout fd.
|
|
87
|
+
This intercepts output from both Python code and child subprocesses.
|
|
88
|
+
An atexit handler closes the write end and joins the thread so the last
|
|
89
|
+
lines are never lost.
|
|
90
|
+
"""
|
|
91
|
+
orig_fd = os.dup(sys.stdout.fileno()) # save original stdout (e.g. fd 1 -> terminal)
|
|
92
|
+
r_fd, w_fd = os.pipe()
|
|
93
|
+
os.dup2(w_fd, 1) # fd 1 now -> pipe write end
|
|
94
|
+
os.dup2(w_fd, 2) # fd 2 now -> pipe write end
|
|
95
|
+
os.close(w_fd)
|
|
96
|
+
|
|
97
|
+
# Rebind Python's sys.stdout/stderr so buffered I/O goes to the new fd too
|
|
98
|
+
sys.stdout = os.fdopen(1, "w", buffering=1, closefd=False)
|
|
99
|
+
sys.stderr = sys.stdout
|
|
100
|
+
|
|
101
|
+
t = threading.Thread(
|
|
102
|
+
target=_filter_thread_main,
|
|
103
|
+
args=(pattern, r_fd, orig_fd),
|
|
104
|
+
daemon=True,
|
|
105
|
+
name="godoo-log-filter",
|
|
106
|
+
)
|
|
107
|
+
t.start()
|
|
108
|
+
|
|
109
|
+
def _cleanup() -> None:
|
|
110
|
+
# Flush Python-buffered output into the pipe, then close the write end.
|
|
111
|
+
# Closing fd 1 AND fd 2 sends EOF to the reader thread, letting it drain and exit.
|
|
112
|
+
with contextlib.suppress(Exception):
|
|
113
|
+
sys.stdout.flush()
|
|
114
|
+
for fd in (1, 2):
|
|
115
|
+
with contextlib.suppress(OSError):
|
|
116
|
+
os.close(fd)
|
|
117
|
+
t.join(timeout=10)
|
|
118
|
+
|
|
119
|
+
atexit.register(_cleanup)
|
|
120
|
+
|
|
121
|
+
|
|
23
122
|
def run_cmd(command: str, **kwargs: dict[str, Any]) -> subprocess.CompletedProcess:
|
|
24
123
|
"""Runs command via subprocess.run."""
|
|
25
124
|
LOGGER.debug("Running shell:\n%s", command)
|
|
@@ -40,8 +139,14 @@ def ensure_dotenv(varname: str) -> str:
|
|
|
40
139
|
return var
|
|
41
140
|
|
|
42
141
|
|
|
43
|
-
def set_logging(verbose: bool = False) -> None:
|
|
44
|
-
"""Set the logging configuration according to passed arguments.
|
|
142
|
+
def set_logging(verbose: bool = False, log_filter: Optional[str] = None) -> None:
|
|
143
|
+
"""Set the logging configuration according to passed arguments.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
verbose: Enable DEBUG-level logging with rich tracebacks.
|
|
147
|
+
log_filter: Optional regex pattern; only log records whose logger name
|
|
148
|
+
matches the pattern will be emitted.
|
|
149
|
+
"""
|
|
45
150
|
if verbose:
|
|
46
151
|
install_rich_traceback(suppress=[click, godoo_cli_helpers])
|
|
47
152
|
logging.basicConfig(
|
|
@@ -68,6 +173,14 @@ def set_logging(verbose: bool = False) -> None:
|
|
|
68
173
|
datefmt="%Y-%m-%d %H:%M:%S",
|
|
69
174
|
)
|
|
70
175
|
|
|
176
|
+
if log_filter:
|
|
177
|
+
pattern = re.compile(log_filter)
|
|
178
|
+
regex_filter = RegexLogFilter(log_filter)
|
|
179
|
+
for handler in logging.root.handlers:
|
|
180
|
+
handler.addFilter(regex_filter)
|
|
181
|
+
_install_fd_filter(pattern)
|
|
182
|
+
LOGGER.debug("Log filter active: '%s'", log_filter)
|
|
183
|
+
|
|
71
184
|
|
|
72
185
|
def download_file(url: str, save_path: Path, chunk_size: int = 128) -> None:
|
|
73
186
|
"""Download file from URL to specified path."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/odoo_bin/godoo_test/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/odoo_bin/godoo_test/test_load_data.py
RENAMED
|
File without changes
|
{godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/odoo_bin/godoo_test/test_run.py
RENAMED
|
File without changes
|
|
File without changes
|
{godoo_cli-0.16.0 → godoo_cli-0.17.0}/src/godoo_cli/commands/odoo_bin/shell_scripts/odoo_banner.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|