gOdoo-cli 1.0.3__tar.gz → 1.0.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.
Files changed (184) hide show
  1. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.agents/skills/godoo-editor-validation/SKILL.md +3 -4
  2. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.vscode/settings.json +1 -1
  3. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/AGENTS.md +18 -0
  4. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/PKG-INFO +2 -1
  5. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/docs/lifecycle.md +33 -0
  6. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/docs/workspace.md +8 -3
  7. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/pyproject.toml +1 -0
  8. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/__about__.py +1 -1
  9. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/db/archive.py +21 -12
  10. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/archive.py +74 -27
  11. godoo_cli-1.0.4/src/godoo_cli/runtime/backup.py +80 -0
  12. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/workspace/editor.py +25 -5
  13. godoo_cli-1.0.4/tests/database/test_db_backup.py +342 -0
  14. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/database/test_db_reset.py +12 -5
  15. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/integration/test_odoo_integration.py +40 -3
  16. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/workspace/test_workspace.py +58 -0
  17. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/workspace/test_workspace_layout.py +1 -2
  18. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/uv.lock +11 -0
  19. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.agents/skills/godoo-changeset-review/SKILL.md +0 -0
  20. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.agents/skills/godoo-runtime-validation/SKILL.md +0 -0
  21. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.agents/skills/godoo-source-workspaces/SKILL.md +0 -0
  22. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.codecovrc +0 -0
  23. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.dockerignore +0 -0
  24. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.env.sample +0 -0
  25. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.github/FUNDING.yml +0 -0
  26. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.github/workflows/README.md +0 -0
  27. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.github/workflows/quality.yml +0 -0
  28. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.github/workflows/version-bump.yml +0 -0
  29. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.github/workflows/version-publish.yml +0 -0
  30. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.gitignore +0 -0
  31. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.pre-commit-config.yaml +0 -0
  32. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.prettierrc.yml +0 -0
  33. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.vscode/extensions.json +0 -0
  34. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.vscode/tasks.json +0 -0
  35. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/LICENSE +0 -0
  36. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/README.md +0 -0
  37. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/addons/.gitkeep +0 -0
  38. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/addons/godoo_test_succ_mod/__init__.py +0 -0
  39. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/addons/godoo_test_succ_mod/__manifest__.py +0 -0
  40. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/addons/godoo_test_succ_mod/models/__init__.py +0 -0
  41. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/addons/godoo_test_succ_mod/tests/__init__.py +0 -0
  42. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/addons/godoo_test_succ_mod/tests/data.py +0 -0
  43. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/addons/godoo_test_succ_mod/tests/test_succ.py +0 -0
  44. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/assets/odoo_logo.png +0 -0
  45. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/config/nginx.conf.template +0 -0
  46. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/config/postgresql.conf +0 -0
  47. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/docker/Dockerfile +0 -0
  48. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/docker/docker-compose.base.yml +0 -0
  49. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/docker/docker-compose.dev.yml +0 -0
  50. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/docker/docker-compose.traefik.yml +0 -0
  51. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/docker/postgres-init/10-vector.sql +0 -0
  52. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/docs/architecture.md +0 -0
  53. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/docs/downstream.md +0 -0
  54. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/docs/hooks.md +0 -0
  55. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/makefile +0 -0
  56. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/odoo_manifest.yml +0 -0
  57. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/package-lock.json +0 -0
  58. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/package.json +0 -0
  59. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/scripts/check_docstring_style.py +0 -0
  60. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/scripts/migrations/development/shrink_database.py +0 -0
  61. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/__init__.py +0 -0
  62. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/__main__.py +0 -0
  63. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/__init__.py +0 -0
  64. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/common.py +0 -0
  65. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/configuration.py +0 -0
  66. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/db/__init__.py +0 -0
  67. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/db/cli.py +0 -0
  68. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/db/passwords.py +0 -0
  69. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/db/query.py +0 -0
  70. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/db/reset.py +0 -0
  71. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/odoo_bin/__init__.py +0 -0
  72. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/root.py +0 -0
  73. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/rpc/__init__.py +0 -0
  74. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/rpc/cli.py +0 -0
  75. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/rpc/config_parameters.py +0 -0
  76. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/rpc/importer.py +0 -0
  77. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/rpc/modules.py +0 -0
  78. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/rpc/translations.py +0 -0
  79. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/run.py +0 -0
  80. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/runtime/__init__.py +0 -0
  81. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/runtime/dependencies.py +0 -0
  82. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/runtime/init.py +0 -0
  83. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/runtime/launch.py +0 -0
  84. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/runtime/shell.py +0 -0
  85. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/runtime/shell_scripts/odoo_banner.py +0 -0
  86. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/runtime/shell_scripts/render_report.py +0 -0
  87. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/runtime/start.py +0 -0
  88. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/runtime/status.py +0 -0
  89. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/test/__init__.py +0 -0
  90. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/test/cli.py +0 -0
  91. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/test/load_data.py +0 -0
  92. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/test/run.py +0 -0
  93. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/upgrade.py +0 -0
  94. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/workspace/__init__.py +0 -0
  95. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/workspace/common.py +0 -0
  96. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/workspace/configure.py +0 -0
  97. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/workspace/image.py +0 -0
  98. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/workspace/storage.py +0 -0
  99. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/workspace/sync.py +0 -0
  100. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/database/__init__.py +0 -0
  101. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/database/connection.py +0 -0
  102. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/database/postgres.py +0 -0
  103. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/database/settings.py +0 -0
  104. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/database/state.py +0 -0
  105. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/git/__init__.py +0 -0
  106. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/git/git_url.py +0 -0
  107. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/git/repository.py +0 -0
  108. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/helpers/__init__.py +0 -0
  109. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/helpers/hashing.py +0 -0
  110. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/helpers/modules_git.py +0 -0
  111. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/helpers/progress.py +0 -0
  112. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/helpers/system.py +0 -0
  113. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/models/__init__.py +0 -0
  114. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/models/godoo_git_repo.py +0 -0
  115. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/models/godoo_manifest.py +0 -0
  116. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/models/godoo_models.py +0 -0
  117. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/models/godoo_modules.py +0 -0
  118. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/__init__.py +0 -0
  119. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/baseline.py +0 -0
  120. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/checksums.py +0 -0
  121. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/dataset.py +0 -0
  122. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/filestore.py +0 -0
  123. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/hooks.py +0 -0
  124. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/lifecycle.py +0 -0
  125. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/locks.py +0 -0
  126. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/nginx.py +0 -0
  127. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/odoo.py +0 -0
  128. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/promotion.py +0 -0
  129. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/reset.py +0 -0
  130. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/restore.py +0 -0
  131. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/status.py +0 -0
  132. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/workspace/__init__.py +0 -0
  133. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/workspace/git.py +0 -0
  134. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/workspace/image.py +0 -0
  135. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/workspace/manifest.py +0 -0
  136. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/workspace/operations.py +0 -0
  137. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/workspace/specifications.py +0 -0
  138. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/workspace/storage.py +0 -0
  139. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/workspace/types.py +0 -0
  140. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/__init__.py +0 -0
  141. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/commands/test_db_archive_cli.py +0 -0
  142. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/commands/test_db_query_cli.py +0 -0
  143. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/commands/test_odoo_bin_commands.py +0 -0
  144. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/commands/test_rpc_commands.py +0 -0
  145. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/commands/test_runtime_cli.py +0 -0
  146. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/commands/test_runtime_dependencies.py +0 -0
  147. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/commands/test_runtime_shell_scripts.py +0 -0
  148. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/commands/test_runtime_start.py +0 -0
  149. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/commands/test_test_run.py +0 -0
  150. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/commands/test_upgrade.py +0 -0
  151. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/conftest.py +0 -0
  152. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/database/test_db_query.py +0 -0
  153. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/database/test_db_restore.py +0 -0
  154. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/git/test_git_url_uri.py +0 -0
  155. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/integration/test_container_contract.py +0 -0
  156. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_archive_crc.py +0 -0
  157. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_baseline_recovery.py +0 -0
  158. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_checksums.py +0 -0
  159. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_database_copy.py +0 -0
  160. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_dataset.py +0 -0
  161. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_filestore.py +0 -0
  162. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_filestore_copy.py +0 -0
  163. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_hooks.py +0 -0
  164. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_init_hooks.py +0 -0
  165. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_init_policy.py +0 -0
  166. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_lifecycle.py +0 -0
  167. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_lifecycle_service.py +0 -0
  168. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_nginx.py +0 -0
  169. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_nginx_cli.py +0 -0
  170. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_odoo_config_generation.py +0 -0
  171. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_odoo_preflight.py +0 -0
  172. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_odoo_version_compatibility.py +0 -0
  173. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_report_url.py +0 -0
  174. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_reset_baseline.py +0 -0
  175. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_runtime_models.py +0 -0
  176. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_runtime_operations.py +0 -0
  177. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_runtime_source_environment.py +0 -0
  178. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/test_logging.py +0 -0
  179. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/workspace/test_godoo_manifest.py +0 -0
  180. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/workspace/test_workspace_git.py +0 -0
  181. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/workspace/test_workspace_image.py +0 -0
  182. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/workspace/test_workspace_runtime_env.py +0 -0
  183. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/workspace/test_workspace_storage.py +0 -0
  184. {godoo_cli-1.0.3 → godoo_cli-1.0.4}/thirdparty/.gitkeep +0 -0
