odoo-devops-tools 1.24.2__tar.gz → 1.24.4__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.24.2 → odoo_devops_tools-1.24.4}/PKG-INFO +50 -20
- {odoo_devops_tools-1.24.2 → odoo_devops_tools-1.24.4}/README.md +49 -19
- odoo_devops_tools-1.24.4/odoo_devops_tools/__init__.py +1 -0
- {odoo_devops_tools-1.24.2 → odoo_devops_tools-1.24.4}/odoo_devops_tools/env.py +274 -32
- {odoo_devops_tools-1.24.2 → odoo_devops_tools-1.24.4}/odoo_devops_tools.egg-info/PKG-INFO +50 -20
- {odoo_devops_tools-1.24.2 → odoo_devops_tools-1.24.4}/pyproject.toml +1 -1
- odoo_devops_tools-1.24.2/odoo_devops_tools/__init__.py +0 -1
- {odoo_devops_tools-1.24.2 → odoo_devops_tools-1.24.4}/LICENSE +0 -0
- {odoo_devops_tools-1.24.2 → odoo_devops_tools-1.24.4}/odoo_devops_tools.egg-info/SOURCES.txt +0 -0
- {odoo_devops_tools-1.24.2 → odoo_devops_tools-1.24.4}/odoo_devops_tools.egg-info/dependency_links.txt +0 -0
- {odoo_devops_tools-1.24.2 → odoo_devops_tools-1.24.4}/odoo_devops_tools.egg-info/entry_points.txt +0 -0
- {odoo_devops_tools-1.24.2 → odoo_devops_tools-1.24.4}/odoo_devops_tools.egg-info/top_level.txt +0 -0
- {odoo_devops_tools-1.24.2 → odoo_devops_tools-1.24.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: odoo-devops-tools
|
|
3
|
-
Version: 1.24.
|
|
3
|
+
Version: 1.24.4
|
|
4
4
|
Summary: Build reproducible Odoo workspaces from a single project definition.
|
|
5
5
|
Author: Roman Lacko
|
|
6
6
|
License-Expression: MIT
|
|
@@ -134,7 +134,7 @@ Then start the Docker Compose environment:
|
|
|
134
134
|
|
|
135
135
|
```bash
|
|
136
136
|
cd ./odoo19
|
|
137
|
-
docker compose up -d --build
|
|
137
|
+
docker compose up -d --build --wait
|
|
138
138
|
```
|
|
139
139
|
|
|
140
140
|
Optionally initialize the Odoo database from the command line instead of through the web interface:
|
|
@@ -174,7 +174,7 @@ Sync the configured addon repositories and refresh the generated Docker workflow
|
|
|
174
174
|
|
|
175
175
|
```bash
|
|
176
176
|
odoo-compose --sync-addons
|
|
177
|
-
docker compose up -d --build
|
|
177
|
+
docker compose up -d --build --wait
|
|
178
178
|
```
|
|
179
179
|
|
|
180
180
|
The addon repositories are cloned into `ROOT/odoo-addons/oca-web/` and `ROOT/odoo-addons/oca-helpdesk/` and bind-mounted into the Odoo container.
|
|
@@ -215,7 +215,7 @@ Sync the Odoo source and generate the Docker artifacts:
|
|
|
215
215
|
|
|
216
216
|
```bash
|
|
217
217
|
odoo-compose --sync-all
|
|
218
|
-
docker compose up -d --build
|
|
218
|
+
docker compose up -d --build --wait
|
|
219
219
|
```
|
|
220
220
|
|
|
221
221
|
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.
|
|
@@ -226,14 +226,14 @@ Enable Mailpit in the local Docker workflow when you need to inspect or test ema
|
|
|
226
226
|
|
|
227
227
|
```bash
|
|
228
228
|
odoo-compose --set docker:mailpit_enabled=true
|
|
229
|
-
docker compose up -d --build
|
|
229
|
+
docker compose up -d --build --wait
|
|
230
230
|
```
|
|
231
231
|
|
|
232
232
|
Mailpit is available at http://localhost:8025 by default. When running multiple workspaces, override the host web UI port, for example:
|
|
233
233
|
|
|
234
234
|
```bash
|
|
235
235
|
odoo-compose --set docker:mailpit_enabled=true --set docker:mailpit_webui_port=8026
|
|
236
|
-
docker compose up -d --build
|
|
236
|
+
docker compose up -d --build --wait
|
|
237
237
|
```
|
|
238
238
|
|
|
239
239
|
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.
|
|
@@ -256,7 +256,33 @@ target = /mnt/exchange
|
|
|
256
256
|
|
|
257
257
|
`odoo-compose` does not create the source directory or change its ownership or permissions; host provisioning or the deployment process must prepare the directory before Docker starts.
|
|
258
258
|
|
|
259
|
-
### 1.5.
|
|
259
|
+
### 1.5. Limiting Docker Compose resources
|
|
260
|
+
|
|
261
|
+
You can optionally set CPU and memory limits for each service in the generated `compose.yaml` using `[docker]` in `odoo-project.ini`:
|
|
262
|
+
|
|
263
|
+
```ini
|
|
264
|
+
[docker]
|
|
265
|
+
odoo_cpus = 2.0
|
|
266
|
+
odoo_mem_limit = 4g
|
|
267
|
+
|
|
268
|
+
db_cpus = 1.0
|
|
269
|
+
db_mem_limit = 2g
|
|
270
|
+
|
|
271
|
+
mailpit_enabled = true
|
|
272
|
+
mailpit_cpus = 0.5
|
|
273
|
+
mailpit_mem_limit = 256m
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
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.
|
|
277
|
+
|
|
278
|
+
Regenerate the Docker configuration after editing the INI, then start the services:
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
odoo-compose
|
|
282
|
+
docker compose up -d --build --wait
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### 1.6. Script reference
|
|
260
286
|
|
|
261
287
|
The Docker workflow generates platform-specific helper scripts under `ROOT/docker/local/scripts/`. Unix-like systems generate `.sh` helpers; Windows generates the corresponding `.bat` helpers.
|
|
262
288
|
|
|
@@ -283,7 +309,7 @@ ODOO_DOCKER_SUDO=1 ./docker/local/scripts/run.sh --help
|
|
|
283
309
|
|
|
284
310
|
Docker helpers use `[config].db_name` when configured, or `odoo` by default.
|
|
285
311
|
|
|
286
|
-
#### 1.
|
|
312
|
+
#### 1.6.1. run
|
|
287
313
|
|
|
288
314
|
Runs Odoo in a one-off Docker container for the configured database:
|
|
289
315
|
|
|
@@ -297,7 +323,7 @@ Any extra arguments are passed through to Odoo:
|
|
|
297
323
|
./docker/local/scripts/run.sh -i base --stop-after-init
|
|
298
324
|
```
|
|
299
325
|
|
|
300
|
-
#### 1.
|
|
326
|
+
#### 1.6.2. shell
|
|
301
327
|
|
|
302
328
|
Opens an Odoo shell for the configured database:
|
|
303
329
|
|
|
@@ -307,7 +333,7 @@ Opens an Odoo shell for the configured database:
|
|
|
307
333
|
|
|
308
334
|
Any extra arguments are passed through to the underlying Odoo shell command.
|
|
309
335
|
|
|
310
|
-
#### 1.
|
|
336
|
+
#### 1.6.3. update
|
|
311
337
|
|
|
312
338
|
Updates installed addons using `click-odoo-update`:
|
|
313
339
|
|
|
@@ -321,7 +347,7 @@ Any extra arguments are passed through to `click-odoo-update`:
|
|
|
321
347
|
./docker/local/scripts/update.sh --update-all
|
|
322
348
|
```
|
|
323
349
|
|
|
324
|
-
#### 1.
|
|
350
|
+
#### 1.6.4. Database backup
|
|
325
351
|
|
|
326
352
|
Create a PostgreSQL custom-format dump under `ROOT/odoo-backups/`:
|
|
327
353
|
|
|
@@ -341,7 +367,7 @@ Pass an output path explicitly when needed:
|
|
|
341
367
|
./docker/local/scripts/backup-db.sh ./odoo-backups/pre-upgrade.dump
|
|
342
368
|
```
|
|
343
369
|
|
|
344
|
-
#### 1.
|
|
370
|
+
#### 1.6.5. Database restore
|
|
345
371
|
|
|
346
372
|
Restore a database dump with `pg_restore`:
|
|
347
373
|
|
|
@@ -349,7 +375,7 @@ Restore a database dump with `pg_restore`:
|
|
|
349
375
|
./docker/local/scripts/restore-db.sh ./odoo-backups/pre-upgrade.dump
|
|
350
376
|
```
|
|
351
377
|
|
|
352
|
-
Database restores use copy semantics by default
|
|
378
|
+
Database restores use copy semantics by default.
|
|
353
379
|
After `pg_restore`, Odoo database identity parameters are regenerated so the restored
|
|
354
380
|
database can coexist with the source database:
|
|
355
381
|
|
|
@@ -372,7 +398,7 @@ docker compose stop odoo
|
|
|
372
398
|
docker compose up -d odoo
|
|
373
399
|
```
|
|
374
400
|
|
|
375
|
-
#### 1.
|
|
401
|
+
#### 1.6.6. Database neutralization
|
|
376
402
|
|
|
377
403
|
For development or test databases restored from production, run the generated neutralization helper explicitly:
|
|
378
404
|
|
|
@@ -407,7 +433,7 @@ docker compose stop odoo
|
|
|
407
433
|
docker compose up -d odoo
|
|
408
434
|
```
|
|
409
435
|
|
|
410
|
-
#### 1.
|
|
436
|
+
#### 1.6.7. Filestore backup
|
|
411
437
|
|
|
412
438
|
Create a compressed tar archive of the selected database filestore:
|
|
413
439
|
|
|
@@ -438,7 +464,7 @@ docker compose stop odoo
|
|
|
438
464
|
docker compose up -d odoo
|
|
439
465
|
```
|
|
440
466
|
|
|
441
|
-
#### 1.
|
|
467
|
+
#### 1.6.8. Filestore restore
|
|
442
468
|
|
|
443
469
|
Restore a filestore archive:
|
|
444
470
|
|
|
@@ -454,7 +480,7 @@ docker compose stop odoo
|
|
|
454
480
|
docker compose up -d odoo
|
|
455
481
|
```
|
|
456
482
|
|
|
457
|
-
#### 1.
|
|
483
|
+
#### 1.6.9. Restic filestore backup
|
|
458
484
|
|
|
459
485
|
The generated Docker image also includes `restic`. Restic is an additional option intended especially for large filestores where incremental snapshots and deduplication are useful.
|
|
460
486
|
|
|
@@ -492,7 +518,7 @@ List or inspect snapshots through the generic wrapper:
|
|
|
492
518
|
./docker/local/scripts/restic.sh check
|
|
493
519
|
```
|
|
494
520
|
|
|
495
|
-
#### 1.
|
|
521
|
+
#### 1.6.10. Restic filestore restore
|
|
496
522
|
|
|
497
523
|
Restore the latest matching filestore snapshot:
|
|
498
524
|
|
|
@@ -514,7 +540,7 @@ docker compose up -d odoo
|
|
|
514
540
|
|
|
515
541
|
With `--force`, the restore helper removes the target database filestore before restoring it. When `latest` is used, the snapshot selection is restricted to the current `RESTIC_HOST` and source filestore path. Use the same `RESTIC_HOST` value that was used when the snapshot was created if the restore is performed from another host.
|
|
516
542
|
|
|
517
|
-
### 1.
|
|
543
|
+
### 1.7. Creating a Docker deploy build context
|
|
518
544
|
|
|
519
545
|
Use `--create-docker-deploy` to generate a self-contained Docker build context for CI/CD, testing, staging, production, or another non-local deployment workflow.
|
|
520
546
|
|
|
@@ -787,7 +813,7 @@ Restore a database dump with `pg_restore`:
|
|
|
787
813
|
./odoo-scripts/restore-db.sh ./odoo-backups/pre-upgrade.dump
|
|
788
814
|
```
|
|
789
815
|
|
|
790
|
-
Database restores use copy semantics by default
|
|
816
|
+
Database restores use copy semantics by default.
|
|
791
817
|
After `pg_restore`, Odoo database identity parameters are regenerated so the restored
|
|
792
818
|
database can coexist with the source database:
|
|
793
819
|
|
|
@@ -1412,6 +1438,10 @@ This section is optional.
|
|
|
1412
1438
|
- `mailpit_enabled` — enables a persistent Mailpit service in the local Docker workflow for SMTP and POP3 mail testing. Default: `false`. It does not affect the deploy build context.
|
|
1413
1439
|
- `mailpit_version` — version of `ghcr.io/axllent/mailpit` in local Compose. Default: `v1.31.3`. An explicitly set value must be a concrete version; empty values and `latest` are invalid.
|
|
1414
1440
|
- `mailpit_webui_port` — host port used for the Mailpit web UI. Default: `8025`. The internal Mailpit web UI port remains `8025`.
|
|
1441
|
+
- `odoo_cpus`, `db_cpus`, `mailpit_cpus` — optional CPU limits for the respective Compose services; positive decimal CPU counts such as `0.5` or `2.0`.
|
|
1442
|
+
- `odoo_mem_limit`, `db_mem_limit`, `mailpit_mem_limit` — optional memory limits for the respective Compose services; positive amounts such as `256m` or `4g`.
|
|
1443
|
+
|
|
1444
|
+
All six resource options default to unset and are omitted from generated Compose when empty. See [Limiting Docker Compose resources](#17-limiting-docker-compose-resources) for a complete example.
|
|
1415
1445
|
|
|
1416
1446
|
Example:
|
|
1417
1447
|
|
|
@@ -106,7 +106,7 @@ Then start the Docker Compose environment:
|
|
|
106
106
|
|
|
107
107
|
```bash
|
|
108
108
|
cd ./odoo19
|
|
109
|
-
docker compose up -d --build
|
|
109
|
+
docker compose up -d --build --wait
|
|
110
110
|
```
|
|
111
111
|
|
|
112
112
|
Optionally initialize the Odoo database from the command line instead of through the web interface:
|
|
@@ -146,7 +146,7 @@ Sync the configured addon repositories and refresh the generated Docker workflow
|
|
|
146
146
|
|
|
147
147
|
```bash
|
|
148
148
|
odoo-compose --sync-addons
|
|
149
|
-
docker compose up -d --build
|
|
149
|
+
docker compose up -d --build --wait
|
|
150
150
|
```
|
|
151
151
|
|
|
152
152
|
The addon repositories are cloned into `ROOT/odoo-addons/oca-web/` and `ROOT/odoo-addons/oca-helpdesk/` and bind-mounted into the Odoo container.
|
|
@@ -187,7 +187,7 @@ Sync the Odoo source and generate the Docker artifacts:
|
|
|
187
187
|
|
|
188
188
|
```bash
|
|
189
189
|
odoo-compose --sync-all
|
|
190
|
-
docker compose up -d --build
|
|
190
|
+
docker compose up -d --build --wait
|
|
191
191
|
```
|
|
192
192
|
|
|
193
193
|
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.
|
|
@@ -198,14 +198,14 @@ Enable Mailpit in the local Docker workflow when you need to inspect or test ema
|
|
|
198
198
|
|
|
199
199
|
```bash
|
|
200
200
|
odoo-compose --set docker:mailpit_enabled=true
|
|
201
|
-
docker compose up -d --build
|
|
201
|
+
docker compose up -d --build --wait
|
|
202
202
|
```
|
|
203
203
|
|
|
204
204
|
Mailpit is available at http://localhost:8025 by default. When running multiple workspaces, override the host web UI port, for example:
|
|
205
205
|
|
|
206
206
|
```bash
|
|
207
207
|
odoo-compose --set docker:mailpit_enabled=true --set docker:mailpit_webui_port=8026
|
|
208
|
-
docker compose up -d --build
|
|
208
|
+
docker compose up -d --build --wait
|
|
209
209
|
```
|
|
210
210
|
|
|
211
211
|
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.
|
|
@@ -228,7 +228,33 @@ target = /mnt/exchange
|
|
|
228
228
|
|
|
229
229
|
`odoo-compose` does not create the source directory or change its ownership or permissions; host provisioning or the deployment process must prepare the directory before Docker starts.
|
|
230
230
|
|
|
231
|
-
### 1.5.
|
|
231
|
+
### 1.5. Limiting Docker Compose resources
|
|
232
|
+
|
|
233
|
+
You can optionally set CPU and memory limits for each service in the generated `compose.yaml` using `[docker]` in `odoo-project.ini`:
|
|
234
|
+
|
|
235
|
+
```ini
|
|
236
|
+
[docker]
|
|
237
|
+
odoo_cpus = 2.0
|
|
238
|
+
odoo_mem_limit = 4g
|
|
239
|
+
|
|
240
|
+
db_cpus = 1.0
|
|
241
|
+
db_mem_limit = 2g
|
|
242
|
+
|
|
243
|
+
mailpit_enabled = true
|
|
244
|
+
mailpit_cpus = 0.5
|
|
245
|
+
mailpit_mem_limit = 256m
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
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.
|
|
249
|
+
|
|
250
|
+
Regenerate the Docker configuration after editing the INI, then start the services:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
odoo-compose
|
|
254
|
+
docker compose up -d --build --wait
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### 1.6. Script reference
|
|
232
258
|
|
|
233
259
|
The Docker workflow generates platform-specific helper scripts under `ROOT/docker/local/scripts/`. Unix-like systems generate `.sh` helpers; Windows generates the corresponding `.bat` helpers.
|
|
234
260
|
|
|
@@ -255,7 +281,7 @@ ODOO_DOCKER_SUDO=1 ./docker/local/scripts/run.sh --help
|
|
|
255
281
|
|
|
256
282
|
Docker helpers use `[config].db_name` when configured, or `odoo` by default.
|
|
257
283
|
|
|
258
|
-
#### 1.
|
|
284
|
+
#### 1.6.1. run
|
|
259
285
|
|
|
260
286
|
Runs Odoo in a one-off Docker container for the configured database:
|
|
261
287
|
|
|
@@ -269,7 +295,7 @@ Any extra arguments are passed through to Odoo:
|
|
|
269
295
|
./docker/local/scripts/run.sh -i base --stop-after-init
|
|
270
296
|
```
|
|
271
297
|
|
|
272
|
-
#### 1.
|
|
298
|
+
#### 1.6.2. shell
|
|
273
299
|
|
|
274
300
|
Opens an Odoo shell for the configured database:
|
|
275
301
|
|
|
@@ -279,7 +305,7 @@ Opens an Odoo shell for the configured database:
|
|
|
279
305
|
|
|
280
306
|
Any extra arguments are passed through to the underlying Odoo shell command.
|
|
281
307
|
|
|
282
|
-
#### 1.
|
|
308
|
+
#### 1.6.3. update
|
|
283
309
|
|
|
284
310
|
Updates installed addons using `click-odoo-update`:
|
|
285
311
|
|
|
@@ -293,7 +319,7 @@ Any extra arguments are passed through to `click-odoo-update`:
|
|
|
293
319
|
./docker/local/scripts/update.sh --update-all
|
|
294
320
|
```
|
|
295
321
|
|
|
296
|
-
#### 1.
|
|
322
|
+
#### 1.6.4. Database backup
|
|
297
323
|
|
|
298
324
|
Create a PostgreSQL custom-format dump under `ROOT/odoo-backups/`:
|
|
299
325
|
|
|
@@ -313,7 +339,7 @@ Pass an output path explicitly when needed:
|
|
|
313
339
|
./docker/local/scripts/backup-db.sh ./odoo-backups/pre-upgrade.dump
|
|
314
340
|
```
|
|
315
341
|
|
|
316
|
-
#### 1.
|
|
342
|
+
#### 1.6.5. Database restore
|
|
317
343
|
|
|
318
344
|
Restore a database dump with `pg_restore`:
|
|
319
345
|
|
|
@@ -321,7 +347,7 @@ Restore a database dump with `pg_restore`:
|
|
|
321
347
|
./docker/local/scripts/restore-db.sh ./odoo-backups/pre-upgrade.dump
|
|
322
348
|
```
|
|
323
349
|
|
|
324
|
-
Database restores use copy semantics by default
|
|
350
|
+
Database restores use copy semantics by default.
|
|
325
351
|
After `pg_restore`, Odoo database identity parameters are regenerated so the restored
|
|
326
352
|
database can coexist with the source database:
|
|
327
353
|
|
|
@@ -344,7 +370,7 @@ docker compose stop odoo
|
|
|
344
370
|
docker compose up -d odoo
|
|
345
371
|
```
|
|
346
372
|
|
|
347
|
-
#### 1.
|
|
373
|
+
#### 1.6.6. Database neutralization
|
|
348
374
|
|
|
349
375
|
For development or test databases restored from production, run the generated neutralization helper explicitly:
|
|
350
376
|
|
|
@@ -379,7 +405,7 @@ docker compose stop odoo
|
|
|
379
405
|
docker compose up -d odoo
|
|
380
406
|
```
|
|
381
407
|
|
|
382
|
-
#### 1.
|
|
408
|
+
#### 1.6.7. Filestore backup
|
|
383
409
|
|
|
384
410
|
Create a compressed tar archive of the selected database filestore:
|
|
385
411
|
|
|
@@ -410,7 +436,7 @@ docker compose stop odoo
|
|
|
410
436
|
docker compose up -d odoo
|
|
411
437
|
```
|
|
412
438
|
|
|
413
|
-
#### 1.
|
|
439
|
+
#### 1.6.8. Filestore restore
|
|
414
440
|
|
|
415
441
|
Restore a filestore archive:
|
|
416
442
|
|
|
@@ -426,7 +452,7 @@ docker compose stop odoo
|
|
|
426
452
|
docker compose up -d odoo
|
|
427
453
|
```
|
|
428
454
|
|
|
429
|
-
#### 1.
|
|
455
|
+
#### 1.6.9. Restic filestore backup
|
|
430
456
|
|
|
431
457
|
The generated Docker image also includes `restic`. Restic is an additional option intended especially for large filestores where incremental snapshots and deduplication are useful.
|
|
432
458
|
|
|
@@ -464,7 +490,7 @@ List or inspect snapshots through the generic wrapper:
|
|
|
464
490
|
./docker/local/scripts/restic.sh check
|
|
465
491
|
```
|
|
466
492
|
|
|
467
|
-
#### 1.
|
|
493
|
+
#### 1.6.10. Restic filestore restore
|
|
468
494
|
|
|
469
495
|
Restore the latest matching filestore snapshot:
|
|
470
496
|
|
|
@@ -486,7 +512,7 @@ docker compose up -d odoo
|
|
|
486
512
|
|
|
487
513
|
With `--force`, the restore helper removes the target database filestore before restoring it. When `latest` is used, the snapshot selection is restricted to the current `RESTIC_HOST` and source filestore path. Use the same `RESTIC_HOST` value that was used when the snapshot was created if the restore is performed from another host.
|
|
488
514
|
|
|
489
|
-
### 1.
|
|
515
|
+
### 1.7. Creating a Docker deploy build context
|
|
490
516
|
|
|
491
517
|
Use `--create-docker-deploy` to generate a self-contained Docker build context for CI/CD, testing, staging, production, or another non-local deployment workflow.
|
|
492
518
|
|
|
@@ -759,7 +785,7 @@ Restore a database dump with `pg_restore`:
|
|
|
759
785
|
./odoo-scripts/restore-db.sh ./odoo-backups/pre-upgrade.dump
|
|
760
786
|
```
|
|
761
787
|
|
|
762
|
-
Database restores use copy semantics by default
|
|
788
|
+
Database restores use copy semantics by default.
|
|
763
789
|
After `pg_restore`, Odoo database identity parameters are regenerated so the restored
|
|
764
790
|
database can coexist with the source database:
|
|
765
791
|
|
|
@@ -1384,6 +1410,10 @@ This section is optional.
|
|
|
1384
1410
|
- `mailpit_enabled` — enables a persistent Mailpit service in the local Docker workflow for SMTP and POP3 mail testing. Default: `false`. It does not affect the deploy build context.
|
|
1385
1411
|
- `mailpit_version` — version of `ghcr.io/axllent/mailpit` in local Compose. Default: `v1.31.3`. An explicitly set value must be a concrete version; empty values and `latest` are invalid.
|
|
1386
1412
|
- `mailpit_webui_port` — host port used for the Mailpit web UI. Default: `8025`. The internal Mailpit web UI port remains `8025`.
|
|
1413
|
+
- `odoo_cpus`, `db_cpus`, `mailpit_cpus` — optional CPU limits for the respective Compose services; positive decimal CPU counts such as `0.5` or `2.0`.
|
|
1414
|
+
- `odoo_mem_limit`, `db_mem_limit`, `mailpit_mem_limit` — optional memory limits for the respective Compose services; positive amounts such as `256m` or `4g`.
|
|
1415
|
+
|
|
1416
|
+
All six resource options default to unset and are omitted from generated Compose when empty. See [Limiting Docker Compose resources](#17-limiting-docker-compose-resources) for a complete example.
|
|
1387
1417
|
|
|
1388
1418
|
Example:
|
|
1389
1419
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.24.4"
|
|
@@ -25,6 +25,7 @@ import sys
|
|
|
25
25
|
import tempfile
|
|
26
26
|
import zipfile
|
|
27
27
|
from dataclasses import asdict, dataclass, field, replace
|
|
28
|
+
from decimal import Decimal
|
|
28
29
|
from datetime import datetime, timezone
|
|
29
30
|
from pathlib import Path, PurePosixPath
|
|
30
31
|
from typing import Any, Dict, Optional
|
|
@@ -153,6 +154,12 @@ _INI_SET_SUPPORTED_OPTIONS = {
|
|
|
153
154
|
"mailpit_enabled",
|
|
154
155
|
"mailpit_version",
|
|
155
156
|
"mailpit_webui_port",
|
|
157
|
+
"odoo_cpus",
|
|
158
|
+
"odoo_mem_limit",
|
|
159
|
+
"db_cpus",
|
|
160
|
+
"db_mem_limit",
|
|
161
|
+
"mailpit_cpus",
|
|
162
|
+
"mailpit_mem_limit",
|
|
156
163
|
},
|
|
157
164
|
}
|
|
158
165
|
_INI_SET_ADDON_SUPPORTED_OPTIONS = {
|
|
@@ -239,6 +246,12 @@ class DockerConfig:
|
|
|
239
246
|
mailpit_enabled: bool = False
|
|
240
247
|
mailpit_version: str = _DEFAULT_DOCKER_MAILPIT_VERSION
|
|
241
248
|
mailpit_webui_port: int = _DEFAULT_DOCKER_MAILPIT_WEBUI_PORT
|
|
249
|
+
odoo_cpus: Optional[str] = None
|
|
250
|
+
odoo_mem_limit: Optional[str] = None
|
|
251
|
+
db_cpus: Optional[str] = None
|
|
252
|
+
db_mem_limit: Optional[str] = None
|
|
253
|
+
mailpit_cpus: Optional[str] = None
|
|
254
|
+
mailpit_mem_limit: Optional[str] = None
|
|
242
255
|
shares: Dict[str, DockerShareSpec] = field(default_factory=dict)
|
|
243
256
|
|
|
244
257
|
|
|
@@ -1049,10 +1062,27 @@ def _parse_odoo_version(odoo_version: str) -> int:
|
|
|
1049
1062
|
|
|
1050
1063
|
def _validate_docker_image(value: str, option: str) -> str:
|
|
1051
1064
|
image = (value or "").strip()
|
|
1052
|
-
|
|
1065
|
+
# This value is embedded directly into Dockerfile FROM instructions.
|
|
1066
|
+
# Require a single Docker image reference (registry, tag and digest allowed).
|
|
1067
|
+
component = r"[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*"
|
|
1068
|
+
label = r"[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?"
|
|
1069
|
+
registry = rf"(?:{label}(?:\.{label})*(?::[0-9]+)?|\[[0-9A-Fa-f:.]+\](?::[0-9]+)?)"
|
|
1070
|
+
tag = r"[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}"
|
|
1071
|
+
digest = r"[A-Za-z][A-Za-z0-9_+.-]*:[A-Za-z0-9=_-]{32,}"
|
|
1072
|
+
if (
|
|
1073
|
+
not image
|
|
1074
|
+
or len(image) > 512
|
|
1075
|
+
or "\n" in (value or "")
|
|
1076
|
+
or "\r" in (value or "")
|
|
1077
|
+
or re.fullmatch(
|
|
1078
|
+
rf"(?:{registry}/)?{component}(?:/{component})*(?::{tag})?(?:@{digest})?",
|
|
1079
|
+
image,
|
|
1080
|
+
flags=re.ASCII,
|
|
1081
|
+
) is None
|
|
1082
|
+
):
|
|
1053
1083
|
raise Exception(
|
|
1054
1084
|
f"Invalid option '{option}' in section [docker] "
|
|
1055
|
-
"(expected
|
|
1085
|
+
"(expected a single Docker image reference, optionally with registry, tag or digest)."
|
|
1056
1086
|
)
|
|
1057
1087
|
|
|
1058
1088
|
return image
|
|
@@ -1113,6 +1143,37 @@ def _validate_tcp_port(value: str, option: str) -> int:
|
|
|
1113
1143
|
return port
|
|
1114
1144
|
|
|
1115
1145
|
|
|
1146
|
+
def _validate_docker_cpus(value: str, option: str) -> Optional[str]:
|
|
1147
|
+
raw = (value or "").strip()
|
|
1148
|
+
if not raw:
|
|
1149
|
+
return None
|
|
1150
|
+
if (
|
|
1151
|
+
len(raw) > 32
|
|
1152
|
+
or re.fullmatch(r"[0-9]+(?:\.[0-9]+)?", raw, flags=re.ASCII) is None
|
|
1153
|
+
or Decimal(raw) <= 0
|
|
1154
|
+
):
|
|
1155
|
+
raise Exception(
|
|
1156
|
+
f"Invalid option '{option}' in section [docker] "
|
|
1157
|
+
"(expected a positive CPU count, for example 0.5 or 2.0)."
|
|
1158
|
+
)
|
|
1159
|
+
return str(Decimal(raw))
|
|
1160
|
+
|
|
1161
|
+
|
|
1162
|
+
def _validate_docker_mem_limit(value: str, option: str) -> Optional[str]:
|
|
1163
|
+
raw = (value or "").strip()
|
|
1164
|
+
if not raw:
|
|
1165
|
+
return None
|
|
1166
|
+
# Docker Compose memory values are positive byte counts with optional units.
|
|
1167
|
+
if len(raw) > 32 or re.fullmatch(
|
|
1168
|
+
r"[1-9][0-9]*(?:b|[kmgpt](?:b|ib)?)?", raw, flags=re.ASCII | re.IGNORECASE
|
|
1169
|
+
) is None:
|
|
1170
|
+
raise Exception(
|
|
1171
|
+
f"Invalid option '{option}' in section [docker] "
|
|
1172
|
+
"(expected a positive memory amount, for example 256m or 4g)."
|
|
1173
|
+
)
|
|
1174
|
+
return raw.lower()
|
|
1175
|
+
|
|
1176
|
+
|
|
1116
1177
|
def _get_default_virtualenv_settings(odoo_version: str) -> tuple[str, list[str], list[str]]:
|
|
1117
1178
|
odoo_major_version = _parse_odoo_version(odoo_version)
|
|
1118
1179
|
|
|
@@ -1271,6 +1332,13 @@ def load_project_config(
|
|
|
1271
1332
|
for sec in cp.sections():
|
|
1272
1333
|
if sec.startswith("addons."):
|
|
1273
1334
|
name = sec.split(".", 1)[1]
|
|
1335
|
+
# Git addon destinations are ROOT/odoo-addons/<name>. Keep the
|
|
1336
|
+
# section name a single, portable path component (also on Windows).
|
|
1337
|
+
if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_.-]*", name) or name.endswith("."):
|
|
1338
|
+
raise Exception(
|
|
1339
|
+
f"Invalid addon section name [{sec}]: use letters, digits, '.', '_' or '-' "
|
|
1340
|
+
"without path separators or traversal components."
|
|
1341
|
+
)
|
|
1274
1342
|
|
|
1275
1343
|
has_path = cp.has_option(sec, "path")
|
|
1276
1344
|
has_repo = cp.has_option(sec, "repo")
|
|
@@ -1368,13 +1436,21 @@ def load_project_config(
|
|
|
1368
1436
|
"mailpit_enabled",
|
|
1369
1437
|
"mailpit_version",
|
|
1370
1438
|
"mailpit_webui_port",
|
|
1439
|
+
"odoo_cpus",
|
|
1440
|
+
"odoo_mem_limit",
|
|
1441
|
+
"db_cpus",
|
|
1442
|
+
"db_mem_limit",
|
|
1443
|
+
"mailpit_cpus",
|
|
1444
|
+
"mailpit_mem_limit",
|
|
1371
1445
|
}
|
|
1372
1446
|
for key in cp._sections.get("docker", {}).keys():
|
|
1373
1447
|
if key not in supported_docker_options:
|
|
1374
1448
|
raise Exception(
|
|
1375
1449
|
f"Unsupported option '{key}' in section [docker]. "
|
|
1376
1450
|
"Supported options: base_image, mailpit_enabled, mailpit_version, "
|
|
1377
|
-
"mailpit_webui_port, odoo_source, postgres_version, restic_version
|
|
1451
|
+
"mailpit_webui_port, odoo_source, postgres_version, restic_version, "
|
|
1452
|
+
"odoo_cpus, odoo_mem_limit, db_cpus, db_mem_limit, "
|
|
1453
|
+
"mailpit_cpus, mailpit_mem_limit."
|
|
1378
1454
|
)
|
|
1379
1455
|
|
|
1380
1456
|
base_image = (
|
|
@@ -1414,6 +1490,30 @@ def load_project_config(
|
|
|
1414
1490
|
if cp.has_option("docker", "mailpit_webui_port")
|
|
1415
1491
|
else _DEFAULT_DOCKER_MAILPIT_WEBUI_PORT
|
|
1416
1492
|
),
|
|
1493
|
+
odoo_cpus=(
|
|
1494
|
+
_validate_docker_cpus(cp.get("docker", "odoo_cpus"), "odoo_cpus")
|
|
1495
|
+
if cp.has_option("docker", "odoo_cpus") else None
|
|
1496
|
+
),
|
|
1497
|
+
odoo_mem_limit=(
|
|
1498
|
+
_validate_docker_mem_limit(cp.get("docker", "odoo_mem_limit"), "odoo_mem_limit")
|
|
1499
|
+
if cp.has_option("docker", "odoo_mem_limit") else None
|
|
1500
|
+
),
|
|
1501
|
+
db_cpus=(
|
|
1502
|
+
_validate_docker_cpus(cp.get("docker", "db_cpus"), "db_cpus")
|
|
1503
|
+
if cp.has_option("docker", "db_cpus") else None
|
|
1504
|
+
),
|
|
1505
|
+
db_mem_limit=(
|
|
1506
|
+
_validate_docker_mem_limit(cp.get("docker", "db_mem_limit"), "db_mem_limit")
|
|
1507
|
+
if cp.has_option("docker", "db_mem_limit") else None
|
|
1508
|
+
),
|
|
1509
|
+
mailpit_cpus=(
|
|
1510
|
+
_validate_docker_cpus(cp.get("docker", "mailpit_cpus"), "mailpit_cpus")
|
|
1511
|
+
if cp.has_option("docker", "mailpit_cpus") else None
|
|
1512
|
+
),
|
|
1513
|
+
mailpit_mem_limit=(
|
|
1514
|
+
_validate_docker_mem_limit(cp.get("docker", "mailpit_mem_limit"), "mailpit_mem_limit")
|
|
1515
|
+
if cp.has_option("docker", "mailpit_mem_limit") else None
|
|
1516
|
+
),
|
|
1417
1517
|
shares=docker_shares,
|
|
1418
1518
|
)
|
|
1419
1519
|
|
|
@@ -2151,7 +2251,22 @@ def _validate_local_odoo_path(layout: Layout, spec: OdooSpec) -> Path:
|
|
|
2151
2251
|
|
|
2152
2252
|
|
|
2153
2253
|
def _resolve_addon_path(layout: Layout, addon_name: str, spec: AddonSpec) -> Path:
|
|
2154
|
-
|
|
2254
|
+
if spec.is_local:
|
|
2255
|
+
return _resolve_workspace_path(layout, spec.path or "", layout.addons_root / addon_name)
|
|
2256
|
+
dest = layout.addons_root / addon_name
|
|
2257
|
+
# Implicit Git destinations must not follow symlinks into another repo,
|
|
2258
|
+
# outside the workspace, or back onto the addons root itself.
|
|
2259
|
+
if (
|
|
2260
|
+
not _is_path_within(layout.addons_root, layout.root)
|
|
2261
|
+
or dest.is_symlink()
|
|
2262
|
+
or not _is_path_within(dest, layout.addons_root)
|
|
2263
|
+
or dest.resolve() == layout.addons_root.resolve()
|
|
2264
|
+
):
|
|
2265
|
+
raise Exception(
|
|
2266
|
+
f"Unsafe addon destination for [addons.{addon_name}]: {dest} "
|
|
2267
|
+
f"must resolve inside {layout.addons_root}. Use an explicit 'path' for local addons."
|
|
2268
|
+
)
|
|
2269
|
+
return dest
|
|
2155
2270
|
|
|
2156
2271
|
|
|
2157
2272
|
def _validate_local_addon_path(layout: Layout, addon_name: str, spec: AddonSpec) -> Path:
|
|
@@ -2498,11 +2613,44 @@ def _native_default_db_name(cfg: ProjectConfig) -> str:
|
|
|
2498
2613
|
db_name = str(cfg.config.get("db_name") or "").strip()
|
|
2499
2614
|
if not db_name:
|
|
2500
2615
|
raise Exception("Native database helpers require [config].db_name.")
|
|
2501
|
-
|
|
2502
|
-
|
|
2616
|
+
# These names become PostgreSQL identifiers, filesystem path components and
|
|
2617
|
+
# Windows batch variables. Reject traversal, CMD metacharacters and names
|
|
2618
|
+
# that PostgreSQL could silently truncate to another database name.
|
|
2619
|
+
if len(db_name) > 63 or not re.fullmatch(r"[A-Za-z0-9_][A-Za-z0-9_.-]*", db_name):
|
|
2620
|
+
raise Exception(
|
|
2621
|
+
"Invalid [config].db_name: expected 1-63 ASCII letters, digits, "
|
|
2622
|
+
"underscores, dots or hyphens, starting with a letter, digit or underscore."
|
|
2623
|
+
)
|
|
2503
2624
|
return db_name
|
|
2504
2625
|
|
|
2505
2626
|
|
|
2627
|
+
def _native_filestore_data_dir_sh(layout: Layout) -> str:
|
|
2628
|
+
# Preserve workspace-relative paths when the workspace is moved.
|
|
2629
|
+
try:
|
|
2630
|
+
relative = layout.data_dir.relative_to(layout.root)
|
|
2631
|
+
except ValueError:
|
|
2632
|
+
return shlex.quote(str(layout.data_dir))
|
|
2633
|
+
return f'"${{ROOT_DIR}}"/{shlex.quote(relative.as_posix())}'
|
|
2634
|
+
|
|
2635
|
+
|
|
2636
|
+
def _native_filestore_data_dir_bat(layout: Layout) -> str:
|
|
2637
|
+
# Paths cannot contain double quotes on Windows; reject control characters
|
|
2638
|
+
# rather than generating unsafe batch assignments.
|
|
2639
|
+
if any(char in str(layout.data_dir) for char in ('"', '\r', '\n', '\x00')):
|
|
2640
|
+
raise Exception("Native filestore data_dir contains invalid Windows path characters.")
|
|
2641
|
+
try:
|
|
2642
|
+
relative = layout.data_dir.relative_to(layout.root)
|
|
2643
|
+
except ValueError:
|
|
2644
|
+
value = str(layout.data_dir)
|
|
2645
|
+
else:
|
|
2646
|
+
suffix = '' if str(relative) == '.' else '\\' + str(relative)
|
|
2647
|
+
value = '%ROOT_DIR%' + suffix
|
|
2648
|
+
# Keep %ROOT_DIR% expandable while escaping literal percent signs in paths.
|
|
2649
|
+
if value.startswith('%ROOT_DIR%'):
|
|
2650
|
+
return '%ROOT_DIR%' + value[len('%ROOT_DIR%'):].replace('%', '%%').replace('^', '^^')
|
|
2651
|
+
return value.replace('%', '%%').replace('^', '^^')
|
|
2652
|
+
|
|
2653
|
+
|
|
2506
2654
|
def _native_pg_env(cfg: ProjectConfig) -> dict[str, str]:
|
|
2507
2655
|
# libpq reads these environment variables, keeping passwords out of command
|
|
2508
2656
|
# arguments. Missing/False options leave libpq defaults and .pgpass intact.
|
|
@@ -2857,6 +3005,7 @@ echo "INFO: Database neutralization completed: ${{DB_NAME}}"
|
|
|
2857
3005
|
|
|
2858
3006
|
def write_native_backup_filestore_sh(layout: Layout, cfg: ProjectConfig) -> Path:
|
|
2859
3007
|
configured_db_name = shlex.quote(_native_default_db_name(cfg))
|
|
3008
|
+
configured_data_dir = _native_filestore_data_dir_sh(layout)
|
|
2860
3009
|
content = fr"""#!/usr/bin/env bash
|
|
2861
3010
|
set -euo pipefail
|
|
2862
3011
|
|
|
@@ -2865,7 +3014,8 @@ ROOT_DIR="$(cd "${{SCRIPT_DIR}}/.." && pwd)"
|
|
|
2865
3014
|
|
|
2866
3015
|
BACKUPS_DIR="${{ROOT_DIR}}/odoo-backups"
|
|
2867
3016
|
DB_NAME={configured_db_name}
|
|
2868
|
-
|
|
3017
|
+
DATA_DIR={configured_data_dir}
|
|
3018
|
+
FILESTORE_PATH="${{DATA_DIR}}/filestore/${{DB_NAME}}"
|
|
2869
3019
|
TIMESTAMP="$(date +%Y%m%d_%H%M%S)"
|
|
2870
3020
|
OUTPUT="${{1:-${{BACKUPS_DIR}}/${{DB_NAME}}_${{TIMESTAMP}}_filestore.tar.gz}}"
|
|
2871
3021
|
TMP_OUTPUT="${{OUTPUT}}.tmp"
|
|
@@ -2887,6 +3037,7 @@ echo "INFO: Filestore backup created: ${{OUTPUT}}"
|
|
|
2887
3037
|
|
|
2888
3038
|
def write_native_restore_filestore_sh(layout: Layout, cfg: ProjectConfig) -> Path:
|
|
2889
3039
|
configured_db_name = shlex.quote(_native_default_db_name(cfg))
|
|
3040
|
+
configured_data_dir = _native_filestore_data_dir_sh(layout)
|
|
2890
3041
|
content = fr"""#!/usr/bin/env bash
|
|
2891
3042
|
set -euo pipefail
|
|
2892
3043
|
|
|
@@ -2894,7 +3045,8 @@ SCRIPT_DIR="$(cd "$(dirname "${{BASH_SOURCE[0]}}")" && pwd)"
|
|
|
2894
3045
|
ROOT_DIR="$(cd "${{SCRIPT_DIR}}/.." && pwd)"
|
|
2895
3046
|
|
|
2896
3047
|
DB_NAME={configured_db_name}
|
|
2897
|
-
|
|
3048
|
+
DATA_DIR={configured_data_dir}
|
|
3049
|
+
FILESTORE_PATH="${{DATA_DIR}}/filestore/${{DB_NAME}}"
|
|
2898
3050
|
FORCE=false
|
|
2899
3051
|
SOURCE=""
|
|
2900
3052
|
|
|
@@ -2959,6 +3111,7 @@ echo "INFO: Filestore restore completed: ${{DB_NAME}}"
|
|
|
2959
3111
|
|
|
2960
3112
|
def write_native_backup_filestore_restic_sh(layout: Layout, cfg: ProjectConfig) -> Path:
|
|
2961
3113
|
configured_db_name = shlex.quote(_native_default_db_name(cfg))
|
|
3114
|
+
configured_data_dir = _native_filestore_data_dir_sh(layout)
|
|
2962
3115
|
content = fr'''#!/usr/bin/env bash
|
|
2963
3116
|
set -euo pipefail
|
|
2964
3117
|
|
|
@@ -2967,7 +3120,8 @@ ROOT_DIR="$(cd "${{SCRIPT_DIR}}/.." && pwd)"
|
|
|
2967
3120
|
RESTIC="${{SCRIPT_DIR}}/restic.sh"
|
|
2968
3121
|
DB_NAME={configured_db_name}
|
|
2969
3122
|
RESTIC_HOST_NAME="${{RESTIC_HOST:-$(hostname -f 2>/dev/null || hostname)}}"
|
|
2970
|
-
|
|
3123
|
+
DATA_DIR={configured_data_dir}
|
|
3124
|
+
FILESTORE_PATH="${{DATA_DIR}}/filestore/${{DB_NAME}}"
|
|
2971
3125
|
|
|
2972
3126
|
if [[ ! -x "${{RESTIC}}" ]]; then
|
|
2973
3127
|
echo "ERROR: restic helper not found: ${{RESTIC}}" >&2
|
|
@@ -2994,6 +3148,7 @@ echo "INFO: Backing up filestore for database '${{DB_NAME}}' with restic."
|
|
|
2994
3148
|
|
|
2995
3149
|
def write_native_restore_filestore_restic_sh(layout: Layout, cfg: ProjectConfig) -> Path:
|
|
2996
3150
|
configured_db_name = shlex.quote(_native_default_db_name(cfg))
|
|
3151
|
+
configured_data_dir = _native_filestore_data_dir_sh(layout)
|
|
2997
3152
|
restic_command = _native_restic_command_sh()
|
|
2998
3153
|
content = fr'''#!/usr/bin/env bash
|
|
2999
3154
|
set -euo pipefail
|
|
@@ -3008,8 +3163,9 @@ RESTIC_HOST_NAME="${{RESTIC_HOST:-$(hostname -f 2>/dev/null || hostname)}}"
|
|
|
3008
3163
|
FORCE=false
|
|
3009
3164
|
SNAPSHOT="latest"
|
|
3010
3165
|
SNAPSHOT_SET=false
|
|
3011
|
-
|
|
3012
|
-
|
|
3166
|
+
DATA_DIR={configured_data_dir}
|
|
3167
|
+
SOURCE_FILESTORE_PATH="${{DATA_DIR}}/filestore/${{SOURCE_DB_NAME}}"
|
|
3168
|
+
TARGET_FILESTORE_PATH="${{DATA_DIR}}/filestore/${{DB_NAME}}"
|
|
3013
3169
|
|
|
3014
3170
|
usage() {{
|
|
3015
3171
|
echo "Usage: $(basename "$0") [--force] [SNAPSHOT]" >&2
|
|
@@ -3371,6 +3527,7 @@ exit /b 0
|
|
|
3371
3527
|
|
|
3372
3528
|
def write_native_backup_filestore_bat(layout: Layout, cfg: ProjectConfig) -> Path:
|
|
3373
3529
|
configured_db_name = _native_default_db_name(cfg).replace("%", "%%")
|
|
3530
|
+
configured_data_dir = _native_filestore_data_dir_bat(layout)
|
|
3374
3531
|
content = rf"""@echo off
|
|
3375
3532
|
setlocal DisableDelayedExpansion
|
|
3376
3533
|
|
|
@@ -3379,7 +3536,8 @@ if "%SCRIPT_DIR:~-1%"=="\" set "SCRIPT_DIR=%SCRIPT_DIR:~0,-1%"
|
|
|
3379
3536
|
for %%I in ("%SCRIPT_DIR%\..") do set "ROOT_DIR=%%~fI"
|
|
3380
3537
|
set "BACKUPS_DIR=%ROOT_DIR%\odoo-backups"
|
|
3381
3538
|
set "DB_NAME={configured_db_name}"
|
|
3382
|
-
set "
|
|
3539
|
+
set "DATA_DIR={configured_data_dir}"
|
|
3540
|
+
set "FILESTORE_PATH=%DATA_DIR%\filestore\%DB_NAME%"
|
|
3383
3541
|
|
|
3384
3542
|
for /f %%I in ('powershell -NoProfile -Command "Get-Date -Format yyyyMMdd_HHmmss"') do set "TIMESTAMP=%%I"
|
|
3385
3543
|
if "%~1"=="" (
|
|
@@ -3412,6 +3570,7 @@ endlocal
|
|
|
3412
3570
|
|
|
3413
3571
|
def write_native_restore_filestore_bat(layout: Layout, cfg: ProjectConfig) -> Path:
|
|
3414
3572
|
configured_db_name = _native_default_db_name(cfg).replace("%", "%%")
|
|
3573
|
+
configured_data_dir = _native_filestore_data_dir_bat(layout)
|
|
3415
3574
|
content = rf"""@echo off
|
|
3416
3575
|
setlocal DisableDelayedExpansion
|
|
3417
3576
|
|
|
@@ -3419,7 +3578,8 @@ set "SCRIPT_DIR=%~dp0"
|
|
|
3419
3578
|
if "%SCRIPT_DIR:~-1%"=="\" set "SCRIPT_DIR=%SCRIPT_DIR:~0,-1%"
|
|
3420
3579
|
for %%I in ("%SCRIPT_DIR%\..") do set "ROOT_DIR=%%~fI"
|
|
3421
3580
|
set "DB_NAME={configured_db_name}"
|
|
3422
|
-
set "
|
|
3581
|
+
set "DATA_DIR={configured_data_dir}"
|
|
3582
|
+
set "FILESTORE_PATH=%DATA_DIR%\filestore\%DB_NAME%"
|
|
3423
3583
|
set "FORCE=false"
|
|
3424
3584
|
set "SOURCE="
|
|
3425
3585
|
|
|
@@ -3484,6 +3644,7 @@ exit /b 2
|
|
|
3484
3644
|
|
|
3485
3645
|
def write_native_backup_filestore_restic_bat(layout: Layout, cfg: ProjectConfig) -> Path:
|
|
3486
3646
|
configured_db_name = _native_default_db_name(cfg).replace("%", "%%")
|
|
3647
|
+
configured_data_dir = _native_filestore_data_dir_bat(layout)
|
|
3487
3648
|
content = rf"""@echo off
|
|
3488
3649
|
setlocal DisableDelayedExpansion
|
|
3489
3650
|
|
|
@@ -3497,7 +3658,8 @@ if defined RESTIC_HOST (
|
|
|
3497
3658
|
) else (
|
|
3498
3659
|
for /f "delims=" %%H in ('hostname') do set "RESTIC_HOST_NAME=%%H"
|
|
3499
3660
|
)
|
|
3500
|
-
set "
|
|
3661
|
+
set "DATA_DIR={configured_data_dir}"
|
|
3662
|
+
set "FILESTORE_PATH=%DATA_DIR%\filestore\%DB_NAME%"
|
|
3501
3663
|
|
|
3502
3664
|
if not exist "%RESTIC%" (
|
|
3503
3665
|
echo ERROR: restic helper not found: %RESTIC%
|
|
@@ -3519,6 +3681,7 @@ exit /b %ERRORLEVEL%
|
|
|
3519
3681
|
|
|
3520
3682
|
def write_native_restore_filestore_restic_bat(layout: Layout, cfg: ProjectConfig) -> Path:
|
|
3521
3683
|
configured_db_name = _native_default_db_name(cfg).replace("%", "%%")
|
|
3684
|
+
configured_data_dir = _native_filestore_data_dir_bat(layout)
|
|
3522
3685
|
restic_command = _native_restic_command_bat()
|
|
3523
3686
|
content = rf"""@echo off
|
|
3524
3687
|
setlocal DisableDelayedExpansion
|
|
@@ -3541,8 +3704,9 @@ if defined RESTIC_HOST (
|
|
|
3541
3704
|
set "FORCE=false"
|
|
3542
3705
|
set "SNAPSHOT=latest"
|
|
3543
3706
|
set "SNAPSHOT_SET=false"
|
|
3544
|
-
set "
|
|
3545
|
-
set "
|
|
3707
|
+
set "DATA_DIR={configured_data_dir}"
|
|
3708
|
+
set "SOURCE_FILESTORE_PATH=%DATA_DIR%\filestore\%SOURCE_DB_NAME%"
|
|
3709
|
+
set "TARGET_FILESTORE_PATH=%DATA_DIR%\filestore\%DB_NAME%"
|
|
3546
3710
|
|
|
3547
3711
|
goto parse_args
|
|
3548
3712
|
|
|
@@ -4224,18 +4388,23 @@ def render_docker_compose(layout: Layout, cfg: ProjectConfig) -> str:
|
|
|
4224
4388
|
)
|
|
4225
4389
|
|
|
4226
4390
|
mailpit_service = ""
|
|
4227
|
-
depends_on = ["
|
|
4391
|
+
depends_on = [" db:", " condition: service_healthy"]
|
|
4228
4392
|
named_volumes = [
|
|
4229
4393
|
" odoo-db-data:",
|
|
4230
4394
|
" odoo-data:",
|
|
4231
4395
|
]
|
|
4232
4396
|
|
|
4233
4397
|
if cfg.docker.mailpit_enabled:
|
|
4398
|
+
mailpit_resources = ""
|
|
4399
|
+
if cfg.docker.mailpit_cpus is not None:
|
|
4400
|
+
mailpit_resources += f" cpus: {cfg.docker.mailpit_cpus}\n"
|
|
4401
|
+
if cfg.docker.mailpit_mem_limit is not None:
|
|
4402
|
+
mailpit_resources += f" mem_limit: {cfg.docker.mailpit_mem_limit}\n"
|
|
4234
4403
|
mailpit_service = f"""
|
|
4235
4404
|
mailpit:
|
|
4236
4405
|
image: ghcr.io/axllent/mailpit:{cfg.docker.mailpit_version}
|
|
4237
4406
|
restart: unless-stopped
|
|
4238
|
-
environment:
|
|
4407
|
+
{mailpit_resources} environment:
|
|
4239
4408
|
MP_DATABASE: /data/mailpit.db
|
|
4240
4409
|
MP_DISABLE_VERSION_CHECK: "true"
|
|
4241
4410
|
MP_POP3_BIND_ADDR: "0.0.0.0:{_DEFAULT_DOCKER_MAILPIT_POP3_PORT}"
|
|
@@ -4245,18 +4414,26 @@ def render_docker_compose(layout: Layout, cfg: ProjectConfig) -> str:
|
|
|
4245
4414
|
volumes:
|
|
4246
4415
|
- mailpit-data:/data
|
|
4247
4416
|
"""
|
|
4248
|
-
depends_on.append(" - mailpit")
|
|
4249
4417
|
named_volumes.append(" mailpit-data:")
|
|
4250
4418
|
|
|
4251
4419
|
depends_on_block = "\n".join(depends_on)
|
|
4252
4420
|
named_volumes_block = "\n".join(named_volumes)
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4421
|
+
db_resources = ""
|
|
4422
|
+
if cfg.docker.db_cpus is not None:
|
|
4423
|
+
db_resources += f" cpus: {cfg.docker.db_cpus}\n"
|
|
4424
|
+
if cfg.docker.db_mem_limit is not None:
|
|
4425
|
+
db_resources += f" mem_limit: {cfg.docker.db_mem_limit}\n"
|
|
4426
|
+
odoo_resources = ""
|
|
4427
|
+
if cfg.docker.odoo_cpus is not None:
|
|
4428
|
+
odoo_resources += f" cpus: {cfg.docker.odoo_cpus}\n"
|
|
4429
|
+
if cfg.docker.odoo_mem_limit is not None:
|
|
4430
|
+
odoo_resources += f" mem_limit: {cfg.docker.odoo_mem_limit}\n"
|
|
4431
|
+
|
|
4432
|
+
return f"""services:
|
|
4256
4433
|
db:
|
|
4257
4434
|
image: postgres:{cfg.docker.postgres_version}
|
|
4258
4435
|
restart: unless-stopped
|
|
4259
|
-
logging:
|
|
4436
|
+
{db_resources} logging:
|
|
4260
4437
|
driver: local
|
|
4261
4438
|
options:
|
|
4262
4439
|
max-size: "50m"
|
|
@@ -4265,6 +4442,12 @@ services:
|
|
|
4265
4442
|
POSTGRES_DB: postgres
|
|
4266
4443
|
POSTGRES_USER: odoo
|
|
4267
4444
|
POSTGRES_PASSWORD: odoo
|
|
4445
|
+
healthcheck:
|
|
4446
|
+
test: ["CMD-SHELL", "pg_isready -q -h 127.0.0.1 -U $${{POSTGRES_USER}} -d $${{POSTGRES_DB}}"]
|
|
4447
|
+
interval: 10s
|
|
4448
|
+
timeout: 5s
|
|
4449
|
+
retries: 10
|
|
4450
|
+
start_period: 30s
|
|
4268
4451
|
volumes:
|
|
4269
4452
|
- odoo-db-data:{postgres_data_path}
|
|
4270
4453
|
{mailpit_service}
|
|
@@ -4272,13 +4455,19 @@ services:
|
|
|
4272
4455
|
build:
|
|
4273
4456
|
context: {build_context}
|
|
4274
4457
|
restart: unless-stopped
|
|
4275
|
-
logging:
|
|
4458
|
+
{odoo_resources} logging:
|
|
4276
4459
|
driver: local
|
|
4277
4460
|
options:
|
|
4278
4461
|
max-size: "50m"
|
|
4279
4462
|
max-file: "10"
|
|
4280
4463
|
depends_on:
|
|
4281
4464
|
{depends_on_block}
|
|
4465
|
+
healthcheck:
|
|
4466
|
+
test: ["CMD", "curl", "--fail", "--silent", "--show-error", "--max-time", "5", "--output", "/dev/null", "http://127.0.0.1:8069/web/login"]
|
|
4467
|
+
interval: 30s
|
|
4468
|
+
timeout: 10s
|
|
4469
|
+
retries: 5
|
|
4470
|
+
start_period: 90s
|
|
4282
4471
|
ports:
|
|
4283
4472
|
{ports}
|
|
4284
4473
|
environment:
|
|
@@ -4294,23 +4483,66 @@ volumes:
|
|
|
4294
4483
|
"""
|
|
4295
4484
|
|
|
4296
4485
|
|
|
4297
|
-
def write_docker_compose(
|
|
4298
|
-
|
|
4486
|
+
def write_docker_compose(
|
|
4487
|
+
layout: Layout,
|
|
4488
|
+
cfg: ProjectConfig,
|
|
4489
|
+
source_ini: Path,
|
|
4490
|
+
effective_ini_sha256: str,
|
|
4491
|
+
) -> Path:
|
|
4492
|
+
"""Write ROOT/compose.yaml, preserving its timestamp if the content is unchanged."""
|
|
4299
4493
|
path = _docker_compose_path(layout)
|
|
4300
4494
|
|
|
4301
4495
|
if path.exists() and not path.is_file():
|
|
4302
4496
|
raise Exception(f"Docker Compose path exists but is not a file: {path}")
|
|
4303
4497
|
|
|
4304
|
-
|
|
4305
|
-
|
|
4498
|
+
source_sha256 = hashlib.sha256(source_ini.read_bytes()).hexdigest()
|
|
4499
|
+
header = (
|
|
4500
|
+
"# =============================================================================\n"
|
|
4501
|
+
"# AUTO-GENERATED FILE - DO NOT EDIT MANUALLY\n"
|
|
4502
|
+
"# Generator: odoo-devops-tools (odoo-compose)\n"
|
|
4503
|
+
f"# Version: {__version__}\n"
|
|
4504
|
+
f"# Source: {source_ini.name}\n"
|
|
4505
|
+
f"# Source SHA256: {source_sha256}\n"
|
|
4506
|
+
f"# Effective SHA256: {effective_ini_sha256}\n"
|
|
4507
|
+
"# Effective SHA256 includes INI interpolation and CLI overrides.\n"
|
|
4508
|
+
"# Generated: __GENERATED_AT__\n"
|
|
4509
|
+
"# Update the source INI and regenerate this file.\n"
|
|
4510
|
+
"# =============================================================================\n\n"
|
|
4511
|
+
)
|
|
4512
|
+
compose_content = render_docker_compose(layout, cfg)
|
|
4513
|
+
|
|
4514
|
+
if path.is_file():
|
|
4515
|
+
existing = path.read_text(encoding="utf-8", errors="replace")
|
|
4516
|
+
previous_generated_at = re.search(
|
|
4517
|
+
r"(?m)^# Generated: (\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z)$",
|
|
4518
|
+
existing,
|
|
4519
|
+
)
|
|
4520
|
+
if previous_generated_at:
|
|
4521
|
+
unchanged = header.replace(
|
|
4522
|
+
"__GENERATED_AT__", previous_generated_at.group(1)
|
|
4523
|
+
) + compose_content
|
|
4524
|
+
if existing == unchanged:
|
|
4525
|
+
_logger.info("Docker Compose file is unchanged: %s", path)
|
|
4526
|
+
return path
|
|
4527
|
+
|
|
4528
|
+
header = header.replace("__GENERATED_AT__", _utc_now_iso())
|
|
4529
|
+
path.write_text(header + compose_content, encoding="utf-8")
|
|
4530
|
+
_logger.info("Generated Docker Compose file: %s", path)
|
|
4306
4531
|
return path
|
|
4307
4532
|
|
|
4308
4533
|
|
|
4309
4534
|
def _docker_default_db_name(cfg: ProjectConfig) -> str:
|
|
4310
4535
|
db_name = cfg.config.get("db_name")
|
|
4311
|
-
if isinstance(db_name, str)
|
|
4312
|
-
return
|
|
4313
|
-
|
|
4536
|
+
if not isinstance(db_name, str) or not db_name.strip():
|
|
4537
|
+
return "odoo"
|
|
4538
|
+
db_name = db_name.strip()
|
|
4539
|
+
# The same name is used by database and destructive filestore helpers.
|
|
4540
|
+
if len(db_name) > 63 or not re.fullmatch(r"[A-Za-z0-9_][A-Za-z0-9_.-]*", db_name):
|
|
4541
|
+
raise Exception(
|
|
4542
|
+
"Invalid [config].db_name: expected 1-63 ASCII letters, digits, "
|
|
4543
|
+
"underscores, dots or hyphens, starting with a letter, digit or underscore."
|
|
4544
|
+
)
|
|
4545
|
+
return db_name
|
|
4314
4546
|
|
|
4315
4547
|
|
|
4316
4548
|
def _write_docker_local_script(
|
|
@@ -5714,6 +5946,8 @@ def create_local_docker_artifacts(
|
|
|
5714
5946
|
layout: Layout,
|
|
5715
5947
|
cfg: ProjectConfig,
|
|
5716
5948
|
addon_requirement_files: list[Path],
|
|
5949
|
+
source_ini: Path,
|
|
5950
|
+
effective_ini_sha256: str,
|
|
5717
5951
|
) -> dict[str, Any]:
|
|
5718
5952
|
"""Generate the default local-development Docker context and ROOT/compose.yaml."""
|
|
5719
5953
|
if layout.docker_local_dir.exists():
|
|
@@ -5741,7 +5975,7 @@ def create_local_docker_artifacts(
|
|
|
5741
5975
|
include_addons=False,
|
|
5742
5976
|
include_config=False,
|
|
5743
5977
|
)
|
|
5744
|
-
compose_path = write_docker_compose(layout, cfg)
|
|
5978
|
+
compose_path = write_docker_compose(layout, cfg, source_ini, effective_ini_sha256)
|
|
5745
5979
|
script_paths = write_docker_local_scripts(layout, cfg)
|
|
5746
5980
|
|
|
5747
5981
|
_logger.info("Generated local Docker build context: %s", layout.docker_local_dir)
|
|
@@ -6855,10 +7089,18 @@ def _sync_project_impl(
|
|
|
6855
7089
|
|
|
6856
7090
|
if target == "docker":
|
|
6857
7091
|
_logger.info("Generating local-development Docker artifacts.")
|
|
7092
|
+
effective_ini = _ini_for_effective_file(_read_ini(
|
|
7093
|
+
ini_path,
|
|
7094
|
+
vars_overrides=vars_overrides,
|
|
7095
|
+
ini_overrides=ini_overrides,
|
|
7096
|
+
log_overrides=False,
|
|
7097
|
+
))
|
|
6858
7098
|
local_docker_artifacts = create_local_docker_artifacts(
|
|
6859
7099
|
layout=layout,
|
|
6860
7100
|
cfg=cfg,
|
|
6861
7101
|
addon_requirement_files=addon_req_files,
|
|
7102
|
+
source_ini=ini_path,
|
|
7103
|
+
effective_ini_sha256=hashlib.sha256(effective_ini.encode("utf-8")).hexdigest(),
|
|
6862
7104
|
)
|
|
6863
7105
|
else:
|
|
6864
7106
|
_logger.info("Skipping Docker artifacts (target=venv).")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: odoo-devops-tools
|
|
3
|
-
Version: 1.24.
|
|
3
|
+
Version: 1.24.4
|
|
4
4
|
Summary: Build reproducible Odoo workspaces from a single project definition.
|
|
5
5
|
Author: Roman Lacko
|
|
6
6
|
License-Expression: MIT
|
|
@@ -134,7 +134,7 @@ Then start the Docker Compose environment:
|
|
|
134
134
|
|
|
135
135
|
```bash
|
|
136
136
|
cd ./odoo19
|
|
137
|
-
docker compose up -d --build
|
|
137
|
+
docker compose up -d --build --wait
|
|
138
138
|
```
|
|
139
139
|
|
|
140
140
|
Optionally initialize the Odoo database from the command line instead of through the web interface:
|
|
@@ -174,7 +174,7 @@ Sync the configured addon repositories and refresh the generated Docker workflow
|
|
|
174
174
|
|
|
175
175
|
```bash
|
|
176
176
|
odoo-compose --sync-addons
|
|
177
|
-
docker compose up -d --build
|
|
177
|
+
docker compose up -d --build --wait
|
|
178
178
|
```
|
|
179
179
|
|
|
180
180
|
The addon repositories are cloned into `ROOT/odoo-addons/oca-web/` and `ROOT/odoo-addons/oca-helpdesk/` and bind-mounted into the Odoo container.
|
|
@@ -215,7 +215,7 @@ Sync the Odoo source and generate the Docker artifacts:
|
|
|
215
215
|
|
|
216
216
|
```bash
|
|
217
217
|
odoo-compose --sync-all
|
|
218
|
-
docker compose up -d --build
|
|
218
|
+
docker compose up -d --build --wait
|
|
219
219
|
```
|
|
220
220
|
|
|
221
221
|
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.
|
|
@@ -226,14 +226,14 @@ Enable Mailpit in the local Docker workflow when you need to inspect or test ema
|
|
|
226
226
|
|
|
227
227
|
```bash
|
|
228
228
|
odoo-compose --set docker:mailpit_enabled=true
|
|
229
|
-
docker compose up -d --build
|
|
229
|
+
docker compose up -d --build --wait
|
|
230
230
|
```
|
|
231
231
|
|
|
232
232
|
Mailpit is available at http://localhost:8025 by default. When running multiple workspaces, override the host web UI port, for example:
|
|
233
233
|
|
|
234
234
|
```bash
|
|
235
235
|
odoo-compose --set docker:mailpit_enabled=true --set docker:mailpit_webui_port=8026
|
|
236
|
-
docker compose up -d --build
|
|
236
|
+
docker compose up -d --build --wait
|
|
237
237
|
```
|
|
238
238
|
|
|
239
239
|
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.
|
|
@@ -256,7 +256,33 @@ target = /mnt/exchange
|
|
|
256
256
|
|
|
257
257
|
`odoo-compose` does not create the source directory or change its ownership or permissions; host provisioning or the deployment process must prepare the directory before Docker starts.
|
|
258
258
|
|
|
259
|
-
### 1.5.
|
|
259
|
+
### 1.5. Limiting Docker Compose resources
|
|
260
|
+
|
|
261
|
+
You can optionally set CPU and memory limits for each service in the generated `compose.yaml` using `[docker]` in `odoo-project.ini`:
|
|
262
|
+
|
|
263
|
+
```ini
|
|
264
|
+
[docker]
|
|
265
|
+
odoo_cpus = 2.0
|
|
266
|
+
odoo_mem_limit = 4g
|
|
267
|
+
|
|
268
|
+
db_cpus = 1.0
|
|
269
|
+
db_mem_limit = 2g
|
|
270
|
+
|
|
271
|
+
mailpit_enabled = true
|
|
272
|
+
mailpit_cpus = 0.5
|
|
273
|
+
mailpit_mem_limit = 256m
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
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.
|
|
277
|
+
|
|
278
|
+
Regenerate the Docker configuration after editing the INI, then start the services:
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
odoo-compose
|
|
282
|
+
docker compose up -d --build --wait
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### 1.6. Script reference
|
|
260
286
|
|
|
261
287
|
The Docker workflow generates platform-specific helper scripts under `ROOT/docker/local/scripts/`. Unix-like systems generate `.sh` helpers; Windows generates the corresponding `.bat` helpers.
|
|
262
288
|
|
|
@@ -283,7 +309,7 @@ ODOO_DOCKER_SUDO=1 ./docker/local/scripts/run.sh --help
|
|
|
283
309
|
|
|
284
310
|
Docker helpers use `[config].db_name` when configured, or `odoo` by default.
|
|
285
311
|
|
|
286
|
-
#### 1.
|
|
312
|
+
#### 1.6.1. run
|
|
287
313
|
|
|
288
314
|
Runs Odoo in a one-off Docker container for the configured database:
|
|
289
315
|
|
|
@@ -297,7 +323,7 @@ Any extra arguments are passed through to Odoo:
|
|
|
297
323
|
./docker/local/scripts/run.sh -i base --stop-after-init
|
|
298
324
|
```
|
|
299
325
|
|
|
300
|
-
#### 1.
|
|
326
|
+
#### 1.6.2. shell
|
|
301
327
|
|
|
302
328
|
Opens an Odoo shell for the configured database:
|
|
303
329
|
|
|
@@ -307,7 +333,7 @@ Opens an Odoo shell for the configured database:
|
|
|
307
333
|
|
|
308
334
|
Any extra arguments are passed through to the underlying Odoo shell command.
|
|
309
335
|
|
|
310
|
-
#### 1.
|
|
336
|
+
#### 1.6.3. update
|
|
311
337
|
|
|
312
338
|
Updates installed addons using `click-odoo-update`:
|
|
313
339
|
|
|
@@ -321,7 +347,7 @@ Any extra arguments are passed through to `click-odoo-update`:
|
|
|
321
347
|
./docker/local/scripts/update.sh --update-all
|
|
322
348
|
```
|
|
323
349
|
|
|
324
|
-
#### 1.
|
|
350
|
+
#### 1.6.4. Database backup
|
|
325
351
|
|
|
326
352
|
Create a PostgreSQL custom-format dump under `ROOT/odoo-backups/`:
|
|
327
353
|
|
|
@@ -341,7 +367,7 @@ Pass an output path explicitly when needed:
|
|
|
341
367
|
./docker/local/scripts/backup-db.sh ./odoo-backups/pre-upgrade.dump
|
|
342
368
|
```
|
|
343
369
|
|
|
344
|
-
#### 1.
|
|
370
|
+
#### 1.6.5. Database restore
|
|
345
371
|
|
|
346
372
|
Restore a database dump with `pg_restore`:
|
|
347
373
|
|
|
@@ -349,7 +375,7 @@ Restore a database dump with `pg_restore`:
|
|
|
349
375
|
./docker/local/scripts/restore-db.sh ./odoo-backups/pre-upgrade.dump
|
|
350
376
|
```
|
|
351
377
|
|
|
352
|
-
Database restores use copy semantics by default
|
|
378
|
+
Database restores use copy semantics by default.
|
|
353
379
|
After `pg_restore`, Odoo database identity parameters are regenerated so the restored
|
|
354
380
|
database can coexist with the source database:
|
|
355
381
|
|
|
@@ -372,7 +398,7 @@ docker compose stop odoo
|
|
|
372
398
|
docker compose up -d odoo
|
|
373
399
|
```
|
|
374
400
|
|
|
375
|
-
#### 1.
|
|
401
|
+
#### 1.6.6. Database neutralization
|
|
376
402
|
|
|
377
403
|
For development or test databases restored from production, run the generated neutralization helper explicitly:
|
|
378
404
|
|
|
@@ -407,7 +433,7 @@ docker compose stop odoo
|
|
|
407
433
|
docker compose up -d odoo
|
|
408
434
|
```
|
|
409
435
|
|
|
410
|
-
#### 1.
|
|
436
|
+
#### 1.6.7. Filestore backup
|
|
411
437
|
|
|
412
438
|
Create a compressed tar archive of the selected database filestore:
|
|
413
439
|
|
|
@@ -438,7 +464,7 @@ docker compose stop odoo
|
|
|
438
464
|
docker compose up -d odoo
|
|
439
465
|
```
|
|
440
466
|
|
|
441
|
-
#### 1.
|
|
467
|
+
#### 1.6.8. Filestore restore
|
|
442
468
|
|
|
443
469
|
Restore a filestore archive:
|
|
444
470
|
|
|
@@ -454,7 +480,7 @@ docker compose stop odoo
|
|
|
454
480
|
docker compose up -d odoo
|
|
455
481
|
```
|
|
456
482
|
|
|
457
|
-
#### 1.
|
|
483
|
+
#### 1.6.9. Restic filestore backup
|
|
458
484
|
|
|
459
485
|
The generated Docker image also includes `restic`. Restic is an additional option intended especially for large filestores where incremental snapshots and deduplication are useful.
|
|
460
486
|
|
|
@@ -492,7 +518,7 @@ List or inspect snapshots through the generic wrapper:
|
|
|
492
518
|
./docker/local/scripts/restic.sh check
|
|
493
519
|
```
|
|
494
520
|
|
|
495
|
-
#### 1.
|
|
521
|
+
#### 1.6.10. Restic filestore restore
|
|
496
522
|
|
|
497
523
|
Restore the latest matching filestore snapshot:
|
|
498
524
|
|
|
@@ -514,7 +540,7 @@ docker compose up -d odoo
|
|
|
514
540
|
|
|
515
541
|
With `--force`, the restore helper removes the target database filestore before restoring it. When `latest` is used, the snapshot selection is restricted to the current `RESTIC_HOST` and source filestore path. Use the same `RESTIC_HOST` value that was used when the snapshot was created if the restore is performed from another host.
|
|
516
542
|
|
|
517
|
-
### 1.
|
|
543
|
+
### 1.7. Creating a Docker deploy build context
|
|
518
544
|
|
|
519
545
|
Use `--create-docker-deploy` to generate a self-contained Docker build context for CI/CD, testing, staging, production, or another non-local deployment workflow.
|
|
520
546
|
|
|
@@ -787,7 +813,7 @@ Restore a database dump with `pg_restore`:
|
|
|
787
813
|
./odoo-scripts/restore-db.sh ./odoo-backups/pre-upgrade.dump
|
|
788
814
|
```
|
|
789
815
|
|
|
790
|
-
Database restores use copy semantics by default
|
|
816
|
+
Database restores use copy semantics by default.
|
|
791
817
|
After `pg_restore`, Odoo database identity parameters are regenerated so the restored
|
|
792
818
|
database can coexist with the source database:
|
|
793
819
|
|
|
@@ -1412,6 +1438,10 @@ This section is optional.
|
|
|
1412
1438
|
- `mailpit_enabled` — enables a persistent Mailpit service in the local Docker workflow for SMTP and POP3 mail testing. Default: `false`. It does not affect the deploy build context.
|
|
1413
1439
|
- `mailpit_version` — version of `ghcr.io/axllent/mailpit` in local Compose. Default: `v1.31.3`. An explicitly set value must be a concrete version; empty values and `latest` are invalid.
|
|
1414
1440
|
- `mailpit_webui_port` — host port used for the Mailpit web UI. Default: `8025`. The internal Mailpit web UI port remains `8025`.
|
|
1441
|
+
- `odoo_cpus`, `db_cpus`, `mailpit_cpus` — optional CPU limits for the respective Compose services; positive decimal CPU counts such as `0.5` or `2.0`.
|
|
1442
|
+
- `odoo_mem_limit`, `db_mem_limit`, `mailpit_mem_limit` — optional memory limits for the respective Compose services; positive amounts such as `256m` or `4g`.
|
|
1443
|
+
|
|
1444
|
+
All six resource options default to unset and are omitted from generated Compose when empty. See [Limiting Docker Compose resources](#17-limiting-docker-compose-resources) for a complete example.
|
|
1415
1445
|
|
|
1416
1446
|
Example:
|
|
1417
1447
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.24.2"
|
|
File without changes
|
{odoo_devops_tools-1.24.2 → odoo_devops_tools-1.24.4}/odoo_devops_tools.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{odoo_devops_tools-1.24.2 → odoo_devops_tools-1.24.4}/odoo_devops_tools.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{odoo_devops_tools-1.24.2 → odoo_devops_tools-1.24.4}/odoo_devops_tools.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|