odoo-devops-tools 1.25.0__tar.gz → 1.25.2__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.
- {odoo_devops_tools-1.25.0 → odoo_devops_tools-1.25.2}/PKG-INFO +129 -13
- {odoo_devops_tools-1.25.0 → odoo_devops_tools-1.25.2}/README.md +128 -12
- odoo_devops_tools-1.25.2/odoo_devops_tools/__init__.py +1 -0
- {odoo_devops_tools-1.25.0 → odoo_devops_tools-1.25.2}/odoo_devops_tools/env.py +343 -21
- {odoo_devops_tools-1.25.0 → odoo_devops_tools-1.25.2}/odoo_devops_tools.egg-info/PKG-INFO +129 -13
- {odoo_devops_tools-1.25.0 → odoo_devops_tools-1.25.2}/pyproject.toml +1 -1
- odoo_devops_tools-1.25.0/odoo_devops_tools/__init__.py +0 -1
- {odoo_devops_tools-1.25.0 → odoo_devops_tools-1.25.2}/LICENSE +0 -0
- {odoo_devops_tools-1.25.0 → odoo_devops_tools-1.25.2}/odoo_devops_tools.egg-info/SOURCES.txt +0 -0
- {odoo_devops_tools-1.25.0 → odoo_devops_tools-1.25.2}/odoo_devops_tools.egg-info/dependency_links.txt +0 -0
- {odoo_devops_tools-1.25.0 → odoo_devops_tools-1.25.2}/odoo_devops_tools.egg-info/entry_points.txt +0 -0
- {odoo_devops_tools-1.25.0 → odoo_devops_tools-1.25.2}/odoo_devops_tools.egg-info/top_level.txt +0 -0
- {odoo_devops_tools-1.25.0 → odoo_devops_tools-1.25.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: odoo-devops-tools
|
|
3
|
-
Version: 1.25.
|
|
3
|
+
Version: 1.25.2
|
|
4
4
|
Summary: Build reproducible Odoo workspaces from a single project definition.
|
|
5
5
|
Author: Roman Lacko
|
|
6
6
|
License-Expression: MIT
|
|
@@ -174,11 +174,11 @@ This creates `./odoo19/odoo-project.ini` with this initial content:
|
|
|
174
174
|
version = 19.0
|
|
175
175
|
```
|
|
176
176
|
|
|
177
|
-
Then start the Docker Compose environment:
|
|
177
|
+
Then prepare and start the Docker Compose environment:
|
|
178
178
|
|
|
179
179
|
```bash
|
|
180
180
|
cd ./odoo19
|
|
181
|
-
|
|
181
|
+
odoo-compose up --build
|
|
182
182
|
```
|
|
183
183
|
|
|
184
184
|
Optionally initialize the Odoo database from the command line instead of through the web interface:
|
|
@@ -194,7 +194,7 @@ Docker Compose runs both Odoo and PostgreSQL. Odoo is available at http://localh
|
|
|
194
194
|
>
|
|
195
195
|
> ```bash
|
|
196
196
|
> odoo-compose setup --set docker:restart=no
|
|
197
|
-
>
|
|
197
|
+
> odoo-compose up --build
|
|
198
198
|
> ```
|
|
199
199
|
|
|
200
200
|
### 1.1. Adding extra addons
|
|
@@ -217,7 +217,7 @@ Sync the addon repositories and regenerate the Docker environment:
|
|
|
217
217
|
|
|
218
218
|
```bash
|
|
219
219
|
odoo-compose setup --sync-addons
|
|
220
|
-
|
|
220
|
+
odoo-compose up --build
|
|
221
221
|
```
|
|
222
222
|
|
|
223
223
|
The repositories are cloned into `ROOT/odoo-addons/` and bind-mounted into the Odoo container. Python dependencies from their `requirements.txt` files are included when rebuilding the Docker image.
|
|
@@ -256,7 +256,7 @@ Sync the Odoo source and generate the Docker artifacts:
|
|
|
256
256
|
|
|
257
257
|
```bash
|
|
258
258
|
odoo-compose setup --sync-all
|
|
259
|
-
|
|
259
|
+
odoo-compose up --build
|
|
260
260
|
```
|
|
261
261
|
|
|
262
262
|
The resolved Odoo source is bind-mounted read-only at `/opt/odoo`. The Odoo Python source from the base image is removed while its installed dependencies remain available.
|
|
@@ -267,14 +267,14 @@ Enable Mailpit in the local Docker workflow when you need to inspect or test ema
|
|
|
267
267
|
|
|
268
268
|
```bash
|
|
269
269
|
odoo-compose setup --set docker:mailpit_enabled=true
|
|
270
|
-
|
|
270
|
+
odoo-compose up
|
|
271
271
|
```
|
|
272
272
|
|
|
273
273
|
Mailpit is available at http://localhost:8025 by default. When running multiple workspaces, override the host web UI port, for example:
|
|
274
274
|
|
|
275
275
|
```bash
|
|
276
276
|
odoo-compose setup --set docker:mailpit_enabled=true --set docker:mailpit_webui_port=8026
|
|
277
|
-
|
|
277
|
+
odoo-compose up
|
|
278
278
|
```
|
|
279
279
|
|
|
280
280
|
The generated local Odoo configuration sends outgoing email to `mailpit:1025`. When Mailpit is enabled, local Docker SMTP connection settings (`smtp_server`, `smtp_port`, `smtp_ssl`, `smtp_user`, and `smtp_password`) are intentionally overridden; the deploy configuration keeps the values from `[config]` unchanged.
|
|
@@ -316,11 +316,10 @@ mailpit_mem_limit = 256m
|
|
|
316
316
|
|
|
317
317
|
Each option is independent and optional. If an option is missing or empty, `odoo-compose` omits that limit from the Compose service; no CPU or memory limit is imposed by the generator. CPU values must be positive decimal numbers (such as `0.5` or `2.0`). Memory limits must be positive amounts (such as `256m`, `2g`, or `4g`); zero and negative values are rejected. A memory limit is a hard container limit: exceeding it may cause an out-of-memory termination, so size it for your workload.
|
|
318
318
|
|
|
319
|
-
|
|
319
|
+
Apply the updated resource limits and start the services. `up` regenerates the Compose configuration automatically, so no separate `setup` or image rebuild is necessary:
|
|
320
320
|
|
|
321
321
|
```bash
|
|
322
|
-
odoo-compose
|
|
323
|
-
docker compose up -d --build --wait
|
|
322
|
+
odoo-compose up
|
|
324
323
|
```
|
|
325
324
|
|
|
326
325
|
### 1.6. Creating a Docker deploy build context
|
|
@@ -388,15 +387,23 @@ db_user = odoo
|
|
|
388
387
|
db_password = odoo
|
|
389
388
|
```
|
|
390
389
|
|
|
391
|
-
Then start Odoo
|
|
390
|
+
Then start Odoo in the background:
|
|
392
391
|
|
|
393
392
|
```bash
|
|
394
393
|
cd ./odoo19
|
|
395
|
-
odoo-compose-venv
|
|
394
|
+
odoo-compose-venv up
|
|
395
|
+
odoo-compose-venv status
|
|
396
396
|
```
|
|
397
397
|
|
|
398
398
|
Odoo starts with the generated configuration from `./odoo-configs/odoo-server.conf` and is available at http://localhost:8069.
|
|
399
399
|
|
|
400
|
+
Use `odoo-compose-venv run` instead to run in the foreground:
|
|
401
|
+
|
|
402
|
+
```bash
|
|
403
|
+
cd ./odoo19
|
|
404
|
+
odoo-compose-venv run
|
|
405
|
+
```
|
|
406
|
+
|
|
400
407
|
### 2.1. Adding extra addons
|
|
401
408
|
|
|
402
409
|
To extend Odoo with additional functionality, add extra addons through `[addons.<name>]` sections in `odoo-project.ini`.
|
|
@@ -614,6 +621,32 @@ This recreates `ROOT/venv`, skips dependency compilation and wheelhouse building
|
|
|
614
621
|
|
|
615
622
|
---
|
|
616
623
|
|
|
624
|
+
## Runtime lifecycle
|
|
625
|
+
|
|
626
|
+
Use the same lifecycle commands with either CLI after provisioning the workspace with `setup`:
|
|
627
|
+
|
|
628
|
+
```bash
|
|
629
|
+
# Docker: generate Docker config and start the Compose stack
|
|
630
|
+
odoo-compose up --build
|
|
631
|
+
odoo-compose status
|
|
632
|
+
odoo-compose down
|
|
633
|
+
|
|
634
|
+
# Native: generate config and start Odoo in the background
|
|
635
|
+
odoo-compose-venv up
|
|
636
|
+
odoo-compose-venv status
|
|
637
|
+
odoo-compose-venv down
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
`odoo-compose up` regenerates local workspace artifacts and then runs the equivalent of `docker compose up -d --wait`.
|
|
641
|
+
|
|
642
|
+
`odoo-compose up --build` additionally builds the images (`docker compose up -d --build --wait`).
|
|
643
|
+
|
|
644
|
+
Container orchestration remains with Docker Compose, and its CLI can still be used directly for advanced or service-specific operations.
|
|
645
|
+
|
|
646
|
+
`up` requires an existing `odoo-project.ini`. It regenerates local configuration without fetching Git repositories or recreating virtualenvs.
|
|
647
|
+
|
|
648
|
+
---
|
|
649
|
+
|
|
617
650
|
## Commands
|
|
618
651
|
|
|
619
652
|
`odoo-compose` operates on Docker workspaces; `odoo-compose-venv` operates on native Python virtualenv workspaces. Both use `ROOT/odoo-project.ini`, but generate their own workflow artifacts. Commands operate directly through the installed CLI; generated wrapper scripts are not required.
|
|
@@ -626,6 +659,15 @@ Running either CLI without a command displays help; workspace changes are perfor
|
|
|
626
659
|
<dt><a href="#setup"><code>setup</code></a></dt>
|
|
627
660
|
<dd><p>Create or update a workspace, optionally synchronizing sources and generating artifacts.</p></dd>
|
|
628
661
|
|
|
662
|
+
<dt><a href="#up"><code>up</code></a></dt>
|
|
663
|
+
<dd><p>Regenerate workspace artifacts and start Odoo in the background.</p></dd>
|
|
664
|
+
|
|
665
|
+
<dt><a href="#down"><code>down</code></a></dt>
|
|
666
|
+
<dd><p>Stop the runtime without deleting data.</p></dd>
|
|
667
|
+
|
|
668
|
+
<dt><a href="#status"><code>status</code></a></dt>
|
|
669
|
+
<dd><p>Show the current runtime state.</p></dd>
|
|
670
|
+
|
|
629
671
|
<dt><a href="#run"><code>run</code></a></dt>
|
|
630
672
|
<dd><p>Run Odoo with additional arguments.</p></dd>
|
|
631
673
|
|
|
@@ -791,6 +833,78 @@ odoo-compose-venv setup ./odoo19 --sync-all --create-venv
|
|
|
791
833
|
odoo-compose-venv setup ./odoo18 --set odoo:version=18.0 --sync-all --create-venv
|
|
792
834
|
```
|
|
793
835
|
|
|
836
|
+
### `up`
|
|
837
|
+
|
|
838
|
+
Prepare and start the selected workspace. Requires an existing `odoo-project.ini`. Docker regenerates the local Docker build context, Odoo config and `compose.yaml`, then starts services with Docker Compose; it waits for health checks. Native regenerates its configuration and starts Odoo in the background using the existing virtualenv. Neither variant automatically synchronizes repositories or rebuilds the native virtualenv.
|
|
839
|
+
|
|
840
|
+
**Usage**
|
|
841
|
+
|
|
842
|
+
```text
|
|
843
|
+
{odoo-compose|odoo-compose-venv} up [--root ROOT]
|
|
844
|
+
odoo-compose up [--root ROOT] [--build] [--recreate]
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
**Options**
|
|
848
|
+
|
|
849
|
+
<dl class="cli-reference">
|
|
850
|
+
<dt><code>--root</code> <i>ROOT</i></dt>
|
|
851
|
+
<dd><p>Select an existing workspace (default: current directory).</p></dd>
|
|
852
|
+
|
|
853
|
+
<dt><code>--build</code></dt>
|
|
854
|
+
<dd><p>Build Docker images before starting the services (Docker only).</p></dd>
|
|
855
|
+
|
|
856
|
+
<dt><code>--recreate</code></dt>
|
|
857
|
+
<dd><p>Force recreation of the Odoo container, without forcing recreation of the database container (Docker only).</p></dd>
|
|
858
|
+
</dl>
|
|
859
|
+
|
|
860
|
+
**Notes**
|
|
861
|
+
|
|
862
|
+
Docker `up` remembers the last applied generated Odoo configuration and recreates the Odoo container when it changes, including after a separate `setup`.
|
|
863
|
+
Running `up` without `--build` does not guarantee that changed Python dependencies or Dockerfile instructions are built into the image.
|
|
864
|
+
Native `up` uses `ROOT/.odt-env/odoo-process.json` for process identity and writes runtime output to `ROOT/odoo-logs/odoo-up.log`; it restarts the tracked process if the generated config changes.
|
|
865
|
+
|
|
866
|
+
### `down`
|
|
867
|
+
|
|
868
|
+
Stop the selected workspace, preserving data. Docker delegates to `docker compose down` **without** `--volumes`; native gracefully stops only the verified Odoo process created by `odoo-compose-venv up`.
|
|
869
|
+
|
|
870
|
+
**Usage**
|
|
871
|
+
|
|
872
|
+
```text
|
|
873
|
+
{odoo-compose|odoo-compose-venv} down [--root ROOT]
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
**Options**
|
|
877
|
+
|
|
878
|
+
<dl class="cli-reference">
|
|
879
|
+
<dt><code>--root</code> <i>ROOT</i></dt>
|
|
880
|
+
<dd><p>Select an existing workspace (default: current directory).</p></dd>
|
|
881
|
+
</dl>
|
|
882
|
+
|
|
883
|
+
**Notes**
|
|
884
|
+
|
|
885
|
+
Native `down` never terminates arbitrary processes found by name or port. It does not stop PostgreSQL or a separately managed systemd service. Docker `down` removes Compose-managed containers and networks, but retains named volumes.
|
|
886
|
+
|
|
887
|
+
### `status`
|
|
888
|
+
|
|
889
|
+
Show runtime status without provisioning or changing the workspace. Docker displays the Docker Compose service table; native reports the tracked Odoo PID and whether the process is still running.
|
|
890
|
+
|
|
891
|
+
**Usage**
|
|
892
|
+
|
|
893
|
+
```text
|
|
894
|
+
{odoo-compose|odoo-compose-venv} status [--root ROOT]
|
|
895
|
+
```
|
|
896
|
+
|
|
897
|
+
**Options**
|
|
898
|
+
|
|
899
|
+
<dl class="cli-reference">
|
|
900
|
+
<dt><code>--root</code> <i>ROOT</i></dt>
|
|
901
|
+
<dd><p>Select an existing workspace (default: current directory).</p></dd>
|
|
902
|
+
</dl>
|
|
903
|
+
|
|
904
|
+
**Notes**
|
|
905
|
+
|
|
906
|
+
Native `status` verifies the recorded process identity to avoid confusing a reused PID with Odoo. It does not probe the Odoo HTTP endpoint or report application health.
|
|
907
|
+
|
|
794
908
|
### `run`
|
|
795
909
|
|
|
796
910
|
Run Odoo directly from the selected workspace. Docker uses a one-off container; Venv uses the existing virtualenv and generated `odoo-server.conf`.
|
|
@@ -1340,6 +1454,8 @@ odoo-compose-venv update --all
|
|
|
1340
1454
|
odoo-compose-venv neutralize
|
|
1341
1455
|
```
|
|
1342
1456
|
|
|
1457
|
+
The Docker example uses `docker compose stop odoo` and `docker compose up -d odoo` intentionally: only the Odoo service is stopped and restarted, while PostgreSQL remains available for the restore operations.
|
|
1458
|
+
|
|
1343
1459
|
### `completion`
|
|
1344
1460
|
|
|
1345
1461
|
Generate shell-completion definitions for either CLI. See [Shell completion](#shell-completion) under Installation for activation instructions.
|
|
@@ -146,11 +146,11 @@ This creates `./odoo19/odoo-project.ini` with this initial content:
|
|
|
146
146
|
version = 19.0
|
|
147
147
|
```
|
|
148
148
|
|
|
149
|
-
Then start the Docker Compose environment:
|
|
149
|
+
Then prepare and start the Docker Compose environment:
|
|
150
150
|
|
|
151
151
|
```bash
|
|
152
152
|
cd ./odoo19
|
|
153
|
-
|
|
153
|
+
odoo-compose up --build
|
|
154
154
|
```
|
|
155
155
|
|
|
156
156
|
Optionally initialize the Odoo database from the command line instead of through the web interface:
|
|
@@ -166,7 +166,7 @@ Docker Compose runs both Odoo and PostgreSQL. Odoo is available at http://localh
|
|
|
166
166
|
>
|
|
167
167
|
> ```bash
|
|
168
168
|
> odoo-compose setup --set docker:restart=no
|
|
169
|
-
>
|
|
169
|
+
> odoo-compose up --build
|
|
170
170
|
> ```
|
|
171
171
|
|
|
172
172
|
### 1.1. Adding extra addons
|
|
@@ -189,7 +189,7 @@ Sync the addon repositories and regenerate the Docker environment:
|
|
|
189
189
|
|
|
190
190
|
```bash
|
|
191
191
|
odoo-compose setup --sync-addons
|
|
192
|
-
|
|
192
|
+
odoo-compose up --build
|
|
193
193
|
```
|
|
194
194
|
|
|
195
195
|
The repositories are cloned into `ROOT/odoo-addons/` and bind-mounted into the Odoo container. Python dependencies from their `requirements.txt` files are included when rebuilding the Docker image.
|
|
@@ -228,7 +228,7 @@ Sync the Odoo source and generate the Docker artifacts:
|
|
|
228
228
|
|
|
229
229
|
```bash
|
|
230
230
|
odoo-compose setup --sync-all
|
|
231
|
-
|
|
231
|
+
odoo-compose up --build
|
|
232
232
|
```
|
|
233
233
|
|
|
234
234
|
The resolved Odoo source is bind-mounted read-only at `/opt/odoo`. The Odoo Python source from the base image is removed while its installed dependencies remain available.
|
|
@@ -239,14 +239,14 @@ Enable Mailpit in the local Docker workflow when you need to inspect or test ema
|
|
|
239
239
|
|
|
240
240
|
```bash
|
|
241
241
|
odoo-compose setup --set docker:mailpit_enabled=true
|
|
242
|
-
|
|
242
|
+
odoo-compose up
|
|
243
243
|
```
|
|
244
244
|
|
|
245
245
|
Mailpit is available at http://localhost:8025 by default. When running multiple workspaces, override the host web UI port, for example:
|
|
246
246
|
|
|
247
247
|
```bash
|
|
248
248
|
odoo-compose setup --set docker:mailpit_enabled=true --set docker:mailpit_webui_port=8026
|
|
249
|
-
|
|
249
|
+
odoo-compose up
|
|
250
250
|
```
|
|
251
251
|
|
|
252
252
|
The generated local Odoo configuration sends outgoing email to `mailpit:1025`. When Mailpit is enabled, local Docker SMTP connection settings (`smtp_server`, `smtp_port`, `smtp_ssl`, `smtp_user`, and `smtp_password`) are intentionally overridden; the deploy configuration keeps the values from `[config]` unchanged.
|
|
@@ -288,11 +288,10 @@ mailpit_mem_limit = 256m
|
|
|
288
288
|
|
|
289
289
|
Each option is independent and optional. If an option is missing or empty, `odoo-compose` omits that limit from the Compose service; no CPU or memory limit is imposed by the generator. CPU values must be positive decimal numbers (such as `0.5` or `2.0`). Memory limits must be positive amounts (such as `256m`, `2g`, or `4g`); zero and negative values are rejected. A memory limit is a hard container limit: exceeding it may cause an out-of-memory termination, so size it for your workload.
|
|
290
290
|
|
|
291
|
-
|
|
291
|
+
Apply the updated resource limits and start the services. `up` regenerates the Compose configuration automatically, so no separate `setup` or image rebuild is necessary:
|
|
292
292
|
|
|
293
293
|
```bash
|
|
294
|
-
odoo-compose
|
|
295
|
-
docker compose up -d --build --wait
|
|
294
|
+
odoo-compose up
|
|
296
295
|
```
|
|
297
296
|
|
|
298
297
|
### 1.6. Creating a Docker deploy build context
|
|
@@ -360,15 +359,23 @@ db_user = odoo
|
|
|
360
359
|
db_password = odoo
|
|
361
360
|
```
|
|
362
361
|
|
|
363
|
-
Then start Odoo
|
|
362
|
+
Then start Odoo in the background:
|
|
364
363
|
|
|
365
364
|
```bash
|
|
366
365
|
cd ./odoo19
|
|
367
|
-
odoo-compose-venv
|
|
366
|
+
odoo-compose-venv up
|
|
367
|
+
odoo-compose-venv status
|
|
368
368
|
```
|
|
369
369
|
|
|
370
370
|
Odoo starts with the generated configuration from `./odoo-configs/odoo-server.conf` and is available at http://localhost:8069.
|
|
371
371
|
|
|
372
|
+
Use `odoo-compose-venv run` instead to run in the foreground:
|
|
373
|
+
|
|
374
|
+
```bash
|
|
375
|
+
cd ./odoo19
|
|
376
|
+
odoo-compose-venv run
|
|
377
|
+
```
|
|
378
|
+
|
|
372
379
|
### 2.1. Adding extra addons
|
|
373
380
|
|
|
374
381
|
To extend Odoo with additional functionality, add extra addons through `[addons.<name>]` sections in `odoo-project.ini`.
|
|
@@ -586,6 +593,32 @@ This recreates `ROOT/venv`, skips dependency compilation and wheelhouse building
|
|
|
586
593
|
|
|
587
594
|
---
|
|
588
595
|
|
|
596
|
+
## Runtime lifecycle
|
|
597
|
+
|
|
598
|
+
Use the same lifecycle commands with either CLI after provisioning the workspace with `setup`:
|
|
599
|
+
|
|
600
|
+
```bash
|
|
601
|
+
# Docker: generate Docker config and start the Compose stack
|
|
602
|
+
odoo-compose up --build
|
|
603
|
+
odoo-compose status
|
|
604
|
+
odoo-compose down
|
|
605
|
+
|
|
606
|
+
# Native: generate config and start Odoo in the background
|
|
607
|
+
odoo-compose-venv up
|
|
608
|
+
odoo-compose-venv status
|
|
609
|
+
odoo-compose-venv down
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
`odoo-compose up` regenerates local workspace artifacts and then runs the equivalent of `docker compose up -d --wait`.
|
|
613
|
+
|
|
614
|
+
`odoo-compose up --build` additionally builds the images (`docker compose up -d --build --wait`).
|
|
615
|
+
|
|
616
|
+
Container orchestration remains with Docker Compose, and its CLI can still be used directly for advanced or service-specific operations.
|
|
617
|
+
|
|
618
|
+
`up` requires an existing `odoo-project.ini`. It regenerates local configuration without fetching Git repositories or recreating virtualenvs.
|
|
619
|
+
|
|
620
|
+
---
|
|
621
|
+
|
|
589
622
|
## Commands
|
|
590
623
|
|
|
591
624
|
`odoo-compose` operates on Docker workspaces; `odoo-compose-venv` operates on native Python virtualenv workspaces. Both use `ROOT/odoo-project.ini`, but generate their own workflow artifacts. Commands operate directly through the installed CLI; generated wrapper scripts are not required.
|
|
@@ -598,6 +631,15 @@ Running either CLI without a command displays help; workspace changes are perfor
|
|
|
598
631
|
<dt><a href="#setup"><code>setup</code></a></dt>
|
|
599
632
|
<dd><p>Create or update a workspace, optionally synchronizing sources and generating artifacts.</p></dd>
|
|
600
633
|
|
|
634
|
+
<dt><a href="#up"><code>up</code></a></dt>
|
|
635
|
+
<dd><p>Regenerate workspace artifacts and start Odoo in the background.</p></dd>
|
|
636
|
+
|
|
637
|
+
<dt><a href="#down"><code>down</code></a></dt>
|
|
638
|
+
<dd><p>Stop the runtime without deleting data.</p></dd>
|
|
639
|
+
|
|
640
|
+
<dt><a href="#status"><code>status</code></a></dt>
|
|
641
|
+
<dd><p>Show the current runtime state.</p></dd>
|
|
642
|
+
|
|
601
643
|
<dt><a href="#run"><code>run</code></a></dt>
|
|
602
644
|
<dd><p>Run Odoo with additional arguments.</p></dd>
|
|
603
645
|
|
|
@@ -763,6 +805,78 @@ odoo-compose-venv setup ./odoo19 --sync-all --create-venv
|
|
|
763
805
|
odoo-compose-venv setup ./odoo18 --set odoo:version=18.0 --sync-all --create-venv
|
|
764
806
|
```
|
|
765
807
|
|
|
808
|
+
### `up`
|
|
809
|
+
|
|
810
|
+
Prepare and start the selected workspace. Requires an existing `odoo-project.ini`. Docker regenerates the local Docker build context, Odoo config and `compose.yaml`, then starts services with Docker Compose; it waits for health checks. Native regenerates its configuration and starts Odoo in the background using the existing virtualenv. Neither variant automatically synchronizes repositories or rebuilds the native virtualenv.
|
|
811
|
+
|
|
812
|
+
**Usage**
|
|
813
|
+
|
|
814
|
+
```text
|
|
815
|
+
{odoo-compose|odoo-compose-venv} up [--root ROOT]
|
|
816
|
+
odoo-compose up [--root ROOT] [--build] [--recreate]
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
**Options**
|
|
820
|
+
|
|
821
|
+
<dl class="cli-reference">
|
|
822
|
+
<dt><code>--root</code> <i>ROOT</i></dt>
|
|
823
|
+
<dd><p>Select an existing workspace (default: current directory).</p></dd>
|
|
824
|
+
|
|
825
|
+
<dt><code>--build</code></dt>
|
|
826
|
+
<dd><p>Build Docker images before starting the services (Docker only).</p></dd>
|
|
827
|
+
|
|
828
|
+
<dt><code>--recreate</code></dt>
|
|
829
|
+
<dd><p>Force recreation of the Odoo container, without forcing recreation of the database container (Docker only).</p></dd>
|
|
830
|
+
</dl>
|
|
831
|
+
|
|
832
|
+
**Notes**
|
|
833
|
+
|
|
834
|
+
Docker `up` remembers the last applied generated Odoo configuration and recreates the Odoo container when it changes, including after a separate `setup`.
|
|
835
|
+
Running `up` without `--build` does not guarantee that changed Python dependencies or Dockerfile instructions are built into the image.
|
|
836
|
+
Native `up` uses `ROOT/.odt-env/odoo-process.json` for process identity and writes runtime output to `ROOT/odoo-logs/odoo-up.log`; it restarts the tracked process if the generated config changes.
|
|
837
|
+
|
|
838
|
+
### `down`
|
|
839
|
+
|
|
840
|
+
Stop the selected workspace, preserving data. Docker delegates to `docker compose down` **without** `--volumes`; native gracefully stops only the verified Odoo process created by `odoo-compose-venv up`.
|
|
841
|
+
|
|
842
|
+
**Usage**
|
|
843
|
+
|
|
844
|
+
```text
|
|
845
|
+
{odoo-compose|odoo-compose-venv} down [--root ROOT]
|
|
846
|
+
```
|
|
847
|
+
|
|
848
|
+
**Options**
|
|
849
|
+
|
|
850
|
+
<dl class="cli-reference">
|
|
851
|
+
<dt><code>--root</code> <i>ROOT</i></dt>
|
|
852
|
+
<dd><p>Select an existing workspace (default: current directory).</p></dd>
|
|
853
|
+
</dl>
|
|
854
|
+
|
|
855
|
+
**Notes**
|
|
856
|
+
|
|
857
|
+
Native `down` never terminates arbitrary processes found by name or port. It does not stop PostgreSQL or a separately managed systemd service. Docker `down` removes Compose-managed containers and networks, but retains named volumes.
|
|
858
|
+
|
|
859
|
+
### `status`
|
|
860
|
+
|
|
861
|
+
Show runtime status without provisioning or changing the workspace. Docker displays the Docker Compose service table; native reports the tracked Odoo PID and whether the process is still running.
|
|
862
|
+
|
|
863
|
+
**Usage**
|
|
864
|
+
|
|
865
|
+
```text
|
|
866
|
+
{odoo-compose|odoo-compose-venv} status [--root ROOT]
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
**Options**
|
|
870
|
+
|
|
871
|
+
<dl class="cli-reference">
|
|
872
|
+
<dt><code>--root</code> <i>ROOT</i></dt>
|
|
873
|
+
<dd><p>Select an existing workspace (default: current directory).</p></dd>
|
|
874
|
+
</dl>
|
|
875
|
+
|
|
876
|
+
**Notes**
|
|
877
|
+
|
|
878
|
+
Native `status` verifies the recorded process identity to avoid confusing a reused PID with Odoo. It does not probe the Odoo HTTP endpoint or report application health.
|
|
879
|
+
|
|
766
880
|
### `run`
|
|
767
881
|
|
|
768
882
|
Run Odoo directly from the selected workspace. Docker uses a one-off container; Venv uses the existing virtualenv and generated `odoo-server.conf`.
|
|
@@ -1312,6 +1426,8 @@ odoo-compose-venv update --all
|
|
|
1312
1426
|
odoo-compose-venv neutralize
|
|
1313
1427
|
```
|
|
1314
1428
|
|
|
1429
|
+
The Docker example uses `docker compose stop odoo` and `docker compose up -d odoo` intentionally: only the Odoo service is stopped and restarted, while PostgreSQL remains available for the restore operations.
|
|
1430
|
+
|
|
1315
1431
|
### `completion`
|
|
1316
1432
|
|
|
1317
1433
|
Generate shell-completion definitions for either CLI. See [Shell completion](#shell-completion) under Installation for activation instructions.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.25.2"
|
|
@@ -9,6 +9,8 @@ from __future__ import annotations
|
|
|
9
9
|
|
|
10
10
|
import argparse
|
|
11
11
|
import configparser
|
|
12
|
+
import contextlib
|
|
13
|
+
import ctypes
|
|
12
14
|
import hashlib
|
|
13
15
|
import io
|
|
14
16
|
import json
|
|
@@ -18,11 +20,13 @@ import platform
|
|
|
18
20
|
import re
|
|
19
21
|
import shlex
|
|
20
22
|
import shutil
|
|
23
|
+
import signal
|
|
21
24
|
import socket
|
|
22
25
|
import stat
|
|
23
26
|
import subprocess
|
|
24
27
|
import sys
|
|
25
28
|
import tempfile
|
|
29
|
+
import time
|
|
26
30
|
import zipfile
|
|
27
31
|
from dataclasses import asdict, dataclass, field, replace
|
|
28
32
|
from datetime import datetime, timezone
|
|
@@ -190,6 +194,30 @@ _INI_SET_DOCKER_SHARE_SUPPORTED_OPTIONS = {
|
|
|
190
194
|
"target",
|
|
191
195
|
}
|
|
192
196
|
|
|
197
|
+
_NATIVE_RUNTIME_STATE = "odoo-process.json"
|
|
198
|
+
_NATIVE_RUNTIME_LOCK = "odoo-process.lock"
|
|
199
|
+
_NATIVE_RUNTIME_LOG = "odoo-up.log"
|
|
200
|
+
|
|
201
|
+
_CLI_COMMANDS = (
|
|
202
|
+
("setup", "Create or update an Odoo workspace"),
|
|
203
|
+
("up", "Prepare and start the Odoo workspace"),
|
|
204
|
+
("down", "Stop the Odoo workspace (keep data)"),
|
|
205
|
+
("status", "Show the Odoo runtime status"),
|
|
206
|
+
("completion", "Generate shell completion script (bash, zsh, fish, powershell)"),
|
|
207
|
+
("run", "Run Odoo"),
|
|
208
|
+
("shell", "Open an Odoo shell"),
|
|
209
|
+
("update", "Update installed Odoo modules"),
|
|
210
|
+
("test", "Run Odoo tests"),
|
|
211
|
+
("neutralize", "Neutralize the workspace database"),
|
|
212
|
+
("backup-db", "Back up the database"),
|
|
213
|
+
("restore-db", "Restore the database"),
|
|
214
|
+
("backup-filestore", "Back up the filestore"),
|
|
215
|
+
("restore-filestore", "Restore the filestore"),
|
|
216
|
+
("restic", "Run a Restic command"),
|
|
217
|
+
("backup-filestore-restic", "Back up the filestore using Restic"),
|
|
218
|
+
("restore-filestore-restic", "Restore a Restic filestore snapshot"),
|
|
219
|
+
)
|
|
220
|
+
|
|
193
221
|
|
|
194
222
|
# -----------------------------
|
|
195
223
|
# Data models
|
|
@@ -3291,7 +3319,7 @@ COPY --chown=odoo:odoo odoo/ {_DOCKER_ODOO_CONTAINER_ROOT.as_posix()}/
|
|
|
3291
3319
|
workspace_runtime_step = f"""
|
|
3292
3320
|
ENV PYTHONPATH={_DOCKER_ODOO_CONTAINER_ROOT.as_posix()}
|
|
3293
3321
|
|
|
3294
|
-
RUN printf '%s\\n' '#!/bin/sh' 'exec {_DOCKER_ODOO_CONTAINER_ROOT.as_posix()}/odoo-bin "$@"' > /usr/local/bin/odoo \\
|
|
3322
|
+
RUN printf '%s\\n' '#!/bin/sh' 'exec python3 {_DOCKER_ODOO_CONTAINER_ROOT.as_posix()}/odoo-bin "$@"' > /usr/local/bin/odoo \\
|
|
3295
3323
|
&& chmod 0755 /usr/local/bin/odoo
|
|
3296
3324
|
"""
|
|
3297
3325
|
|
|
@@ -5447,26 +5475,6 @@ def _prepare_included_project_ini(
|
|
|
5447
5475
|
return output_path
|
|
5448
5476
|
|
|
5449
5477
|
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
_CLI_COMMANDS = (
|
|
5453
|
-
("setup", "Create or update an Odoo workspace"),
|
|
5454
|
-
("completion", "Generate shell completion script (bash, zsh, fish, powershell)"),
|
|
5455
|
-
("run", "Run Odoo"),
|
|
5456
|
-
("shell", "Open an Odoo shell"),
|
|
5457
|
-
("update", "Update installed Odoo modules"),
|
|
5458
|
-
("test", "Run Odoo tests"),
|
|
5459
|
-
("neutralize", "Neutralize the workspace database"),
|
|
5460
|
-
("backup-db", "Back up the database"),
|
|
5461
|
-
("restore-db", "Restore the database"),
|
|
5462
|
-
("backup-filestore", "Back up the filestore"),
|
|
5463
|
-
("restore-filestore", "Restore the filestore"),
|
|
5464
|
-
("restic", "Run a Restic command"),
|
|
5465
|
-
("backup-filestore-restic", "Back up the filestore using Restic"),
|
|
5466
|
-
("restore-filestore-restic", "Restore a Restic filestore snapshot"),
|
|
5467
|
-
)
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
5478
|
def build_parser(prog: str = "odoo-compose", target: str = "docker", *, setup_command: bool = False) -> argparse.ArgumentParser:
|
|
5471
5479
|
"""Build the top-level command list or the full workspace setup parser."""
|
|
5472
5480
|
if not setup_command:
|
|
@@ -6383,6 +6391,317 @@ def _run_odoo_cli(target: str, action: str, args: list[str], prog: str) -> int:
|
|
|
6383
6391
|
return 130
|
|
6384
6392
|
|
|
6385
6393
|
|
|
6394
|
+
# -----------------------------
|
|
6395
|
+
# Workspace lifecycle: Docker Compose and native virtualenv
|
|
6396
|
+
# -----------------------------
|
|
6397
|
+
|
|
6398
|
+
def _build_lifecycle_parser(action: str, prog: str, target: str) -> argparse.ArgumentParser:
|
|
6399
|
+
descriptions = {
|
|
6400
|
+
"up": "Regenerate the workspace configuration and start Odoo in the background.",
|
|
6401
|
+
"down": "Stop the running workspace without deleting databases or filestores.",
|
|
6402
|
+
"status": "Show the current workspace runtime status without changing anything.",
|
|
6403
|
+
}
|
|
6404
|
+
parser = argparse.ArgumentParser(prog=f"{prog} {action}", description=descriptions[action])
|
|
6405
|
+
parser.add_argument("--root", default=".", metavar="ROOT", help="Existing workspace root (default: current directory).")
|
|
6406
|
+
if action == "up" and target == "docker":
|
|
6407
|
+
parser.add_argument("--build", action="store_true", help="Build the Docker image before starting services.")
|
|
6408
|
+
parser.add_argument("--recreate", action="store_true", help="Force recreation of the Odoo container only.")
|
|
6409
|
+
return parser
|
|
6410
|
+
|
|
6411
|
+
|
|
6412
|
+
def _native_runtime_paths(root: Path) -> tuple[Path, Path, Path]:
|
|
6413
|
+
return (root / ".odt-env" / _NATIVE_RUNTIME_STATE,
|
|
6414
|
+
root / ".odt-env" / _NATIVE_RUNTIME_LOCK,
|
|
6415
|
+
root / "odoo-logs" / _NATIVE_RUNTIME_LOG)
|
|
6416
|
+
|
|
6417
|
+
|
|
6418
|
+
@contextlib.contextmanager
|
|
6419
|
+
def _native_runtime_lock(root: Path):
|
|
6420
|
+
"""Serialize up/down across CLI invocations; never leave a stale lock owner."""
|
|
6421
|
+
_, lock_path, _ = _native_runtime_paths(root)
|
|
6422
|
+
lock_path.parent.mkdir(parents=True, exist_ok=True)
|
|
6423
|
+
with lock_path.open("a+b") as lock_file:
|
|
6424
|
+
if os.name == "nt":
|
|
6425
|
+
import msvcrt
|
|
6426
|
+
lock_file.seek(0)
|
|
6427
|
+
lock_file.write(b"0")
|
|
6428
|
+
lock_file.flush()
|
|
6429
|
+
lock_file.seek(0)
|
|
6430
|
+
try:
|
|
6431
|
+
msvcrt.locking(lock_file.fileno(), msvcrt.LK_NBLCK, 1)
|
|
6432
|
+
except OSError as exc:
|
|
6433
|
+
raise _WorkspaceOperationError("another native lifecycle operation is in progress") from exc
|
|
6434
|
+
else:
|
|
6435
|
+
import fcntl
|
|
6436
|
+
try:
|
|
6437
|
+
fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
6438
|
+
except OSError as exc:
|
|
6439
|
+
raise _WorkspaceOperationError("another native lifecycle operation is in progress") from exc
|
|
6440
|
+
try:
|
|
6441
|
+
yield
|
|
6442
|
+
finally:
|
|
6443
|
+
if os.name == "nt":
|
|
6444
|
+
lock_file.seek(0)
|
|
6445
|
+
msvcrt.locking(lock_file.fileno(), msvcrt.LK_UNLCK, 1)
|
|
6446
|
+
else:
|
|
6447
|
+
fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)
|
|
6448
|
+
|
|
6449
|
+
|
|
6450
|
+
def _native_windows_process_identity(pid: int) -> Optional[str]:
|
|
6451
|
+
"""Read the creation time of a live Windows process to guard against PID reuse."""
|
|
6452
|
+
from ctypes import wintypes
|
|
6453
|
+
kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
|
|
6454
|
+
kernel32.OpenProcess.argtypes = [wintypes.DWORD, wintypes.BOOL, wintypes.DWORD]
|
|
6455
|
+
kernel32.OpenProcess.restype = wintypes.HANDLE
|
|
6456
|
+
kernel32.GetExitCodeProcess.argtypes = [wintypes.HANDLE, ctypes.POINTER(wintypes.DWORD)]
|
|
6457
|
+
kernel32.GetExitCodeProcess.restype = wintypes.BOOL
|
|
6458
|
+
kernel32.GetProcessTimes.argtypes = [wintypes.HANDLE] + [ctypes.POINTER(wintypes.FILETIME)] * 4
|
|
6459
|
+
kernel32.GetProcessTimes.restype = wintypes.BOOL
|
|
6460
|
+
kernel32.CloseHandle.argtypes = [wintypes.HANDLE]
|
|
6461
|
+
kernel32.CloseHandle.restype = wintypes.BOOL
|
|
6462
|
+
handle = kernel32.OpenProcess(0x1000, False, pid) # PROCESS_QUERY_LIMITED_INFORMATION
|
|
6463
|
+
if not handle:
|
|
6464
|
+
return None
|
|
6465
|
+
try:
|
|
6466
|
+
status = wintypes.DWORD()
|
|
6467
|
+
creation, exit_time, kernel_time, user_time = (wintypes.FILETIME() for _ in range(4))
|
|
6468
|
+
if not kernel32.GetExitCodeProcess(handle, ctypes.byref(status)) or status.value != 259:
|
|
6469
|
+
return None # STILL_ACTIVE
|
|
6470
|
+
if not kernel32.GetProcessTimes(handle, ctypes.byref(creation), ctypes.byref(exit_time),
|
|
6471
|
+
ctypes.byref(kernel_time), ctypes.byref(user_time)):
|
|
6472
|
+
return None
|
|
6473
|
+
return f"windows:{(creation.dwHighDateTime << 32) | creation.dwLowDateTime}"
|
|
6474
|
+
finally:
|
|
6475
|
+
kernel32.CloseHandle(handle)
|
|
6476
|
+
|
|
6477
|
+
|
|
6478
|
+
def _native_process_identity(pid: int) -> Optional[str]:
|
|
6479
|
+
"""Return a stable per-process creation token; refuse unverified PID reuse."""
|
|
6480
|
+
if not isinstance(pid, int) or pid <= 0:
|
|
6481
|
+
return None
|
|
6482
|
+
if sys.platform.startswith("linux"):
|
|
6483
|
+
try:
|
|
6484
|
+
raw = Path(f"/proc/{pid}/stat").read_text(encoding="utf-8")
|
|
6485
|
+
fields = raw[raw.rfind(")") + 2:].split()
|
|
6486
|
+
if fields[0] in ("Z", "X"):
|
|
6487
|
+
return None
|
|
6488
|
+
try:
|
|
6489
|
+
boot_id = Path("/proc/sys/kernel/random/boot_id").read_text(encoding="ascii").strip()
|
|
6490
|
+
except OSError:
|
|
6491
|
+
boot_id = "unknown"
|
|
6492
|
+
return f"linux:{boot_id}:{fields[19]}" # field 22: starttime in clock ticks
|
|
6493
|
+
except (OSError, IndexError, ValueError):
|
|
6494
|
+
return None
|
|
6495
|
+
if os.name == "nt":
|
|
6496
|
+
return _native_windows_process_identity(pid)
|
|
6497
|
+
# macOS and other POSIX systems: start timestamp from ps.
|
|
6498
|
+
result = subprocess.run(["ps", "-p", str(pid), "-o", "lstart="],
|
|
6499
|
+
text=True, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, check=False)
|
|
6500
|
+
created = result.stdout.strip()
|
|
6501
|
+
return f"posix:{created}" if result.returncode == 0 and created else None
|
|
6502
|
+
|
|
6503
|
+
|
|
6504
|
+
def _native_read_process(root: Path) -> Optional[dict[str, Any]]:
|
|
6505
|
+
state_path, _, _ = _native_runtime_paths(root)
|
|
6506
|
+
try:
|
|
6507
|
+
state = json.loads(state_path.read_text(encoding="utf-8"))
|
|
6508
|
+
except FileNotFoundError:
|
|
6509
|
+
return None
|
|
6510
|
+
except (ValueError, OSError) as exc:
|
|
6511
|
+
raise _WorkspaceOperationError(f"cannot read native process state: {state_path}") from exc
|
|
6512
|
+
if not isinstance(state, dict) or not isinstance(state.get("pid"), int) or not isinstance(state.get("identity"), str):
|
|
6513
|
+
raise _WorkspaceOperationError(f"invalid native process state: {state_path}")
|
|
6514
|
+
# Compare filesystem identity instead of path spelling: Windows 8.3 aliases
|
|
6515
|
+
# and symlinks may represent the same workspace under different names.
|
|
6516
|
+
saved_root = state.get("root")
|
|
6517
|
+
try:
|
|
6518
|
+
same_workspace = isinstance(saved_root, str) and Path(saved_root).samefile(root)
|
|
6519
|
+
except (OSError, ValueError):
|
|
6520
|
+
same_workspace = False
|
|
6521
|
+
if not same_workspace:
|
|
6522
|
+
raise _WorkspaceOperationError("native process state belongs to a different workspace")
|
|
6523
|
+
return state
|
|
6524
|
+
|
|
6525
|
+
|
|
6526
|
+
def _native_process_running(state: dict[str, Any]) -> bool:
|
|
6527
|
+
return _native_process_identity(state["pid"]) == state["identity"]
|
|
6528
|
+
|
|
6529
|
+
|
|
6530
|
+
def _native_wait_stopped(state: dict[str, Any], timeout: float) -> bool:
|
|
6531
|
+
deadline = time.monotonic() + timeout
|
|
6532
|
+
while time.monotonic() < deadline:
|
|
6533
|
+
if not _native_process_running(state):
|
|
6534
|
+
return True
|
|
6535
|
+
time.sleep(0.2)
|
|
6536
|
+
return not _native_process_running(state)
|
|
6537
|
+
|
|
6538
|
+
|
|
6539
|
+
def _native_stop_process(state: dict[str, Any]) -> None:
|
|
6540
|
+
"""Only terminate our verified Odoo process/process group, never arbitrary PID matches."""
|
|
6541
|
+
if not _native_process_running(state):
|
|
6542
|
+
return
|
|
6543
|
+
pid = state["pid"]
|
|
6544
|
+
if os.name == "nt":
|
|
6545
|
+
# A new process group permits a graceful CTRL_BREAK when a console exists.
|
|
6546
|
+
try:
|
|
6547
|
+
os.kill(pid, signal.CTRL_BREAK_EVENT)
|
|
6548
|
+
except OSError:
|
|
6549
|
+
# A non-interactive Windows session may have no console to signal.
|
|
6550
|
+
subprocess.run(["taskkill", "/PID", str(pid), "/T"],
|
|
6551
|
+
check=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
6552
|
+
else:
|
|
6553
|
+
# up starts a dedicated session; refuse to signal any unrelated group.
|
|
6554
|
+
if os.getpgid(pid) != pid:
|
|
6555
|
+
raise _WorkspaceOperationError("Odoo is no longer the leader of its recorded process group")
|
|
6556
|
+
os.killpg(pid, signal.SIGTERM)
|
|
6557
|
+
if _native_wait_stopped(state, 10):
|
|
6558
|
+
return
|
|
6559
|
+
if not _native_process_running(state):
|
|
6560
|
+
return
|
|
6561
|
+
if os.name == "nt":
|
|
6562
|
+
subprocess.run(["taskkill", "/F", "/T", "/PID", str(pid)],
|
|
6563
|
+
check=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
6564
|
+
else:
|
|
6565
|
+
if os.getpgid(pid) != pid:
|
|
6566
|
+
raise _WorkspaceOperationError("Odoo process group changed while stopping")
|
|
6567
|
+
os.killpg(pid, signal.SIGKILL)
|
|
6568
|
+
if not _native_wait_stopped(state, 5):
|
|
6569
|
+
raise _WorkspaceOperationError(f"Odoo process {pid} did not stop")
|
|
6570
|
+
|
|
6571
|
+
|
|
6572
|
+
def _native_start_process(root: Path, cfg: ProjectConfig, conf: Path) -> dict[str, Any]:
|
|
6573
|
+
layout = Layout.from_root(root)
|
|
6574
|
+
python = root / "venv" / ("Scripts/python.exe" if os.name == "nt" else "bin/python")
|
|
6575
|
+
odoo_bin = _resolve_odoo_path(layout, cfg.odoo) / "odoo-bin"
|
|
6576
|
+
if not python.is_file() or not odoo_bin.is_file():
|
|
6577
|
+
raise _WorkspaceOperationError("native Odoo Python or odoo-bin not found; run setup first")
|
|
6578
|
+
if not conf.is_file():
|
|
6579
|
+
raise _WorkspaceOperationError("generated native Odoo config is missing")
|
|
6580
|
+
state_path, _, log_path = _native_runtime_paths(root)
|
|
6581
|
+
log_path.parent.mkdir(parents=True, exist_ok=True)
|
|
6582
|
+
command = [str(python), str(odoo_bin), "-c", str(conf)]
|
|
6583
|
+
flags = subprocess.CREATE_NEW_PROCESS_GROUP if os.name == "nt" else 0
|
|
6584
|
+
with log_path.open("ab") as log_file:
|
|
6585
|
+
proc = subprocess.Popen(command, cwd=root, stdin=subprocess.DEVNULL,
|
|
6586
|
+
stdout=log_file, stderr=subprocess.STDOUT, close_fds=True,
|
|
6587
|
+
creationflags=flags, start_new_session=(os.name != "nt"))
|
|
6588
|
+
# A process may exit immediately (bad interpreter, invalid config, etc.).
|
|
6589
|
+
time.sleep(0.3)
|
|
6590
|
+
identity = _native_process_identity(proc.pid)
|
|
6591
|
+
if proc.poll() is not None or identity is None:
|
|
6592
|
+
raise _WorkspaceOperationError(f"Odoo exited during startup; check {log_path}")
|
|
6593
|
+
state = {"pid": proc.pid, "identity": identity, "root": str(root),
|
|
6594
|
+
"config_sha256": _sha256_file(conf)}
|
|
6595
|
+
try:
|
|
6596
|
+
_write_json_atomic(state_path, state)
|
|
6597
|
+
except Exception:
|
|
6598
|
+
# Never leave a detached process unmanaged if recording it fails.
|
|
6599
|
+
_native_stop_process(state)
|
|
6600
|
+
raise
|
|
6601
|
+
print(f"Odoo started (PID {proc.pid}); logs: {log_path}")
|
|
6602
|
+
return state
|
|
6603
|
+
|
|
6604
|
+
|
|
6605
|
+
def _run_lifecycle_cli(target: str, action: str, args: list[str], prog: str) -> int:
|
|
6606
|
+
parser = _build_lifecycle_parser(action, prog, target)
|
|
6607
|
+
opts = parser.parse_args(args)
|
|
6608
|
+
root = Path(opts.root).expanduser().resolve()
|
|
6609
|
+
if not root.is_dir():
|
|
6610
|
+
parser.error(f"workspace ROOT does not exist: {root}")
|
|
6611
|
+
ini = root / _DEFAULT_PROJECT_INI_NAME
|
|
6612
|
+
if not ini.is_file():
|
|
6613
|
+
parser.error(f"workspace project file not found: {ini}; run setup first")
|
|
6614
|
+
try:
|
|
6615
|
+
if target == "docker":
|
|
6616
|
+
compose = _workspace_docker_command() + ["compose"]
|
|
6617
|
+
if action == "up":
|
|
6618
|
+
conf = _docker_odoo_conf_path(Layout.from_root(root).docker_local_dir)
|
|
6619
|
+
runtime_path = root / ".odt-env" / "docker-odoo-runtime.json"
|
|
6620
|
+
try:
|
|
6621
|
+
previous = json.loads(runtime_path.read_text(encoding="utf-8"))
|
|
6622
|
+
if not isinstance(previous, dict):
|
|
6623
|
+
previous = {}
|
|
6624
|
+
except (OSError, ValueError):
|
|
6625
|
+
previous = {}
|
|
6626
|
+
# Provision existing sources only; never clone repos, install packages or create a new project.
|
|
6627
|
+
sync_project(ini, sync_odoo=False, sync_addons=False, root_override=root,
|
|
6628
|
+
target="docker", no_provisioning_log=True)
|
|
6629
|
+
new_hash = _sha256_file(conf)
|
|
6630
|
+
preexisting = subprocess.run(compose + ["ps", "-q", "odoo"], cwd=root,
|
|
6631
|
+
text=True, capture_output=True, check=False)
|
|
6632
|
+
if preexisting.returncode:
|
|
6633
|
+
raise _WorkspaceOperationError("could not query Docker Compose Odoo container")
|
|
6634
|
+
up = compose + ["up", "-d", "--wait"]
|
|
6635
|
+
if opts.build:
|
|
6636
|
+
up.append("--build")
|
|
6637
|
+
_workspace_command(up, root)
|
|
6638
|
+
current = subprocess.run(compose + ["ps", "-q", "odoo"], cwd=root,
|
|
6639
|
+
text=True, capture_output=True, check=False)
|
|
6640
|
+
if current.returncode or not current.stdout.strip():
|
|
6641
|
+
raise _WorkspaceOperationError("could not find running Docker Compose Odoo container")
|
|
6642
|
+
container_id = current.stdout.strip()
|
|
6643
|
+
if (preexisting.stdout.strip() == container_id and
|
|
6644
|
+
(opts.recreate or previous.get("config_sha256") != new_hash or
|
|
6645
|
+
previous.get("container_id") != container_id)):
|
|
6646
|
+
# A bind-mounted odoo.conf change is invisible to Compose config hashing.
|
|
6647
|
+
# Recreate only Odoo; do not restart PostgreSQL or delete its data.
|
|
6648
|
+
_workspace_command(compose + ["up", "-d", "--wait", "--no-deps",
|
|
6649
|
+
"--force-recreate", "odoo"], root)
|
|
6650
|
+
current = subprocess.run(compose + ["ps", "-q", "odoo"], cwd=root,
|
|
6651
|
+
text=True, capture_output=True, check=False)
|
|
6652
|
+
if current.returncode or not current.stdout.strip():
|
|
6653
|
+
raise _WorkspaceOperationError("could not query recreated Odoo container")
|
|
6654
|
+
container_id = current.stdout.strip()
|
|
6655
|
+
_write_json_atomic(runtime_path, {"container_id": container_id, "config_sha256": new_hash})
|
|
6656
|
+
return 0
|
|
6657
|
+
if action == "down":
|
|
6658
|
+
_workspace_command(compose + ["down"], root)
|
|
6659
|
+
(root / ".odt-env" / "docker-odoo-runtime.json").unlink(missing_ok=True)
|
|
6660
|
+
return 0
|
|
6661
|
+
_workspace_command(compose + ["ps", "-a"], root)
|
|
6662
|
+
return 0
|
|
6663
|
+
|
|
6664
|
+
state_path, _, log_path = _native_runtime_paths(root)
|
|
6665
|
+
if action == "status":
|
|
6666
|
+
state = _native_read_process(root)
|
|
6667
|
+
if state and _native_process_running(state):
|
|
6668
|
+
print(f"Odoo: running (PID {state['pid']})\nLogs: {log_path}")
|
|
6669
|
+
else:
|
|
6670
|
+
print("Odoo: stopped" + (" (stale process state)" if state else ""))
|
|
6671
|
+
return 0
|
|
6672
|
+
with _native_runtime_lock(root):
|
|
6673
|
+
state = _native_read_process(root)
|
|
6674
|
+
running = bool(state and _native_process_running(state))
|
|
6675
|
+
if action == "down":
|
|
6676
|
+
if running:
|
|
6677
|
+
_native_stop_process(state)
|
|
6678
|
+
print("Odoo: stopped")
|
|
6679
|
+
else:
|
|
6680
|
+
print("Odoo: already stopped")
|
|
6681
|
+
state_path.unlink(missing_ok=True)
|
|
6682
|
+
return 0
|
|
6683
|
+
conf = Layout.from_root(root).conf_path
|
|
6684
|
+
# Native up requires a provisioned environment. Never recreate the venv.
|
|
6685
|
+
if not (root / "venv" / ("Scripts/python.exe" if os.name == "nt" else "bin/python")).is_file():
|
|
6686
|
+
raise _WorkspaceOperationError("native virtualenv not found; run setup --create-venv first")
|
|
6687
|
+
sync_project(ini, sync_odoo=False, sync_addons=False, root_override=root,
|
|
6688
|
+
target="venv", no_provisioning_log=True)
|
|
6689
|
+
if running and state.get("config_sha256") == _sha256_file(conf):
|
|
6690
|
+
print(f"Odoo: already running (PID {state['pid']})")
|
|
6691
|
+
return 0
|
|
6692
|
+
cfg = load_project_config(ini, log_resolved=False, log_overrides=False)
|
|
6693
|
+
if running:
|
|
6694
|
+
_native_stop_process(state)
|
|
6695
|
+
state_path.unlink(missing_ok=True)
|
|
6696
|
+
elif state:
|
|
6697
|
+
state_path.unlink(missing_ok=True) # never signal a reused PID
|
|
6698
|
+
_native_start_process(root, cfg, conf)
|
|
6699
|
+
return 0
|
|
6700
|
+
except (OSError, ValueError, configparser.Error, _WorkspaceOperationError) as exc:
|
|
6701
|
+
_logger.error("Could not %s workspace: %s", action, exc)
|
|
6702
|
+
return 1
|
|
6703
|
+
|
|
6704
|
+
|
|
6386
6705
|
def _completion_options(parser: argparse.ArgumentParser) -> list[str]:
|
|
6387
6706
|
return list(dict.fromkeys(option for action in parser._actions for option in action.option_strings))
|
|
6388
6707
|
|
|
@@ -6642,6 +6961,7 @@ def _run_completion_cli(target: str, args: list[str], prog: str) -> int:
|
|
|
6642
6961
|
parsers = {
|
|
6643
6962
|
"": build_parser(prog, target),
|
|
6644
6963
|
"setup": build_parser(f"{prog} setup", target, setup_command=True),
|
|
6964
|
+
**{name: _build_lifecycle_parser(name, prog, target) for name in ("up", "down", "status")},
|
|
6645
6965
|
"completion": parser,
|
|
6646
6966
|
**{name: _build_odoo_parser(name, prog) for name in ("run", "shell", "update", "test")},
|
|
6647
6967
|
"neutralize": _build_neutralize_parser(prog),
|
|
@@ -6658,6 +6978,8 @@ def main(target: str = "docker", prog: str = "odoo-compose") -> None:
|
|
|
6658
6978
|
stream=sys.stdout,
|
|
6659
6979
|
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
|
|
6660
6980
|
)
|
|
6981
|
+
if sys.argv[1:2] and sys.argv[1] in ("up", "down", "status"):
|
|
6982
|
+
raise SystemExit(_run_lifecycle_cli(target, sys.argv[1], sys.argv[2:], prog))
|
|
6661
6983
|
if sys.argv[1:2] == ["completion"]:
|
|
6662
6984
|
raise SystemExit(_run_completion_cli(target, sys.argv[2:], prog))
|
|
6663
6985
|
if sys.argv[1:2] and sys.argv[1] in ("run", "shell", "update", "test"):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: odoo-devops-tools
|
|
3
|
-
Version: 1.25.
|
|
3
|
+
Version: 1.25.2
|
|
4
4
|
Summary: Build reproducible Odoo workspaces from a single project definition.
|
|
5
5
|
Author: Roman Lacko
|
|
6
6
|
License-Expression: MIT
|
|
@@ -174,11 +174,11 @@ This creates `./odoo19/odoo-project.ini` with this initial content:
|
|
|
174
174
|
version = 19.0
|
|
175
175
|
```
|
|
176
176
|
|
|
177
|
-
Then start the Docker Compose environment:
|
|
177
|
+
Then prepare and start the Docker Compose environment:
|
|
178
178
|
|
|
179
179
|
```bash
|
|
180
180
|
cd ./odoo19
|
|
181
|
-
|
|
181
|
+
odoo-compose up --build
|
|
182
182
|
```
|
|
183
183
|
|
|
184
184
|
Optionally initialize the Odoo database from the command line instead of through the web interface:
|
|
@@ -194,7 +194,7 @@ Docker Compose runs both Odoo and PostgreSQL. Odoo is available at http://localh
|
|
|
194
194
|
>
|
|
195
195
|
> ```bash
|
|
196
196
|
> odoo-compose setup --set docker:restart=no
|
|
197
|
-
>
|
|
197
|
+
> odoo-compose up --build
|
|
198
198
|
> ```
|
|
199
199
|
|
|
200
200
|
### 1.1. Adding extra addons
|
|
@@ -217,7 +217,7 @@ Sync the addon repositories and regenerate the Docker environment:
|
|
|
217
217
|
|
|
218
218
|
```bash
|
|
219
219
|
odoo-compose setup --sync-addons
|
|
220
|
-
|
|
220
|
+
odoo-compose up --build
|
|
221
221
|
```
|
|
222
222
|
|
|
223
223
|
The repositories are cloned into `ROOT/odoo-addons/` and bind-mounted into the Odoo container. Python dependencies from their `requirements.txt` files are included when rebuilding the Docker image.
|
|
@@ -256,7 +256,7 @@ Sync the Odoo source and generate the Docker artifacts:
|
|
|
256
256
|
|
|
257
257
|
```bash
|
|
258
258
|
odoo-compose setup --sync-all
|
|
259
|
-
|
|
259
|
+
odoo-compose up --build
|
|
260
260
|
```
|
|
261
261
|
|
|
262
262
|
The resolved Odoo source is bind-mounted read-only at `/opt/odoo`. The Odoo Python source from the base image is removed while its installed dependencies remain available.
|
|
@@ -267,14 +267,14 @@ Enable Mailpit in the local Docker workflow when you need to inspect or test ema
|
|
|
267
267
|
|
|
268
268
|
```bash
|
|
269
269
|
odoo-compose setup --set docker:mailpit_enabled=true
|
|
270
|
-
|
|
270
|
+
odoo-compose up
|
|
271
271
|
```
|
|
272
272
|
|
|
273
273
|
Mailpit is available at http://localhost:8025 by default. When running multiple workspaces, override the host web UI port, for example:
|
|
274
274
|
|
|
275
275
|
```bash
|
|
276
276
|
odoo-compose setup --set docker:mailpit_enabled=true --set docker:mailpit_webui_port=8026
|
|
277
|
-
|
|
277
|
+
odoo-compose up
|
|
278
278
|
```
|
|
279
279
|
|
|
280
280
|
The generated local Odoo configuration sends outgoing email to `mailpit:1025`. When Mailpit is enabled, local Docker SMTP connection settings (`smtp_server`, `smtp_port`, `smtp_ssl`, `smtp_user`, and `smtp_password`) are intentionally overridden; the deploy configuration keeps the values from `[config]` unchanged.
|
|
@@ -316,11 +316,10 @@ mailpit_mem_limit = 256m
|
|
|
316
316
|
|
|
317
317
|
Each option is independent and optional. If an option is missing or empty, `odoo-compose` omits that limit from the Compose service; no CPU or memory limit is imposed by the generator. CPU values must be positive decimal numbers (such as `0.5` or `2.0`). Memory limits must be positive amounts (such as `256m`, `2g`, or `4g`); zero and negative values are rejected. A memory limit is a hard container limit: exceeding it may cause an out-of-memory termination, so size it for your workload.
|
|
318
318
|
|
|
319
|
-
|
|
319
|
+
Apply the updated resource limits and start the services. `up` regenerates the Compose configuration automatically, so no separate `setup` or image rebuild is necessary:
|
|
320
320
|
|
|
321
321
|
```bash
|
|
322
|
-
odoo-compose
|
|
323
|
-
docker compose up -d --build --wait
|
|
322
|
+
odoo-compose up
|
|
324
323
|
```
|
|
325
324
|
|
|
326
325
|
### 1.6. Creating a Docker deploy build context
|
|
@@ -388,15 +387,23 @@ db_user = odoo
|
|
|
388
387
|
db_password = odoo
|
|
389
388
|
```
|
|
390
389
|
|
|
391
|
-
Then start Odoo
|
|
390
|
+
Then start Odoo in the background:
|
|
392
391
|
|
|
393
392
|
```bash
|
|
394
393
|
cd ./odoo19
|
|
395
|
-
odoo-compose-venv
|
|
394
|
+
odoo-compose-venv up
|
|
395
|
+
odoo-compose-venv status
|
|
396
396
|
```
|
|
397
397
|
|
|
398
398
|
Odoo starts with the generated configuration from `./odoo-configs/odoo-server.conf` and is available at http://localhost:8069.
|
|
399
399
|
|
|
400
|
+
Use `odoo-compose-venv run` instead to run in the foreground:
|
|
401
|
+
|
|
402
|
+
```bash
|
|
403
|
+
cd ./odoo19
|
|
404
|
+
odoo-compose-venv run
|
|
405
|
+
```
|
|
406
|
+
|
|
400
407
|
### 2.1. Adding extra addons
|
|
401
408
|
|
|
402
409
|
To extend Odoo with additional functionality, add extra addons through `[addons.<name>]` sections in `odoo-project.ini`.
|
|
@@ -614,6 +621,32 @@ This recreates `ROOT/venv`, skips dependency compilation and wheelhouse building
|
|
|
614
621
|
|
|
615
622
|
---
|
|
616
623
|
|
|
624
|
+
## Runtime lifecycle
|
|
625
|
+
|
|
626
|
+
Use the same lifecycle commands with either CLI after provisioning the workspace with `setup`:
|
|
627
|
+
|
|
628
|
+
```bash
|
|
629
|
+
# Docker: generate Docker config and start the Compose stack
|
|
630
|
+
odoo-compose up --build
|
|
631
|
+
odoo-compose status
|
|
632
|
+
odoo-compose down
|
|
633
|
+
|
|
634
|
+
# Native: generate config and start Odoo in the background
|
|
635
|
+
odoo-compose-venv up
|
|
636
|
+
odoo-compose-venv status
|
|
637
|
+
odoo-compose-venv down
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
`odoo-compose up` regenerates local workspace artifacts and then runs the equivalent of `docker compose up -d --wait`.
|
|
641
|
+
|
|
642
|
+
`odoo-compose up --build` additionally builds the images (`docker compose up -d --build --wait`).
|
|
643
|
+
|
|
644
|
+
Container orchestration remains with Docker Compose, and its CLI can still be used directly for advanced or service-specific operations.
|
|
645
|
+
|
|
646
|
+
`up` requires an existing `odoo-project.ini`. It regenerates local configuration without fetching Git repositories or recreating virtualenvs.
|
|
647
|
+
|
|
648
|
+
---
|
|
649
|
+
|
|
617
650
|
## Commands
|
|
618
651
|
|
|
619
652
|
`odoo-compose` operates on Docker workspaces; `odoo-compose-venv` operates on native Python virtualenv workspaces. Both use `ROOT/odoo-project.ini`, but generate their own workflow artifacts. Commands operate directly through the installed CLI; generated wrapper scripts are not required.
|
|
@@ -626,6 +659,15 @@ Running either CLI without a command displays help; workspace changes are perfor
|
|
|
626
659
|
<dt><a href="#setup"><code>setup</code></a></dt>
|
|
627
660
|
<dd><p>Create or update a workspace, optionally synchronizing sources and generating artifacts.</p></dd>
|
|
628
661
|
|
|
662
|
+
<dt><a href="#up"><code>up</code></a></dt>
|
|
663
|
+
<dd><p>Regenerate workspace artifacts and start Odoo in the background.</p></dd>
|
|
664
|
+
|
|
665
|
+
<dt><a href="#down"><code>down</code></a></dt>
|
|
666
|
+
<dd><p>Stop the runtime without deleting data.</p></dd>
|
|
667
|
+
|
|
668
|
+
<dt><a href="#status"><code>status</code></a></dt>
|
|
669
|
+
<dd><p>Show the current runtime state.</p></dd>
|
|
670
|
+
|
|
629
671
|
<dt><a href="#run"><code>run</code></a></dt>
|
|
630
672
|
<dd><p>Run Odoo with additional arguments.</p></dd>
|
|
631
673
|
|
|
@@ -791,6 +833,78 @@ odoo-compose-venv setup ./odoo19 --sync-all --create-venv
|
|
|
791
833
|
odoo-compose-venv setup ./odoo18 --set odoo:version=18.0 --sync-all --create-venv
|
|
792
834
|
```
|
|
793
835
|
|
|
836
|
+
### `up`
|
|
837
|
+
|
|
838
|
+
Prepare and start the selected workspace. Requires an existing `odoo-project.ini`. Docker regenerates the local Docker build context, Odoo config and `compose.yaml`, then starts services with Docker Compose; it waits for health checks. Native regenerates its configuration and starts Odoo in the background using the existing virtualenv. Neither variant automatically synchronizes repositories or rebuilds the native virtualenv.
|
|
839
|
+
|
|
840
|
+
**Usage**
|
|
841
|
+
|
|
842
|
+
```text
|
|
843
|
+
{odoo-compose|odoo-compose-venv} up [--root ROOT]
|
|
844
|
+
odoo-compose up [--root ROOT] [--build] [--recreate]
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
**Options**
|
|
848
|
+
|
|
849
|
+
<dl class="cli-reference">
|
|
850
|
+
<dt><code>--root</code> <i>ROOT</i></dt>
|
|
851
|
+
<dd><p>Select an existing workspace (default: current directory).</p></dd>
|
|
852
|
+
|
|
853
|
+
<dt><code>--build</code></dt>
|
|
854
|
+
<dd><p>Build Docker images before starting the services (Docker only).</p></dd>
|
|
855
|
+
|
|
856
|
+
<dt><code>--recreate</code></dt>
|
|
857
|
+
<dd><p>Force recreation of the Odoo container, without forcing recreation of the database container (Docker only).</p></dd>
|
|
858
|
+
</dl>
|
|
859
|
+
|
|
860
|
+
**Notes**
|
|
861
|
+
|
|
862
|
+
Docker `up` remembers the last applied generated Odoo configuration and recreates the Odoo container when it changes, including after a separate `setup`.
|
|
863
|
+
Running `up` without `--build` does not guarantee that changed Python dependencies or Dockerfile instructions are built into the image.
|
|
864
|
+
Native `up` uses `ROOT/.odt-env/odoo-process.json` for process identity and writes runtime output to `ROOT/odoo-logs/odoo-up.log`; it restarts the tracked process if the generated config changes.
|
|
865
|
+
|
|
866
|
+
### `down`
|
|
867
|
+
|
|
868
|
+
Stop the selected workspace, preserving data. Docker delegates to `docker compose down` **without** `--volumes`; native gracefully stops only the verified Odoo process created by `odoo-compose-venv up`.
|
|
869
|
+
|
|
870
|
+
**Usage**
|
|
871
|
+
|
|
872
|
+
```text
|
|
873
|
+
{odoo-compose|odoo-compose-venv} down [--root ROOT]
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
**Options**
|
|
877
|
+
|
|
878
|
+
<dl class="cli-reference">
|
|
879
|
+
<dt><code>--root</code> <i>ROOT</i></dt>
|
|
880
|
+
<dd><p>Select an existing workspace (default: current directory).</p></dd>
|
|
881
|
+
</dl>
|
|
882
|
+
|
|
883
|
+
**Notes**
|
|
884
|
+
|
|
885
|
+
Native `down` never terminates arbitrary processes found by name or port. It does not stop PostgreSQL or a separately managed systemd service. Docker `down` removes Compose-managed containers and networks, but retains named volumes.
|
|
886
|
+
|
|
887
|
+
### `status`
|
|
888
|
+
|
|
889
|
+
Show runtime status without provisioning or changing the workspace. Docker displays the Docker Compose service table; native reports the tracked Odoo PID and whether the process is still running.
|
|
890
|
+
|
|
891
|
+
**Usage**
|
|
892
|
+
|
|
893
|
+
```text
|
|
894
|
+
{odoo-compose|odoo-compose-venv} status [--root ROOT]
|
|
895
|
+
```
|
|
896
|
+
|
|
897
|
+
**Options**
|
|
898
|
+
|
|
899
|
+
<dl class="cli-reference">
|
|
900
|
+
<dt><code>--root</code> <i>ROOT</i></dt>
|
|
901
|
+
<dd><p>Select an existing workspace (default: current directory).</p></dd>
|
|
902
|
+
</dl>
|
|
903
|
+
|
|
904
|
+
**Notes**
|
|
905
|
+
|
|
906
|
+
Native `status` verifies the recorded process identity to avoid confusing a reused PID with Odoo. It does not probe the Odoo HTTP endpoint or report application health.
|
|
907
|
+
|
|
794
908
|
### `run`
|
|
795
909
|
|
|
796
910
|
Run Odoo directly from the selected workspace. Docker uses a one-off container; Venv uses the existing virtualenv and generated `odoo-server.conf`.
|
|
@@ -1340,6 +1454,8 @@ odoo-compose-venv update --all
|
|
|
1340
1454
|
odoo-compose-venv neutralize
|
|
1341
1455
|
```
|
|
1342
1456
|
|
|
1457
|
+
The Docker example uses `docker compose stop odoo` and `docker compose up -d odoo` intentionally: only the Odoo service is stopped and restarted, while PostgreSQL remains available for the restore operations.
|
|
1458
|
+
|
|
1343
1459
|
### `completion`
|
|
1344
1460
|
|
|
1345
1461
|
Generate shell-completion definitions for either CLI. See [Shell completion](#shell-completion) under Installation for activation instructions.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.25.0"
|
|
File without changes
|
{odoo_devops_tools-1.25.0 → odoo_devops_tools-1.25.2}/odoo_devops_tools.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{odoo_devops_tools-1.25.0 → odoo_devops_tools-1.25.2}/odoo_devops_tools.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{odoo_devops_tools-1.25.0 → odoo_devops_tools-1.25.2}/odoo_devops_tools.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|