@@ -8,10 +8,9 @@ description:
8
8
 
9
9
  Read `docs/workspace.md`, then open the generated `<project>.code-workspace`.
10
10
 
11
- gOdoo sets `python.languageServer` to `None` so the installed Odoo IDE extension provides Python and Odoo analysis
12
- without a competing Pylance server. Keep the Python extension for interpreter selection and debugging. The generated
13
- `python.analysis.extraPaths` covers the resolved project, Odoo, third-party, and archive addon roots. The supported
14
- source mapping is the generated multi-root workspace plus its generated extra paths.
11
+ Keep the Python extension for interpreter selection and debugging. The generated `python.analysis.extraPaths` covers the
12
+ resolved project, Odoo, third-party, and archive addon roots. The supported source mapping is the generated multi-root
13
+ workspace plus its generated extra paths.
15
14
 
16
15
  Reload the window after generation, save representative project, Odoo, and addon files, then inspect VS Code Problems.
17
16
  Confirm each diagnostic refers to the intended selected host worktree. Problems validate editor analysis, not execution.
@@ -2,7 +2,7 @@
2
2
  "python.testing.unittestEnabled": false,
3
3
  "python.testing.pytestEnabled": false,
4
4
  "xml.symbols.enabled": false,
5
- "python.languageServer": "Pylance",
5
+ "python.languageServer": "None",
6
6
  "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
