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.
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.agents/skills/godoo-editor-validation/SKILL.md +3 -4
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.vscode/settings.json +1 -1
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/AGENTS.md +18 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/PKG-INFO +2 -1
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/docs/lifecycle.md +33 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/docs/workspace.md +8 -3
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/pyproject.toml +1 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/__about__.py +1 -1
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/db/archive.py +21 -12
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/archive.py +74 -27
- godoo_cli-1.0.4/src/godoo_cli/runtime/backup.py +80 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/workspace/editor.py +25 -5
- godoo_cli-1.0.4/tests/database/test_db_backup.py +342 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/database/test_db_reset.py +12 -5
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/integration/test_odoo_integration.py +40 -3
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/workspace/test_workspace.py +58 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/workspace/test_workspace_layout.py +1 -2
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/uv.lock +11 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.agents/skills/godoo-changeset-review/SKILL.md +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.agents/skills/godoo-runtime-validation/SKILL.md +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.agents/skills/godoo-source-workspaces/SKILL.md +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.codecovrc +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.dockerignore +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.env.sample +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.github/FUNDING.yml +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.github/workflows/README.md +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.github/workflows/quality.yml +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.github/workflows/version-bump.yml +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.github/workflows/version-publish.yml +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.gitignore +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.pre-commit-config.yaml +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.prettierrc.yml +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.vscode/extensions.json +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/.vscode/tasks.json +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/LICENSE +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/README.md +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/addons/.gitkeep +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/addons/godoo_test_succ_mod/__init__.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/addons/godoo_test_succ_mod/__manifest__.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/addons/godoo_test_succ_mod/models/__init__.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/addons/godoo_test_succ_mod/tests/__init__.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/addons/godoo_test_succ_mod/tests/data.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/addons/godoo_test_succ_mod/tests/test_succ.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/assets/odoo_logo.png +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/config/nginx.conf.template +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/config/postgresql.conf +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/docker/Dockerfile +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/docker/docker-compose.base.yml +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/docker/docker-compose.dev.yml +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/docker/docker-compose.traefik.yml +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/docker/postgres-init/10-vector.sql +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/docs/architecture.md +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/docs/downstream.md +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/docs/hooks.md +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/makefile +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/odoo_manifest.yml +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/package-lock.json +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/package.json +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/scripts/check_docstring_style.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/scripts/migrations/development/shrink_database.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/__init__.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/__main__.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/__init__.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/common.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/configuration.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/db/__init__.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/db/cli.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/db/passwords.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/db/query.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/db/reset.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/odoo_bin/__init__.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/root.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/rpc/__init__.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/rpc/cli.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/rpc/config_parameters.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/rpc/importer.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/rpc/modules.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/rpc/translations.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/run.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/runtime/__init__.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/runtime/dependencies.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/runtime/init.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/runtime/launch.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/runtime/shell.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/runtime/shell_scripts/odoo_banner.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/runtime/shell_scripts/render_report.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/runtime/start.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/runtime/status.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/test/__init__.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/test/cli.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/test/load_data.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/test/run.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/upgrade.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/workspace/__init__.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/workspace/common.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/workspace/configure.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/workspace/image.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/workspace/storage.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/commands/workspace/sync.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/database/__init__.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/database/connection.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/database/postgres.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/database/settings.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/database/state.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/git/__init__.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/git/git_url.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/git/repository.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/helpers/__init__.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/helpers/hashing.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/helpers/modules_git.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/helpers/progress.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/helpers/system.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/models/__init__.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/models/godoo_git_repo.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/models/godoo_manifest.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/models/godoo_models.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/models/godoo_modules.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/__init__.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/baseline.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/checksums.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/dataset.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/filestore.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/hooks.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/lifecycle.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/locks.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/nginx.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/odoo.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/promotion.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/reset.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/restore.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/runtime/status.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/workspace/__init__.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/workspace/git.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/workspace/image.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/workspace/manifest.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/workspace/operations.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/workspace/specifications.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/workspace/storage.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/src/godoo_cli/workspace/types.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/__init__.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/commands/test_db_archive_cli.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/commands/test_db_query_cli.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/commands/test_odoo_bin_commands.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/commands/test_rpc_commands.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/commands/test_runtime_cli.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/commands/test_runtime_dependencies.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/commands/test_runtime_shell_scripts.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/commands/test_runtime_start.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/commands/test_test_run.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/commands/test_upgrade.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/conftest.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/database/test_db_query.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/database/test_db_restore.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/git/test_git_url_uri.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/integration/test_container_contract.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_archive_crc.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_baseline_recovery.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_checksums.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_database_copy.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_dataset.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_filestore.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_filestore_copy.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_hooks.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_init_hooks.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_init_policy.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_lifecycle.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_lifecycle_service.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_nginx.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_nginx_cli.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_odoo_config_generation.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_odoo_preflight.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_odoo_version_compatibility.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_report_url.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_reset_baseline.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_runtime_models.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_runtime_operations.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/runtime/test_runtime_source_environment.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/test_logging.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/workspace/test_godoo_manifest.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/workspace/test_workspace_git.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/workspace/test_workspace_image.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/workspace/test_workspace_runtime_env.py +0 -0
- {godoo_cli-1.0.3 → godoo_cli-1.0.4}/tests/workspace/test_workspace_storage.py +0 -0
- {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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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": "
|
|
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
|
+
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
|
|
|
@@ -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
|
-
"""
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
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
|
-
"""
|
|
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
|
-
|
|
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
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
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
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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",
|