7
7
  "ruff.path": ["${workspaceFolder}/.venv/bin/ruff"],
8
8
  "ruff.configurationPreference": "filesystemFirst",
@@ -14,6 +14,24 @@ coherent change.
14
14
  whole-changeset reviews.
15
15
  - Keep shared Docker validation sequential.
16
16
 
17
+ ## Branches and releases
18
+
19
+ - Start each independent change on a short-lived `codex/<topic>` branch from freshly fetched `origin/main`. Do not reuse
20
+ a merged branch for new work. Keep implementation, tests, and documentation for one behavior together.
21
+ - Before switching or creating a branch, inspect staged, unstaged, and untracked work. Preserve existing changes and
22
+ verify the resulting diff against `origin/main`; use a separate Git worktree for concurrent independent work.
23
+ - Open topic PRs into `main` and check Quality results before merging. Topic-branch pushes alone do not trigger Quality.
24
+ Follow the existing merge strategy and commit conventions. Push and merge only within explicit authorization.
25
+ - After merge, return to an updated `main` or start the next topic branch from current `origin/main`. Prune stale remote
26
+ references and remove merged branches only after checking ancestry and worktree ownership; preserve unfinished work.
27
+ - Leave package version changes to `.github/workflows/version-bump.yml`, dispatched against `main` with the chosen bump
28
+ type. It creates `release/<version>` and the release PR; keep that PR limited to preparing the release.
29
+ - Review the release PR description and available Quality results before merging: its description becomes the release
30
+ notes. The release-labeled PR must target `main` and match the title, branch, and version identities documented in
31
+ `.github/workflows/README.md`. Publication uses the release merge commit for PyPI, the tag, and the GitHub Release.
32
+ - Keep release PRs short-lived and coordinate other merges to preserve the intended release scope. Do not use a
33
+ long-lived `release` or `develop` branch for this workflow, or move an existing release tag to repair publication.
34
+
17
35
  ## Code and CLI
18
36
 
19
37
  - Every shell script under `scripts/` has a purpose comment immediately below its shebang.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: gOdoo-cli
3
- Version: 1.0.3
3
+ Version: 1.0.4
4
4
  Summary: Odoo lifecycle, Python dependency preflight, and shared Git workspaces.
5
5
  Project-URL: repository, https://github.com/OpenJKSoftware/gOdoo
6
6
  Author-email: Joshua Kreuder <Joshua_Kreuder@outlook.com>
@@ -10,6 +10,7 @@ Keywords: docker,godoo,odoo,worktree
10
10
  Requires-Python: >=3.11
11
11
  Requires-Dist: gitpython>=3.1.27
12
12
  Requires-Dist: godoo-rpc>=0.1.4
13
+ Requires-Dist: json-with-comments>=1.3.0
13
14
  Requires-Dist: packaging>=21.0
14
15
  Requires-Dist: passlib>=1.7.3
15
16
  Requires-Dist: psycopg2-binary>=2.7.7; sys_platform != 'linux'
@@ -8,6 +8,39 @@ hooks, then launch Odoo. Runtime commands require Odoo 16 or newer.
8
8
  Stop ordinary Odoo writers before initialization, restore, clone, or replacement. gOdoo locks coordinate gOdoo
9
9
  operations; they do not stop ordinary Odoo writes.
10
10
 
11
+ ## Back up a live runtime
12
+
13
+ ```bash
14
+ # Default: one combined ZIP, including attachments.
15
+ godoo db backup /backups/runtime.zip --db-name odoo
16
+
17
+ # PostgreSQL custom format plus a separate filestore, copied with rsync.
18
+ godoo db backup /backups/current --db-name odoo --format dump
19
+ ```
20
+
21
+ `--format` accepts `zip` (default) or `dump`; `GODOO_BACKUP_FORMAT` supplies the environment default. Both formats
22
+ capture the database first, then the filestore. The `dump` destination contains `odoo.dump` and
23
+ `odoo_filestore/filestore/<database>/`, directly usable by `godoo runtime init --dump-path` or
24
+ `godoo db restore <directory> --force`. It requires `pg_dump` and `rsync` in the execution environment. The destination
25
+ must be outside Odoo's data directory. Borg or another backup system owns history; each successful run replaces the
26
+ selected destination.
27
+
28
+ During capture, gOdoo holds a row lock on `base.autovacuum_job`. Odoo's scheduler skips that internal cleanup job;
29
+ users, attachment writes, integrations, and other background jobs continue. No app restart or attachment-table write
30
+ lock is needed. The lock is released on completion or connection closure. If cleanup is already running, backup fails
31
+ immediately so it can be retried afterward. The backup fails if the guard connection is lost before publication.
32
+
33
+ This protects standard Odoo filestore storage and scheduled cleanup on Odoo 16 and newer, including Odoo 19's manual
34
+ scheduled-action trigger. Do not run private cleanup methods, custom file deletion, attachment storage migration, or
35
+ Odoo 16–18 manual autovacuum during backup. External attachment backends need their own backup procedure. Files added
36
+ after the database snapshot may also be copied; they are harmless extra files, not additional database records.
37
+
38
+ Backups are staged before replacing the previous result. Directory destinations must be dedicated child directories
39
+ inside a mounted backup volume, not the mount root itself. Replacing a directory briefly renames the previous pair to
40
+ `.<destination-name>.previous`; failed promotion restores it. After an abrupt termination, inspect that directory and
41
+ the destination before retrying. gOdoo refuses to overwrite a retained previous pair. Start Borg only after this command
42
+ succeeds, and keep other backup readers out of the brief directory promotion window.
43
+
11
44
  ## Initialize and launch
12
45
 
13
46
  Run in the configured downstream runtime environment. CLI values override exported environment values, then the project
@@ -90,18 +90,23 @@ make typecheck
90
90
  ```
91
91
 
92
92
  Open `godoo.code-workspace` for this repository, or the generated `<project>.code-workspace` downstream. Opening the
93
- same files in another project's window does not load their workspace settings.
93
+ same files in another project's window does not load their workspace settings. The generated workspace copies settings
94
+ from the project's `.vscode/settings.json`, including JSON comments and trailing commas. Set fixed editor preferences
95
+ there, including the interpreter, language server, and test discovery. gOdoo generates `python.analysis.extraPaths` from
96
+ the selected sources and takes precedence for that setting in the generated workspace. Run `workspace configure` after
97
+ changing project settings; `workspace check` reports the workspace as stale until it is regenerated. Projects without
98
+ those fixed settings inherit their VS Code defaults or other configured values.
94
99
 
95
100
  | Generated setting | Contract |
96
101
  | -------------------- | ---------------------------------------------------------------------------------------------------------- |
97
102
  | Workspace folder | Fixed name `gOdoo`; project-relative paths use `${workspaceFolder:gOdoo}`. |
98
103
  | Selected sources | Odoo, addon, and archive paths remain absolute host paths. |
99
- | Python analysis | The Python extension's language server is disabled so Odoo IDE can provide Python and Odoo analysis. |
100
- | Python extension | Keep it installed for interpreter selection and debugging. |
101
104
  | Extra paths | `python.analysis.extraPaths` includes the selected sources. |
102
105
  | `gOdoo: attach` | Maps the project to `/odoo/godoo_workspace` and the selected sources to their development-container paths. |
103
106
  | `pyrightconfig.json` | Ignored project-root file covering `src/`, `addons/`, `scripts/`, project addons, and resolved sources. |
104
107
 
108
+ Keep the Python extension for interpreter selection and debugging.
109
+
105
110
  Python uses Ruff for formatting and safe fixes on save, with lint rules from `pyproject.toml`, including C901. Supported
106
111
  text formats use Prettier and `.prettierrc.yml`.
107
112
 
@@ -27,6 +27,7 @@ dependencies = [
27
27
  "packaging>=21.0",
28
28
  "typer-common-functions>=0.1.0",
29
29
  "typer>=0.9.0",
30
+ "json-with-comments>=1.3.0",
30
31
  ]
31
32
 
32
33
  [project.optional-dependencies]
@@ -1,3 +1,3 @@
1
1
  """Version information."""
2
2
 
3
- __version__ = "1.0.3"
3
+ __version__ = "1.0.4"
@@ -15,6 +15,7 @@ from ...runtime.archive import (
15
15
  load_runtime_archive,
16
16
  prepare_archive_with_original_filestore,
17
17
  )
18
+ from ...runtime.backup import BackupFormat
18
19
  from ...runtime.odoo import SUPPORTED_ODOO_VERSION_SPECIFIER
19
20
  from ..common import CommonCLI
20
21
  from ..configuration import require_cli_odoo_version, resolve_development_odoo_main_path
@@ -29,25 +30,33 @@ def dump_database(
29
30
  odoo_main_path: Annotated[Path, CLI.odoo_paths.bin_path],
30
31
  odoo_conf_path: Annotated[Path | None, CLI.odoo_paths.conf_path] = None,
31
32
  data_dir: Annotated[Path, CLI.odoo_paths.data_dir] = Path("/var/lib/odoo"),
33
+ backup_format: Annotated[
34
+ BackupFormat,
35
+ typer.Option(
36
+ "--format",
37
+ envvar="GODOO_BACKUP_FORMAT",
38
+ help="zip: combined archive; dump: custom PostgreSQL dump and rsync filestore directory.",
39
+ ),
40
+ ] = BackupFormat.ZIP,
32
41
  ) -> int:
33
- """Create an Odoo runtime archive."""
42
+ """Back up a live Odoo database and its filestore in the selected format."""
34
43
  try:
35
44
  odoo_main_path = resolve_development_odoo_main_path(odoo_main_path)
36
45
  version = require_cli_odoo_version(odoo_main_path, SUPPORTED_ODOO_VERSION_SPECIFIER)
37
- return CLI.returner(
38
- dump_runtime_archive(
39
- db_name=db_name,
40
- archive_path=archive_path,
41
- odoo_bin_path=odoo_main_path / "odoo-bin",
42
- odoo_conf_path=odoo_conf_path,
43
- data_dir=data_dir,
44
- odoo_version=version.major,
45
- connection=DBConnection.from_odoo_config(db_name, odoo_conf_path),
46
- )
46
+ result = dump_runtime_archive(
47
+ db_name=db_name,
48
+ backup_format=backup_format,
49
+ archive_path=archive_path,
50
+ odoo_bin_path=odoo_main_path / "odoo-bin",
51
+ odoo_conf_path=odoo_conf_path,
52
+ data_dir=data_dir,
53
+ odoo_version=version.major,
54
+ connection=DBConnection.from_odoo_config(db_name, odoo_conf_path),
47
55
  )
48
- except OSError:
56
+ except (OSError, RuntimeError, ValueError, psycopg2.Error):
49
57
  LOGGER.exception("Archive creation failed")
50
58
  return CLI.returner(1)
59
+ return CLI.returner(result)
51
60
 
52
61
 
53
62
  # Typer declares each archive-restore option in this public callback signature.
@@ -24,6 +24,7 @@ from ..database.connection import DBConnection
24
24
  from ..database.postgres import postgres_argv, postgres_environment
25
25
  from ..database.state import base_module_major, database_exists
26
26
  from ..helpers.progress import ProgressPhase as _ProgressPhase
27
+ from .backup import BackupFormat, publish_backup_directory, retain_attachment_files
27
28
  from .baseline import baseline_database_name, save_runtime_baseline
28
29
  from .filestore import copy_filestore
29
30
  from .locks import begin_runtime_restore, runtime_data_directory, runtime_locks
@@ -273,7 +274,9 @@ def _stream_sql_input(command: Sequence[str], dump_path: Path, environment: dict
273
274
  return 0
274
275
 
275
276
 
276
- def dump_runtime_archive(
277
+ # Keep capture, retention, and promotion together, with Odoo and PostgreSQL targets supplied explicitly.
278
+ # pylint: disable-next=too-many-arguments
279
+ def dump_runtime_archive( # noqa: C901
277
280
  *,
278
281
  db_name: str,
279
282
  archive_path: Path,
@@ -283,54 +286,98 @@ def dump_runtime_archive(
283
286
  runner: CommandRunner | None = None,
284
287
  odoo_version: int | None = None,
285
288
  connection: DBConnection | None = None,
289
+ backup_format: BackupFormat = BackupFormat.ZIP,
286
290
  ) -> int:
287
- """Create an archive atomically, retaining any prior destination on failure."""
291
+ """Capture the database before attachments, then publish the complete backup.
292
+
293
+ Returns:
294
+ Zero on success, otherwise the failing subprocess exit status.
295
+
296
+ Raises:
297
+ ValueError: If the destination overlaps runtime storage or has the wrong type.
298
+ """
288
299
  with runtime_locks(data_dir, db_name, odoo_conf_path=odoo_conf_path):
300
+ target_connection = connection or _archive_connection(db_name, odoo_conf_path)
301
+ runtime_data = runtime_data_directory(data_dir, odoo_conf_path)
302
+ archive_path = archive_path.resolve()
303
+ if archive_path == runtime_data or archive_path in runtime_data.parents or runtime_data in archive_path.parents:
304
+ message = "The backup destination must be outside Odoo's data directory."
305
+ raise ValueError(message)
306
+ if backup_format == BackupFormat.DUMP and archive_path.exists() and not archive_path.is_dir():
307
+ message = "The dump backup destination must be a directory."
308
+ raise ValueError(message)
289
309
  archive_path.parent.mkdir(parents=True, exist_ok=True)
290
310
  temporary = archive_path.with_name(f".{archive_path.name}.{uuid.uuid4().hex}.tmp")
291
311
  sql_temporary = temporary.with_suffix(".sql")
292
312
  try:
293
- target_connection = connection or _archive_connection(db_name, odoo_conf_path)
294
- runtime_data = runtime_data_directory(data_dir, odoo_conf_path)
295
313
  native = _uses_native_db_commands(odoo_version, odoo_bin_path)
296
- if native:
314
+ separate = backup_format == BackupFormat.DUMP
315
+ if separate:
316
+ temporary.mkdir()
317
+ if native and not separate:
297
318
  command = odoo_db_command(
298
319
  odoo_bin_path=odoo_bin_path,
299
320
  odoo_conf_path=odoo_conf_path,
300
321
  data_dir=data_dir,
301
- arguments=["dump", db_name, str(temporary)],
322
+ arguments=["dump", "--no-filestore", db_name, str(temporary)],
302
323
  )
303
324
  else:
304
325
  command = [
305
326
  "pg_dump",
306
327
  "--no-owner",
307
- "--format=plain",
328
+ "--format=custom" if separate else "--format=plain",
308
329
  "--file",
309
- str(sql_temporary),
330
+ str(temporary / LEGACY_DUMP_FILENAME if separate else sql_temporary),
310
331
  *postgres_argv(target_connection),
311
332
  ]
312
- with _ProgressPhase("Dumping runtime database", logger=LOGGER) as progress:
313
- if runner is not None:
314
- result = runner(command)
315
- elif native:
316
- result = _run_odoo_db(command)
317
- else:
318
- result = _run_postgres_command(command, target_connection)
319
- progress.failed = result != 0
320
- if result:
321
- return result
322
- if not native:
333
+ with retain_attachment_files(target_connection):
334
+ with _ProgressPhase("Dumping runtime database", logger=LOGGER) as progress:
335
+ if runner is not None:
336
+ result = runner(command)
337
+ else:
338
+ with postgres_environment(target_connection) as environment:
339
+ if native and not separate:
340
+ # Odoo 19 reads these before its config file, including empty overrides.
341
+ environment.update(
342
+ PGHOST=target_connection.hostname,
343
+ PGPORT=str(target_connection.port or 5432),
344
+ PGUSER=target_connection.username,
345
+ PGPASSWORD=target_connection.password,
346
+ PGSSLMODE=target_connection.sslmode or "prefer",
347
+ )
348
+ result = run_odoo_command(command, env=environment).returncode
349
+ progress.failed = result != 0
350
+ if result:
351
+ return result
323
352
  filestore = runtime_filestore_path(runtime_data, db_name)
324
- with zipfile.ZipFile(temporary, "w", zipfile.ZIP_DEFLATED) as archive:
325
- archive.write(sql_temporary, "dump.sql")
326
- if filestore.is_dir():
327
- for path in sorted(filestore.rglob("*")):
328
- if path.is_file():
329
- archive.write(path, Path("filestore") / path.relative_to(filestore))
330
- os.replace(temporary, archive_path)
353
+ with _ProgressPhase("Copying backup filestore", logger=LOGGER) as progress:
354
+ if separate:
355
+ destination = temporary / LEGACY_FILESTORE_DIRECTORY / "filestore" / db_name
356
+ destination.mkdir(parents=True)
357
+ if filestore.is_dir():
358
+ command = ["rsync", "-rLpt", f"{filestore}/", f"{destination}/"]
359
+ result = runner(command) if runner is not None else run_odoo_command(command).returncode
360
+ progress.failed = result != 0
361
+ if result:
362
+ return result
363
+ else:
364
+ with zipfile.ZipFile(temporary, "a" if native else "w", zipfile.ZIP_DEFLATED) as archive:
365
+ if not native:
366
+ archive.write(sql_temporary, "dump.sql")
367
+ if filestore.is_dir():
368
+ for path in sorted(filestore.rglob("*")):
369
+ if path.is_file():
370
+ archive.write(path, Path("filestore") / path.relative_to(filestore))
371
+ if separate:
372
+ publish_backup_directory(temporary, archive_path)
373
+ else:
374
+ os.replace(temporary, archive_path)
331
375
  return 0
332
376
  finally:
333
- temporary.unlink(missing_ok=True)
377
+ if temporary.is_dir():
378
+ shutil.rmtree(temporary)
379
+ else:
380
+ temporary.unlink(missing_ok=True)
334
381
  sql_temporary.unlink(missing_ok=True)
335
382
 
336
383
 
@@ -0,0 +1,80 @@
1
+ """Coordinate online attachment retention and backup publication."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ import os
7
+ import shutil
8
+ from collections.abc import Iterator
9
+ from contextlib import contextmanager
10
+ from enum import StrEnum
11
+ from pathlib import Path
12
+
13
+ import psycopg2
14
+
15
+ from ..database.connection import DBConnection
16
+
17
+ LOGGER = logging.getLogger(__name__)
18
+
19
+
20
+ class BackupFormat(StrEnum):
21
+ """Select a combined Odoo ZIP or a separate custom dump and filestore."""
22
+
23
+ ZIP = "zip"
24
+ DUMP = "dump"
25
+
26
+
27
+ @contextmanager
28
+ def retain_attachment_files(connection: DBConnection) -> Iterator[None]:
29
+ """Defer the standard autovacuum job without locking attachment writes.
30
+
31
+ Raises:
32
+ RuntimeError: If the standard cleanup job is missing or already running.
33
+ """
34
+ with connection.connect() as cursor:
35
+ try:
36
+ cursor.execute(
37
+ "SELECT cron.id FROM ir_cron AS cron "
38
+ "JOIN ir_model_data AS data ON data.res_id = cron.id AND data.model = 'ir.cron' "
39
+ "WHERE data.module = 'base' AND data.name = 'autovacuum_job' "
40
+ "FOR NO KEY UPDATE OF cron NOWAIT"
41
+ )
42
+ except psycopg2.errors.LockNotAvailable as error:
43
+ message = "Odoo attachment cleanup is running; retry the backup after it finishes."
44
+ raise RuntimeError(message) from error
45
+ if len(cursor.fetchall()) != 1:
46
+ message = "Cannot retain attachment files: Odoo's base.autovacuum_job is missing."
47
+ raise RuntimeError(message)
48
+ LOGGER.info("Deferring Odoo's internal autovacuum job during backup")
49
+ yield
50
+ # A server timeout or lost connection must fail before publication.
51
+ cursor.execute("SELECT 1")
52
+ LOGGER.info("Released Odoo's internal autovacuum job after backup")
53
+
54
+
55
+ def publish_backup_directory(staged: Path, destination: Path) -> None:
56
+ """Promote a complete pair, restoring the previous pair on rename failure.
57
+
58
+ Raises:
59
+ RuntimeError: If a previous promotion needs recovery.
60
+ OSError: If promotion fails, after attempting to restore the previous pair.
61
+ """
62
+ previous = destination.with_name(f".{destination.name}.previous")
63
+ if previous.exists():
64
+ message = f"Backup recovery is required: inspect {previous} and {destination} before retrying."
65
+ raise RuntimeError(message)
66
+ had_previous = destination.exists()
67
+ if had_previous:
68
+ os.replace(destination, previous)
69
+ try:
70
+ os.replace(staged, destination)
71
+ except OSError:
72
+ if had_previous:
73
+ LOGGER.warning("Restoring previous backup from %s", previous)
74
+ os.replace(previous, destination)
75
+ raise
76
+ if had_previous:
77
+ try:
78
+ shutil.rmtree(previous)
79
+ except OSError:
80
+ LOGGER.warning("Backup completed, but previous backup remains at %s", previous, exc_info=True)
@@ -5,7 +5,9 @@ from __future__ import annotations
5
5
  from pathlib import Path
6
6
  from typing import TYPE_CHECKING, Any
7
7
 
8
- from .types import ResolvedSource
8
+ import jsonc
9
+
10
+ from .types import ResolvedSource, WorkspaceError
9
11
 
10
12
  if TYPE_CHECKING:
11
13
  from .types import WorkspaceSettings
@@ -16,6 +18,27 @@ PROJECT_FOLDER_NAME = "gOdoo"
16
18
  PROJECT_WORKSPACE_FOLDER = f"${{workspaceFolder:{PROJECT_FOLDER_NAME}}}"
17
19
 
18
20
 
21
+ def _project_settings(project_root: Path) -> dict[str, Any]:
22
+ """Read project editor settings in VS Code's JSON-with-comments format."""
23
+ path = project_root / ".vscode" / "settings.json"
24
+ try:
25
+ content = path.read_text(encoding="utf-8-sig")
26
+ except FileNotFoundError:
27
+ return {}
28
+ except (OSError, UnicodeError) as error:
29
+ message = f"Unable to read VS Code settings {path}: {error}"
30
+ raise WorkspaceError(message) from error
31
+ try:
32
+ data = jsonc.loads(content)
33
+ except jsonc.JSONDecodeError as error:
34
+ message = f"Invalid VS Code settings {path}: {error}"
35
+ raise WorkspaceError(message) from error
36
+ if not isinstance(data, dict):
37
+ message = f"VS Code settings must be an object: {path}"
38
+ raise WorkspaceError(message)
39
+ return data
40
+
41
+
19
42
  def workspace_data(
20
43
  settings: WorkspaceSettings,
21
44
  sources: list[ResolvedSource],
@@ -50,11 +73,8 @@ def workspace_data(
50
73
  return {
51
74
  "folders": folders,
52
75
  "settings": {
53
- "python.defaultInterpreterPath": f"{PROJECT_WORKSPACE_FOLDER}/.venv/bin/python",
54
- "python.languageServer": "None",
76
+ **_project_settings(settings.project_root),
55
77
  "python.analysis.extraPaths": python_extra_paths,
56
- "python.testing.pytestEnabled": False,
57
- "python.testing.unittestEnabled": False,
58
78
  },
59
79
  "launch": {
60
80
  "version": "0.2.0",