pz-cli 5.0.0__py3-none-any.whl
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.
- pz_cli-5.0.0.dist-info/METADATA +51 -0
- pz_cli-5.0.0.dist-info/RECORD +399 -0
- pz_cli-5.0.0.dist-info/WHEEL +4 -0
- pz_cli-5.0.0.dist-info/entry_points.txt +5 -0
- pz_core/__init__.py +64 -0
- pz_core/__main__.py +7 -0
- pz_core/app.py +45 -0
- pz_core/assembly/__init__.py +17 -0
- pz_core/assembly/app_discovery.py +1240 -0
- pz_core/assembly/apps/__init__.py +25 -0
- pz_core/assembly/apps/compiler.py +824 -0
- pz_core/assembly/apps/providers.py +188 -0
- pz_core/assembly/cli/__init__.py +9 -0
- pz_core/assembly/cli/compiler.py +166 -0
- pz_core/assembly/diagnostics/__init__.py +21 -0
- pz_core/assembly/diagnostics/checks.py +257 -0
- pz_core/assembly/diagnostics/dev_sources.py +569 -0
- pz_core/assembly/events/__init__.py +5 -0
- pz_core/assembly/events/compiler.py +219 -0
- pz_core/assembly/extensions/__init__.py +5 -0
- pz_core/assembly/extensions/compiler.py +696 -0
- pz_core/assembly/infra/__init__.py +6 -0
- pz_core/assembly/infra/compiler.py +272 -0
- pz_core/assembly/lifecycle/__init__.py +5 -0
- pz_core/assembly/lifecycle/compiler.py +95 -0
- pz_core/assembly/middlewares/__init__.py +19 -0
- pz_core/assembly/middlewares/compiler.py +508 -0
- pz_core/assembly/project/__init__.py +7 -0
- pz_core/assembly/project/compiler.py +9 -0
- pz_core/assembly/project/context.py +17 -0
- pz_core/assembly/project/discovery.py +227 -0
- pz_core/assembly/resources/__init__.py +19 -0
- pz_core/assembly/resources/compiler.py +310 -0
- pz_core/assembly/resources/settings.py +129 -0
- pz_core/assembly/routes/__init__.py +7 -0
- pz_core/assembly/routes/compiler.py +400 -0
- pz_core/assembly/runtime_manifests/__init__.py +13 -0
- pz_core/assembly/runtime_manifests/compiler.py +159 -0
- pz_core/assembly/settings/__init__.py +17 -0
- pz_core/assembly/settings/compiler.py +786 -0
- pz_core/assembly/settings/host_project_overrides.py +82 -0
- pz_core/assembly/settings/host_runtime_endpoint.py +207 -0
- pz_core/authoring/__init__.py +79 -0
- pz_core/authoring/app_init_service.py +946 -0
- pz_core/authoring/app_versions.py +814 -0
- pz_core/authoring/cli.py +192 -0
- pz_core/authoring/commands.py +1810 -0
- pz_core/authoring/conflicts.py +180 -0
- pz_core/authoring/dev_bindings.py +916 -0
- pz_core/authoring/dev_git.py +1087 -0
- pz_core/authoring/dev_import.py +1061 -0
- pz_core/authoring/dev_manifest.py +522 -0
- pz_core/authoring/dev_sources.py +1638 -0
- pz_core/authoring/env_cli.py +454 -0
- pz_core/authoring/files.py +109 -0
- pz_core/authoring/ide_projection.py +1129 -0
- pz_core/authoring/naming.py +58 -0
- pz_core/authoring/official_dev_sources.py +432 -0
- pz_core/authoring/official_sources.json +156 -0
- pz_core/authoring/orchestration_service.py +558 -0
- pz_core/authoring/project_default.jsonc +53 -0
- pz_core/authoring/project_defaults.py +88 -0
- pz_core/authoring/project_metadata_service.py +199 -0
- pz_core/authoring/project_settings_checks.py +51 -0
- pz_core/authoring/project_settings_cli.py +299 -0
- pz_core/authoring/project_settings_service.py +1700 -0
- pz_core/authoring/project_state.py +399 -0
- pz_core/authoring/publish_git.py +423 -0
- pz_core/authoring/publish_lock.py +188 -0
- pz_core/authoring/publish_source.py +195 -0
- pz_core/authoring/release.py +1382 -0
- pz_core/authoring/repo_keys.py +29 -0
- pz_core/authoring/root_cli.py +28 -0
- pz_core/authoring/scaffold_service.py +325 -0
- pz_core/authoring/sync_service.py +787 -0
- pz_core/authoring/templates/app/__init__.py +1 -0
- pz_core/authoring/templates/app/api.py +25 -0
- pz_core/authoring/templates/app/apps.py +31 -0
- pz_core/authoring/templates/app/auth_package_init.py +14 -0
- pz_core/authoring/templates/app/auth_readme.py +29 -0
- pz_core/authoring/templates/app/configs_package_init.py +14 -0
- pz_core/authoring/templates/app/default_env.py +18 -0
- pz_core/authoring/templates/app/default_jsonc.py +25 -0
- pz_core/authoring/templates/app/locales_readme.py +25 -0
- pz_core/authoring/templates/app/migrations_readme.py +19 -0
- pz_core/authoring/templates/app/models.py +14 -0
- pz_core/authoring/templates/app/package_init.py +8 -0
- pz_core/authoring/templates/app/pages_base.py +161 -0
- pz_core/authoring/templates/app/pages_readme.py +25 -0
- pz_core/authoring/templates/app/public.py +31 -0
- pz_core/authoring/templates/app/pyproject.py +77 -0
- pz_core/authoring/templates/app/readme.py +74 -0
- pz_core/authoring/templates/app/settings.py +46 -0
- pz_core/authoring/templates/app/static_app_js.py +24 -0
- pz_core/authoring/templates/app/static_readme.py +31 -0
- pz_core/authoring/templates/app/template_config_page.py +23 -0
- pz_core/authoring/templates/app/template_index_html.py +28 -0
- pz_core/authoring/templates/app/templates_readme.py +32 -0
- pz_core/authoring/templates/app/urls.py +36 -0
- pz_core/authoring/templates/app/views.py +36 -0
- pz_core/authoring/templates/project/__init__.py +1 -0
- pz_core/authoring/templates/project/agents.py +51 -0
- pz_core/authoring/templates/project/apps_container_readme.py +37 -0
- pz_core/authoring/templates/project/main_py.py +59 -0
- pz_core/authoring/templates/project/project_env.py +19 -0
- pz_core/authoring/templates/project/pyproject.py +26 -0
- pz_core/authoring/templates/project/pz_env_env.py +65 -0
- pz_core/authoring/templates/project/pz_env_jsonc.py +69 -0
- pz_core/authoring/templates/project/pz_jsonc.py +12 -0
- pz_core/authoring/templates/project/readme.py +94 -0
- pz_core/authoring/templates/project/root_gitkeep.py +8 -0
- pz_core/authoring/templates/shared/__init__.py +5 -0
- pz_core/authoring/templates/shared/text.py +10 -0
- pz_core/authoring/venv_service.py +87 -0
- pz_core/authoring/workspace.py +167 -0
- pz_core/cli.py +291 -0
- pz_core/cli_options.py +74 -0
- pz_core/core/__init__.py +3 -0
- pz_core/core/errors/__init__.py +5 -0
- pz_core/core/errors/models.py +117 -0
- pz_core/core/events/__init__.py +5 -0
- pz_core/core/events/registry.py +54 -0
- pz_core/core/extensions/__init__.py +51 -0
- pz_core/core/extensions/registry.py +790 -0
- pz_core/core/health/__init__.py +6 -0
- pz_core/core/health/contracts.py +10 -0
- pz_core/core/middlewares/__init__.py +6 -0
- pz_core/core/middlewares/models.py +24 -0
- pz_core/core/routing/__init__.py +5 -0
- pz_core/core/routing/models.py +21 -0
- pz_core/core/settings_contracts.py +25 -0
- pz_core/docs/README.md +20 -0
- pz_core/docs/distribution-README.md +438 -0
- pz_core/docs/pz/345/221/275/344/273/244/346/200/273/350/247/210.md +1011 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/00-pz/346/236/266/346/236/204/346/200/273/347/272/262.md +240 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/01-/347/233/256/345/275/225/344/270/216/345/210/206/345/261/202/350/247/204/350/214/203.md +216 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/02-/345/214/205/344/270/216/345/210/206/345/217/221/350/247/204/350/214/203.md +403 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/03-App/346/234/200/345/260/217/345/255/220/345/214/205/350/247/204/350/214/203.md +571 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/04-Settings/344/270/216/351/205/215/347/275/256/350/247/204/350/214/203.md +1177 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/05-/350/265/204/346/272/220/344/270/216/350/277/220/350/241/214/347/233/256/345/275/225/350/247/204/350/214/203.md +575 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/06-/346/225/260/346/215/256/345/272/223/344/270/216/345/217/257/351/200/211/345/237/272/347/241/200/350/256/276/346/226/275/350/247/204/350/214/203.md +499 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/07-App/346/233/277/346/215/242/350/247/204/350/214/203.md +206 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/08-/345/271/263/345/217/260/347/272/247App/344/270/216/350/277/220/350/241/214/346/227/266/345/215/225/344/276/213/350/247/204/350/214/203.md +445 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/09-CLI/344/270/216Worker/345/205/245/345/217/243/350/247/204/350/214/203.md +950 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/10-/346/265/213/350/257/225/344/270/216/345/245/221/347/272/246/350/247/204/350/214/203.md +1001 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/11-/344/273/223/345/272/223/347/234/237/347/233/270/346/272/220/345/234/260/345/233/276.md +102 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/12-/346/236/204/345/273/272/344/270/216/345/217/221/345/270/203/346/243/200/346/237/245/346/270/205/345/215/225.md +171 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/13-/346/255/243/345/274/217Facade/346/200/273/350/241/250.md +402 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/14-/345/210/206/345/261/202/344/270/216/346/211/251/345/261/225/350/247/204/350/214/203.md +550 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/15-/345/255/220/345/214/205/345/274/200/345/217/221/344/275/277/347/224/250/346/211/213/345/206/214.md +1004 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/16-/346/211/251/345/261/225/347/202/271/346/255/243/345/274/217/345/215/217/350/256/256/346/270/205/345/215/225.md +352 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/17-pz.protocols.v1/346/255/243/345/274/217/345/215/217/350/256/256.md +266 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/18-/346/211/251/345/261/225/347/202/271/345/206/262/347/252/201/350/243/201/345/206/263/346/200/273/350/241/250.md +131 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/19-/351/224/231/350/257/257/347/240/201/344/270/216/350/257/212/346/226/255/345/215/217/350/256/256.md +176 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/20-/347/247/201/346/234/211/345/214/205/344/273/223/344/270/216/347/273/237/344/270/200/350/256/244/350/257/201/351/203/250/347/275/262/350/247/204/350/214/203.md +243 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/21-contrib/347/272/246/345/256/232/344/270/216Provider/346/216/245/345/205/245/350/247/204/350/214/203.md +365 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/22-/345/244/232/345/233/275/350/257/255/350/250/200/350/265/204/346/272/220/346/262/273/347/220/206/344/270/216/346/214/211/351/234/200/345/212/240/350/275/275/350/247/204/350/214/203.md +912 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/23-/351/246/226/345/261/217/345/261/225/347/244/272/344/270/216/351/246/226/345/270/247/347/250/263/345/256/232/350/247/204/350/214/203.md +269 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/24-pz/345/220/257/345/212/250/344/270/216/350/277/220/350/241/214/346/227/266/347/274/226/350/257/221/346/265/201/347/250/213.md +245 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/25-/351/235/242/345/220/221/345/257/271/350/261/241/345/274/200/345/217/221/350/247/204/350/214/203.md +300 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/README.md +98 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/00-legacy_core/346/240/207/345/207/206pip/345/214/205/345/214/226/351/207/215/346/236/204/346/200/273/347/272/262.md +1073 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/01-/347/233/256/345/275/225/344/270/216/345/210/206/345/261/202/351/207/215/346/236/204/346/211/247/350/241/214/346/226/271/346/241/210.md +224 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/02-/345/214/205/344/270/216/345/210/206/345/217/221/350/247/204/350/214/203-/345/220/253/345/216/206/345/217/262/345/217/243/345/276/204.md +671 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/13-/344/270/200/346/254/241/346/200/247/345/256/236/346/226/275/346/270/205/345/215/225.md +387 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/14-/345/255/220/345/214/205/351/200/202/351/205/215/346/224/271/351/200/240/346/270/205/345/215/225.md +592 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/15-infra/350/243/201/345/211/252/344/270/216/350/220/275/344/275/215/346/270/205/345/215/225.md +81 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/19-core/345/215/225/344/270/200/346/255/243/345/274/217/346/226/271/346/241/210/346/224/266/346/225/233/346/211/247/350/241/214/346/270/205/345/215/225.md +161 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/25-/345/215/217/350/256/256/345/210/260/346/272/220/347/240/201/345/267/256/350/267/235/346/270/205/345/215/225.md +377 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/26-cutover/345/256/214/345/267/245/346/243/200/346/237/245/346/270/205/345/215/225.md +117 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/29-/345/244/232/345/233/275/350/257/255/350/250/200/350/265/204/346/272/220/346/262/273/347/220/206/344/270/216/346/214/211/351/234/200/345/212/240/350/275/275/350/247/204/350/214/203-/345/220/253/350/277/201/347/247/273/347/255/226/347/225/245.md +956 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/99-/350/265/204/346/272/220/346/224/266/345/217/243/345/220/216/347/273/255TODO.md +84 -0
- pz_core/docs//345/217/221/347/211/210/345/211/215/346/270/205/345/215/225-4.0.11.md +84 -0
- pz_core/docs//346/226/260/346/211/213/346/214/207/345/215/227.md +422 -0
- pz_core/infra/__init__.py +17 -0
- pz_core/infra/db/__init__.py +23 -0
- pz_core/infra/db/contracts.py +73 -0
- pz_core/infra/health/__init__.py +5 -0
- pz_core/infra/health/contracts.py +8 -0
- pz_core/infra/kv/__init__.py +5 -0
- pz_core/infra/kv/contracts.py +17 -0
- pz_core/infra/observability/__init__.py +23 -0
- pz_core/infra/observability/contracts.py +142 -0
- pz_core/infra/resilience/__init__.py +5 -0
- pz_core/infra/resilience/contracts.py +17 -0
- pz_core/infra/resources/__init__.py +5 -0
- pz_core/infra/resources/contracts.py +16 -0
- pz_core/infra/settings/__init__.py +17 -0
- pz_core/infra/settings/models.py +81 -0
- pz_core/infra/storage/__init__.py +5 -0
- pz_core/infra/storage/ports.py +27 -0
- pz_core/project_root_commands.py +282 -0
- pz_core/project_runtime_cli.py +344 -0
- pz_core/project_sources.py +253 -0
- pz_core/pz_launcher.py +209 -0
- pz_core/root_cli.py +70 -0
- pz_core/runtime/__init__.py +229 -0
- pz_core/runtime/access.py +86 -0
- pz_core/runtime/adapters/__init__.py +5 -0
- pz_core/runtime/adapters/base.py +18 -0
- pz_core/runtime/asyncio_bridge.py +18 -0
- pz_core/runtime/bindings/__init__.py +5 -0
- pz_core/runtime/bindings/container.py +50 -0
- pz_core/runtime/cli/__init__.py +5 -0
- pz_core/runtime/cli/_args.py +67 -0
- pz_core/runtime/cli/app_commands.py +417 -0
- pz_core/runtime/cli/check.py +214 -0
- pz_core/runtime/cli/db.py +297 -0
- pz_core/runtime/cli/main.py +89 -0
- pz_core/runtime/cli/resources.py +280 -0
- pz_core/runtime/discovery.py +23 -0
- pz_core/runtime/environment_integrity.py +157 -0
- pz_core/runtime/errors.py +5 -0
- pz_core/runtime/events.py +87 -0
- pz_core/runtime/facades/__init__.py +38 -0
- pz_core/runtime/facades/_capabilities.py +46 -0
- pz_core/runtime/facades/cache.py +103 -0
- pz_core/runtime/facades/database.py +71 -0
- pz_core/runtime/facades/events.py +380 -0
- pz_core/runtime/facades/health.py +101 -0
- pz_core/runtime/facades/mail.py +121 -0
- pz_core/runtime/facades/observability.py +102 -0
- pz_core/runtime/facades/providers.py +75 -0
- pz_core/runtime/facades/resources.py +52 -0
- pz_core/runtime/facades/storage.py +296 -0
- pz_core/runtime/health/__init__.py +5 -0
- pz_core/runtime/health/service.py +102 -0
- pz_core/runtime/host.py +188 -0
- pz_core/runtime/host_runner.py +282 -0
- pz_core/runtime/http/__init__.py +20 -0
- pz_core/runtime/http/app.py +576 -0
- pz_core/runtime/http_timing.py +173 -0
- pz_core/runtime/invocation.py +29 -0
- pz_core/runtime/json_safe.py +47 -0
- pz_core/runtime/lifecycle/__init__.py +25 -0
- pz_core/runtime/lifecycle/manager.py +533 -0
- pz_core/runtime/plan.py +214 -0
- pz_core/runtime/plan_context.py +33 -0
- pz_core/runtime/plan_snapshot.py +177 -0
- pz_core/runtime/registries/__init__.py +27 -0
- pz_core/runtime/registries/capabilities.py +101 -0
- pz_core/runtime/registries/providers.py +396 -0
- pz_core/runtime/resource_assets.py +344 -0
- pz_core/runtime/run_cli.py +211 -0
- pz_core/runtime/settings.py +888 -0
- pz_core/runtime/worker/__init__.py +21 -0
- pz_core/runtime/worker/engine.py +921 -0
- pz_core/runtime/worker/runtime.py +25 -0
- pz_core/testing/__init__.py +15 -0
- pz_core/testing/doc_route_contracts.py +305 -0
- pz_core/utils/__init__.py +44 -0
- pz_core/utils/env.py +65 -0
- pz_core/utils/project_jsonc/__init__.py +29 -0
- pz_core/utils/project_jsonc/document.py +420 -0
- pz_core/utils/project_settings.py +99 -0
- pz_foundation/__init__.py +50 -0
- pz_foundation/_contracts.py +81 -0
- pz_foundation/_errors.py +34 -0
- pz_foundation/api.py +14 -0
- pz_foundation/apps.py +127 -0
- pz_foundation/auth/__init__.py +1 -0
- pz_foundation/cache_control.py +233 -0
- pz_foundation/cli/__init__.py +2 -0
- pz_foundation/cli/workers.py +565 -0
- pz_foundation/configs/__init__.py +0 -0
- pz_foundation/configs/default.env +15 -0
- pz_foundation/configs/default.jsonc +452 -0
- pz_foundation/db/__init__.py +94 -0
- pz_foundation/db/aerich_executor.py +664 -0
- pz_foundation/db/config.py +358 -0
- pz_foundation/db/connection_manager.py +689 -0
- pz_foundation/db/connection_probe.py +204 -0
- pz_foundation/db/database.py +1291 -0
- pz_foundation/db/deps.py +47 -0
- pz_foundation/db/lifecycle_status.py +85 -0
- pz_foundation/db/migrations.py +477 -0
- pz_foundation/db/mysql.py +57 -0
- pz_foundation/db/postgresql.py +409 -0
- pz_foundation/db/production_pool_fallback.py +143 -0
- pz_foundation/db/public.py +45 -0
- pz_foundation/db/session.py +369 -0
- pz_foundation/db/sqlite.py +15 -0
- pz_foundation/db/tortoise_manager.py +724 -0
- pz_foundation/db/tortoise_psycopg.py +148 -0
- pz_foundation/docs/README.md +18 -0
- pz_foundation/docs/distribution-README.md +319 -0
- pz_foundation/docs/infra/350/243/201/345/211/252/344/270/216/350/220/275/344/275/215/350/257/264/346/230/216.md +39 -0
- pz_foundation/docs//345/205/254/345/274/200/345/205/215/350/264/271/346/216/245/345/217/243/351/230/262/346/273/245/347/224/250/344/270/216/344/272/272/346/234/272/346/240/241/351/252/214.md +680 -0
- pz_foundation/docs//350/256/276/350/256/241/346/226/207/346/241/243.md +288 -0
- pz_foundation/docs//350/267/257/347/224/261/344/270/216URL/345/244/204/347/220/206/350/247/204/350/214/203.md +119 -0
- pz_foundation/health.py +105 -0
- pz_foundation/host/__init__.py +11 -0
- pz_foundation/host/builder.py +34 -0
- pz_foundation/host/cache_runtime.py +44 -0
- pz_foundation/host/db/__init__.py +17 -0
- pz_foundation/host/db/aerich_config.py +36 -0
- pz_foundation/host/db/config.py +106 -0
- pz_foundation/host/db/connection_manager.py +48 -0
- pz_foundation/host/db/deps.py +5 -0
- pz_foundation/host/db/orm_retry_patch.py +566 -0
- pz_foundation/host/db/request_adapter.py +325 -0
- pz_foundation/host/db/router.py +28 -0
- pz_foundation/host/db/session.py +284 -0
- pz_foundation/host/db/tortoise_manager.py +10 -0
- pz_foundation/host/idempotency.py +132 -0
- pz_foundation/host/lifespan.py +247 -0
- pz_foundation/host/redis_runtime.py +100 -0
- pz_foundation/host/web.py +790 -0
- pz_foundation/infra_provider.py +236 -0
- pz_foundation/kv/__init__.py +25 -0
- pz_foundation/kv/cache.py +1166 -0
- pz_foundation/kv/deps.py +84 -0
- pz_foundation/kv/public.py +25 -0
- pz_foundation/kv/redis_manager.py +1531 -0
- pz_foundation/lazy_exports.py +43 -0
- pz_foundation/lazy_imports.py +124 -0
- pz_foundation/lifecycle.py +392 -0
- pz_foundation/managed_workers.py +1117 -0
- pz_foundation/middlewares/__init__.py +26 -0
- pz_foundation/middlewares/_settings.py +250 -0
- pz_foundation/middlewares/analytics.py +124 -0
- pz_foundation/middlewares/cache_control.py +156 -0
- pz_foundation/middlewares/compression.py +202 -0
- pz_foundation/middlewares/cors.py +62 -0
- pz_foundation/middlewares/geo_country.py +44 -0
- pz_foundation/middlewares/inner_router.py +50 -0
- pz_foundation/middlewares/rate_limited/__init__.py +3 -0
- pz_foundation/middlewares/rate_limited/key_limiter.py +115 -0
- pz_foundation/middlewares/request_logging.py +104 -0
- pz_foundation/middlewares/route_owner.py +309 -0
- pz_foundation/middlewares/security_csrf.py +44 -0
- pz_foundation/middlewares/security_headers/__init__.py +4 -0
- pz_foundation/middlewares/security_headers/conflicts.py +44 -0
- pz_foundation/middlewares/security_headers/csp_nonce.py +200 -0
- pz_foundation/middlewares/security_headers/security_headers.py +44 -0
- pz_foundation/migrations/__init__.py +1 -0
- pz_foundation/migrations/pz_foundation/__init__.py +1 -0
- pz_foundation/models.py +22 -0
- pz_foundation/notifications/__init__.py +23 -0
- pz_foundation/notifications/email.py +542 -0
- pz_foundation/notifications/public.py +31 -0
- pz_foundation/observability/__init__.py +71 -0
- pz_foundation/observability/http.py +34 -0
- pz_foundation/observability/logger/__init__.py +49 -0
- pz_foundation/observability/logger/_bot_settings.py +126 -0
- pz_foundation/observability/logger/bot_message.py +592 -0
- pz_foundation/observability/logger/core.py +385 -0
- pz_foundation/observability/logger/delivery_audit.py +84 -0
- pz_foundation/observability/logger/feishu_bot.py +199 -0
- pz_foundation/observability/logger/telegram_bot.py +194 -0
- pz_foundation/observability/metrics.py +365 -0
- pz_foundation/observability/runtime.py +68 -0
- pz_foundation/public.py +405 -0
- pz_foundation/resources/__init__.py +12 -0
- pz_foundation/resources/builder.py +900 -0
- pz_foundation/resources/images.py +363 -0
- pz_foundation/root_cli.py +20 -0
- pz_foundation/routing/__init__.py +203 -0
- pz_foundation/routing/canonical.py +486 -0
- pz_foundation/routing/convertors.py +82 -0
- pz_foundation/routing/declarations.py +843 -0
- pz_foundation/routing/exports.py +148 -0
- pz_foundation/routing/loaders.py +138 -0
- pz_foundation/routing/manifest.py +215 -0
- pz_foundation/routing/manifest_provider.py +47 -0
- pz_foundation/routing/mounts.py +101 -0
- pz_foundation/routing/ordering.py +172 -0
- pz_foundation/routing/registry.py +133 -0
- pz_foundation/routing/utils.py +28 -0
- pz_foundation/runtime.py +234 -0
- pz_foundation/settings.py +253 -0
- pz_foundation/settings_normalization.py +130 -0
- pz_foundation/storage/__init__.py +25 -0
- pz_foundation/storage/local.py +209 -0
- pz_foundation/storage/public.py +106 -0
- pz_foundation/storage/s3.py +116 -0
- pz_foundation/storage/service.py +234 -0
- pz_foundation/urls.py +19 -0
- pz_foundation/utils/__init__.py +6 -0
- pz_foundation/utils/merge.py +160 -0
- pz_foundation/worker_replicas.py +55 -0
- pz_settings_center/__init__.py +5 -0
- pz_settings_center/api/__init__.py +5 -0
- pz_settings_center/api/runtime_overrides.py +360 -0
- pz_settings_center/apps.py +28 -0
- pz_settings_center/configs/__init__.py +0 -0
- pz_settings_center/configs/default.env +2 -0
- pz_settings_center/configs/default.jsonc +1 -0
- pz_settings_center/docs/01-settings_center/350/203/275/345/212/233/350/276/271/347/225/214.md +124 -0
- pz_settings_center/docs/README.md +12 -0
- pz_settings_center/docs/distribution-README.md +150 -0
- pz_settings_center/docs//350/256/276/350/256/241/346/226/207/346/241/243.md +48 -0
- pz_settings_center/integration_payload.py +18 -0
- pz_settings_center/project_helpers.py +405 -0
- pz_settings_center/public.py +139 -0
- pz_settings_center/routing_helpers.py +73 -0
- pz_settings_center/runtime_override_provider.py +61 -0
- pz_settings_center/typed_settings.py +324 -0
- pz_settings_center/urls.py +9 -0
|
@@ -0,0 +1,1004 @@
|
|
|
1
|
+
# 子包开发使用手册
|
|
2
|
+
|
|
3
|
+
> settings 正式口径:Python 运行时代码只允许 `import pz_settings_center.public as settings_center`,并通过 `settings_center.settings.<app_id>.<field>` 读取;`settings.py` / `settings/` 只负责声明,不再作为运行时读取入口。
|
|
4
|
+
> namespace 正式口径:最小子包是唯一正式运行时单位;标准 `apps.py` 是运行时发现入口真相源。每个最小子包默认统一采用 `app_id = <app_id>`、目录名 = `<app_id>`、import 包名 = `<app_id>`、distribution = `<distribution-name>`。`pz` 与 `pz_apps` 只是并列的源码组织根,不进入运行时 namespace。
|
|
5
|
+
> 命名口径:新建子包默认让目录名、import 包名与 `app_id` 对齐;不强制 `pz`、`pz` 或其它特定技术前缀。历史子包已发布的 `app_id` 与 import 包名以稳定值为准,不因文档命名口径变化自动迁移。
|
|
6
|
+
本文档是高频使用手册。
|
|
7
|
+
|
|
8
|
+
本文按正式架构口径描述:
|
|
9
|
+
|
|
10
|
+
- `pz` 是唯一正式基础包,也是唯一可以独立启动的包
|
|
11
|
+
- 其它最小子包统一作为 `pz` 的插件按 `installed_apps` 自动加载
|
|
12
|
+
|
|
13
|
+
目标不是重复所有设计文档,而是给日常开发一个最短可执行版本:
|
|
14
|
+
|
|
15
|
+
- `pz` 和子包怎么分层
|
|
16
|
+
- 子包应该怎么写
|
|
17
|
+
- 新增功能时先改哪里
|
|
18
|
+
- 改完以后怎么验证
|
|
19
|
+
|
|
20
|
+
日常开发还必须先按 [`25-面向对象开发规范`](./25-面向对象开发规范.md) 判断对象边界:这个能力是 contract object、compiler object、runtime owner、facade、provider、adapter、service,还是纯函数。判断不清楚时,不应先新增 helper、全局状态或临时 import。
|
|
21
|
+
|
|
22
|
+
## 1. 四层边界
|
|
23
|
+
|
|
24
|
+
### `pz`
|
|
25
|
+
|
|
26
|
+
只放稳定合同:
|
|
27
|
+
|
|
28
|
+
- app registry / runtime plan / lifecycle
|
|
29
|
+
- 统一 settings 编译与 `settings_center.settings.<app_id>` 读取协议
|
|
30
|
+
- host builder 发现协议
|
|
31
|
+
- 资源加载协议
|
|
32
|
+
- CLI 分发协议
|
|
33
|
+
- 唯一启动壳
|
|
34
|
+
|
|
35
|
+
不放:
|
|
36
|
+
|
|
37
|
+
- 官方 host 具体实现
|
|
38
|
+
- 业务逻辑
|
|
39
|
+
- 单个项目的快捷路径
|
|
40
|
+
|
|
41
|
+
### `pz_settings_center`
|
|
42
|
+
|
|
43
|
+
只放应用层 settings surface:
|
|
44
|
+
|
|
45
|
+
- `settings_center.settings`
|
|
46
|
+
- project/app settings 正式读取入口
|
|
47
|
+
- app settings provider 正式注册入口
|
|
48
|
+
- 物理源码固定落在 `pz/pz_contrib/pz_settings_center/`
|
|
49
|
+
|
|
50
|
+
不放:
|
|
51
|
+
|
|
52
|
+
- 业务逻辑
|
|
53
|
+
- 第二套配置读取链
|
|
54
|
+
- 宿主级快捷 wrapper
|
|
55
|
+
|
|
56
|
+
### `pz_contrib`
|
|
57
|
+
|
|
58
|
+
只放随 `pz-cli` 默认安装的最小基础能力:
|
|
59
|
+
|
|
60
|
+
- `pz_foundation`
|
|
61
|
+
- `pz_settings_center`
|
|
62
|
+
- 根 `pz-cli` 单 wheel 构建与依赖元数据
|
|
63
|
+
|
|
64
|
+
可以直接依赖:
|
|
65
|
+
|
|
66
|
+
- `pz-cli`(提供 `pz_core`、`pz_foundation` 与 `pz_settings_center` Python 包)
|
|
67
|
+
- 其他基础子包的正式公开面
|
|
68
|
+
|
|
69
|
+
### 其它最小子包
|
|
70
|
+
|
|
71
|
+
只作为插件声明自己的能力:
|
|
72
|
+
|
|
73
|
+
- `apps.py`
|
|
74
|
+
- `urls.py` 或 `urls/`
|
|
75
|
+
- `settings.py` 或 `settings/`
|
|
76
|
+
- `public.py`
|
|
77
|
+
- `extensions()`
|
|
78
|
+
- `startup()/shutdown()`
|
|
79
|
+
|
|
80
|
+
不自己造 host,不自己补第二套框架,不独立启动。
|
|
81
|
+
|
|
82
|
+
如果该子包含页面开发:
|
|
83
|
+
|
|
84
|
+
- 目录组织默认遵循 MVC 心智
|
|
85
|
+
- `urls.py` 或 `urls/` 只放 URL 和入口函数
|
|
86
|
+
- 前端只负责展示层,数据交互优先走 AJAX + 后端 API
|
|
87
|
+
|
|
88
|
+
### 宿主项目
|
|
89
|
+
|
|
90
|
+
只保留:
|
|
91
|
+
|
|
92
|
+
- 单一 `main.py`
|
|
93
|
+
- `pz.jsonc`
|
|
94
|
+
- `pz.{env}.jsonc`
|
|
95
|
+
- 项目配置
|
|
96
|
+
- 宿主特有少量扩展
|
|
97
|
+
|
|
98
|
+
不允许自己重新组装通用 Web host。
|
|
99
|
+
|
|
100
|
+
## 2. app 最小结构
|
|
101
|
+
|
|
102
|
+
推荐最小结构:
|
|
103
|
+
|
|
104
|
+
- `<app_id>/apps.py`
|
|
105
|
+
- `<app_id>/configs/default.jsonc`
|
|
106
|
+
- `<app_id>/configs/default.env`
|
|
107
|
+
- `<app_id>/configs/__init__.py`
|
|
108
|
+
- `<app_id>/urls.py` 或 `<app_id>/urls/`
|
|
109
|
+
- `<app_id>/settings.py` 或 `<app_id>/settings/__init__.py`
|
|
110
|
+
- `<app_id>/public.py`
|
|
111
|
+
- `<app_id>/models.py`
|
|
112
|
+
- `<app_id>/templates/`
|
|
113
|
+
- `<app_id>/static/`
|
|
114
|
+
- `<app_id>/locales/`
|
|
115
|
+
- `<app_id>/pages/`
|
|
116
|
+
- `<app_id>/migrations/<app_id>/`
|
|
117
|
+
|
|
118
|
+
说明:
|
|
119
|
+
|
|
120
|
+
- 最小子包目录名默认使用 `<app_id>`
|
|
121
|
+
- `app_id` 默认等于目录名,也就是 `<app_id>`
|
|
122
|
+
- distribution 固定为 `<distribution-name>`
|
|
123
|
+
- 运行时发现只认标准 `apps.py`:文件内必须有 app class、class 上用静态字符串声明 `id`、模块级 `app = <AppClass>()`,并能解析出合法 `app.id`
|
|
124
|
+
- 非 app 模块不得把普通 CLI、工具、路由聚合或 helper 文件命名为 `apps.py`
|
|
125
|
+
- 顶层源码组织根与其下的容器目录若存在,只是 Git 分组容器,不参与 import 与 `app_id`
|
|
126
|
+
- `installed_apps` 的正式语法固定为目录名;目录名可以是最小子包,也可以是容器目录,不接受路径或 `<pkg>.apps:app`
|
|
127
|
+
- 默认项目配置会把 `apps` 声明为 `installed_apps` 扫描目录;运行时不硬编码 `apps`,而是按当前配置展开,配置声明 `apps` 时只发现 `apps/` 下业务仓真实目录与经过 manifest 校验的可见 binding,物理 `apps/.sources/` checkout 不直接贡献 app
|
|
128
|
+
- 同一 `app_id` 同时存在于项目源码扫描范围和当前解释器已安装 distribution 时,项目源码优先;同一 `app_id` 在项目源码范围内出现多份时必须人工收敛
|
|
129
|
+
- 如果 `installed_apps` 同时出现容器目录和显式叶子 app,作者面工具应在写回时自动压平成单一叶子 app 顺序,不保留 `"apps" + 显式 app` 这类双轨真相
|
|
130
|
+
- 只要包已安装且进入 `installed_apps`,`pz` 就按约定自动发现、编译、组装和加载
|
|
131
|
+
- 分发包 `pyproject.toml` 必须把 `configs` 声明为正式子包,并把 `configs/default.jsonc` 与 `configs/default.env` 打入 package-data;常规写法至少包含 `<app_id>.configs`、`*.jsonc`、`**/*.jsonc`、`*.env`、`**/*.env`
|
|
132
|
+
- 如果 app 提供页面声明,统一放在 `pages/`;分发包必须把该目录下的 `*.jsonc` 打入 package-data
|
|
133
|
+
- 宿主业务子包可以维护自己的迁移演进;`apps/.sources` 中的官方/共享包只保留 `0_*` 数据库 init 文件,不新增 `1_*` 及后续迁移文件
|
|
134
|
+
|
|
135
|
+
### 2.1 pages 约定
|
|
136
|
+
|
|
137
|
+
`pages/` 是页面声明目录,用来承载页面中心的正式页面输入,同时避免把页面列表、页面路由与页面 payload 塞进 settings。
|
|
138
|
+
|
|
139
|
+
目录边界固定如下:
|
|
140
|
+
|
|
141
|
+
- `configs/`:只放 app settings 默认值、secret 模板与配置 schema 配套输入
|
|
142
|
+
- `pages/`:只放页面中心可编译的页面声明
|
|
143
|
+
- `apps.py`:只声明 app 能力、provider、lifecycle、worker、extension
|
|
144
|
+
- `public.py`:只暴露稳定公开 facade
|
|
145
|
+
- provider 模块:只放实现逻辑
|
|
146
|
+
|
|
147
|
+
页面文件组织推荐如下:
|
|
148
|
+
|
|
149
|
+
- `pages/base.jsonc`
|
|
150
|
+
- `pages/<namespace>/base.jsonc`
|
|
151
|
+
- `pages/<namespace>/<page>.jsonc`
|
|
152
|
+
|
|
153
|
+
命名空间规则固定如下:
|
|
154
|
+
|
|
155
|
+
- 页面声明拥有独立的页面配置命名空间
|
|
156
|
+
- 页面配置命名空间和 settings namespace 一样必须稳定、单一、可审计
|
|
157
|
+
- 页面配置命名空间固定由 `owner_app + 页面相对路径` 确定
|
|
158
|
+
- 不允许再单独声明 `page_namespace`、`module_page_namespace` 或任何同义字段
|
|
159
|
+
- `pages/base.jsonc` 属于当前 owner app 的根页面命名空间,不是无主的全局页面
|
|
160
|
+
|
|
161
|
+
不要再创建独立的页面注册文件。页面声明统一进入 `pages/`,并由 `pz_page_center` 负责 schema、校验、冲突裁决和编译诊断。
|
|
162
|
+
|
|
163
|
+
`pages/` 属于正式资源目录。它和 `templates/`、`static/`、`locales/`、`template_config/`、`migrations/` 一样,统一进入资源发现、合并和编译主链;运行期不再单独扫描源码目录。
|
|
164
|
+
|
|
165
|
+
demo、草稿、说明性样例应放到 owner 明确不扫描的位置。不要在页面声明里新增第二套开关;运行时启停属于 settings owner,源码声明是否参与页面编译属于目录约定。
|
|
166
|
+
|
|
167
|
+
页面声明的具体字段与覆盖语义以 `pz_page_center` 设计文档和页面 schema 为准。作者侧只需记住:
|
|
168
|
+
|
|
169
|
+
- `pages/` 是页面声明真相源
|
|
170
|
+
- 页面配置命名空间由 `owner_app + 页面相对路径` 唯一确定
|
|
171
|
+
- 页面中心消费的是合并后的 `pages/`
|
|
172
|
+
- 页面运行时不重扫源码目录
|
|
173
|
+
|
|
174
|
+
正式链路必须是:
|
|
175
|
+
|
|
176
|
+
```text
|
|
177
|
+
app registry
|
|
178
|
+
-> resource discovery
|
|
179
|
+
-> registrations discovery
|
|
180
|
+
-> owner schema validation
|
|
181
|
+
-> RuntimePlan / owner registry
|
|
182
|
+
-> Web / CLI / worker runtime
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
运行时不得临时到处读取 registration JSONC;否则会绕过“编译先于运行”,形成第二套运行图。
|
|
186
|
+
|
|
187
|
+
### 2.2 对象结构约定
|
|
188
|
+
|
|
189
|
+
最小子包不要求机械拆目录,但复杂度上来后必须按对象职责拆分。
|
|
190
|
+
|
|
191
|
+
推荐边界:
|
|
192
|
+
|
|
193
|
+
- `models.py` / `models/`:领域模型、持久化模型、typed value object。
|
|
194
|
+
- `contracts.py`:本子包对外稳定合同、Protocol、命令对象、结果对象。
|
|
195
|
+
- `facade.py`:对外消费面。
|
|
196
|
+
- `runtime.py`:局部组合根或 runtime owner。
|
|
197
|
+
- `service/`:应用服务,只做业务编排,不拥有框架装配。
|
|
198
|
+
- `repository/`:持久化访问,不承担业务决策。
|
|
199
|
+
- `adapters/`:第三方 SDK、HTTP、DB、CLI、浏览器等外部系统适配。
|
|
200
|
+
- `providers/`:正式 provider 实现。
|
|
201
|
+
- `public.py`:只导出稳定 facade / contract,不导出内部 helper。
|
|
202
|
+
|
|
203
|
+
约束:
|
|
204
|
+
|
|
205
|
+
- 有身份、生命周期、状态、不变量或可替换实现的能力,应使用对象表达。
|
|
206
|
+
- 纯转换、小范围校验、无状态格式化可以保持函数。
|
|
207
|
+
- `Manager` 只能管理多个同类 runtime/resource/lease;不得同时承担 settings、IO、编译、执行、诊断、CLI 输出。
|
|
208
|
+
- 类依赖必须通过构造参数、factory、context、`RuntimePlan` 或正式 registry 注入,不在方法内部临时 import 具体实现。
|
|
209
|
+
- 编译产物和 snapshot 视为不可变对象,执行阶段只消费,不原地改写。
|
|
210
|
+
|
|
211
|
+
## 3. URL 约定
|
|
212
|
+
|
|
213
|
+
URL 真相源固定为 `<pkg>.urls`。
|
|
214
|
+
|
|
215
|
+
目录约定:
|
|
216
|
+
|
|
217
|
+
- 简单 app 使用 `urls.py`
|
|
218
|
+
- 复杂 app 使用 `urls/` 目录
|
|
219
|
+
- 复杂 app 的正式入口固定为 `urls/__init__.py`
|
|
220
|
+
|
|
221
|
+
写法要求:
|
|
222
|
+
|
|
223
|
+
- 让 `urls.py` 或 `urls/` 像 Django 的 `urls.py` 一样可读
|
|
224
|
+
- 只声明本 app 的路径结构、子模块 include 关系与稳定 namespace
|
|
225
|
+
- `urls.py` 或 `urls/` 只放 URL 声明与 `urlpatterns` 导出
|
|
226
|
+
- `urls.py` 不负责构建路由
|
|
227
|
+
- 具体路由构建与绑定统一通过 `pz_foundation.routing` 的 `path(...)` / `include(...)` / `mount(...)` 完成
|
|
228
|
+
- `urlpatterns` 的路径字符串统一使用 FastAPI / Starlette 原生 path 参数语法;既支持 `/{lang}/{module}/{slug}`,也支持 `/{lang:locale}/{slug}` 这类 convertor 形式
|
|
229
|
+
|
|
230
|
+
尾斜杠规则:
|
|
231
|
+
|
|
232
|
+
- 目录型页面 URL 统一以 `/` 结尾
|
|
233
|
+
- 文件型 URL 不以 `/` 结尾
|
|
234
|
+
- `urls.py` 只声明 canonical 版本;不要为了兼容访问同时手工声明两套尾斜杠路径
|
|
235
|
+
- slash 规范化跳转由共享 host / routing 规则统一处理
|
|
236
|
+
- 页面 canonical、hreflang、sitemap、redirect target 必须与正式 URL 规则一致
|
|
237
|
+
|
|
238
|
+
不允许:
|
|
239
|
+
|
|
240
|
+
- 在 `apps.py` 里组装 URL
|
|
241
|
+
- 在 `startup()` / `lifespan` 里补挂正式路由
|
|
242
|
+
- 业务包自己处理 prefix 挂载、全局排序、全局清理
|
|
243
|
+
- 在 `urls.py` 里堆业务编排、复杂查询、页面配置拼装、数据访问细节、手工路由绑定或任何路由构建逻辑
|
|
244
|
+
- 再发明一层难懂的 route DSL
|
|
245
|
+
|
|
246
|
+
统一装配规则:
|
|
247
|
+
|
|
248
|
+
- 官方 host 统一加载每个 app 的 `<pkg>.urls`
|
|
249
|
+
- prefix 由上层统一挂载
|
|
250
|
+
- 所有 app、mount、host routes 都加载完成后
|
|
251
|
+
- 最后统一执行 `clean_and_reorder_routes()`
|
|
252
|
+
|
|
253
|
+
推荐模式:
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
from pz_foundation.routing import include, path
|
|
257
|
+
|
|
258
|
+
from .views import demo_page
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
app_name = "pz_demo"
|
|
262
|
+
|
|
263
|
+
urlpatterns = (
|
|
264
|
+
path("/demo", demo_page, name="pz_demo:demo-page"),
|
|
265
|
+
include("pz_demo.api", path_prefix="/api", namespace="pz_demo.api"),
|
|
266
|
+
)
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### 3.1 页面开发目录心智
|
|
270
|
+
|
|
271
|
+
如果子包涉及页面开发,目录组织应遵循 MVC 心智:
|
|
272
|
+
|
|
273
|
+
- `models.py` 或 `models/`:数据模型与持久化结构
|
|
274
|
+
- `views.py` 或 `views/`:页面入口函数与控制器职责;仅当 `urls.py` 需要拆分时使用
|
|
275
|
+
- 页面入口函数:控制器职责,负责把请求接到模板渲染或 API 调用
|
|
276
|
+
- `templates/`、`static/`:视图展示层
|
|
277
|
+
|
|
278
|
+
这里的 MVC 是作者心智,不是要求框架再发明一套 MVC 基类。重点是职责分离:
|
|
279
|
+
|
|
280
|
+
- `urls.py` 只做 URL 声明与 `urlpatterns` 导出,不负责构建路由
|
|
281
|
+
- `pages/` 保留给页面声明目录,不用作页面入口实现模块名
|
|
282
|
+
- 入口函数负责控制器职责
|
|
283
|
+
- 模板和前端资源只负责展示
|
|
284
|
+
- 业务数据读写与交互协议优先走正式后端 API
|
|
285
|
+
|
|
286
|
+
### 3.2 前端与数据交互约定
|
|
287
|
+
|
|
288
|
+
`pz` 对页面前端的默认约定是:
|
|
289
|
+
|
|
290
|
+
- 前端部分优先只负责展示、交互增强与局部异步刷新
|
|
291
|
+
- 数据交互尽可能通过 AJAX 调用后端 API 完成
|
|
292
|
+
- 后端 API 是数据读写、校验、状态变更与业务规则的正式落点
|
|
293
|
+
|
|
294
|
+
不建议:
|
|
295
|
+
|
|
296
|
+
- 在模板脚本里长期维护复杂业务状态机
|
|
297
|
+
- 让页面模板直接承担第二套数据聚合中心
|
|
298
|
+
- 把核心业务协议埋进零散前端脚本,而不形成正式 API
|
|
299
|
+
|
|
300
|
+
## 4. settings 约定
|
|
301
|
+
|
|
302
|
+
settings 作者面默认模板固定为配对的 `<app_id>/configs/default.jsonc` 与 `<app_id>/configs/default.env`。
|
|
303
|
+
|
|
304
|
+
其中:
|
|
305
|
+
|
|
306
|
+
- `default.jsonc` 展示 app settings 的完整 key 树与默认/占位值
|
|
307
|
+
- `default.env` 只展示 `.env` 层面的 password、secret、token、key 路径键等敏感覆盖项
|
|
308
|
+
|
|
309
|
+
二者可以被 Git 追踪,并且只由 `pz settings init/update` 读取;启动阶段不得直接读取 app 包内的 `configs/default.jsonc` 或 `configs/default.env`。
|
|
310
|
+
|
|
311
|
+
settings 声明模块固定为 `settings.py` 或 `settings/`。
|
|
312
|
+
Python 运行时正式读取入口固定为 `settings_center.settings.<app_id>`。
|
|
313
|
+
|
|
314
|
+
允许两种文件形态:
|
|
315
|
+
|
|
316
|
+
- `settings.py`
|
|
317
|
+
- `settings/__init__.py`
|
|
318
|
+
|
|
319
|
+
无论使用哪种形态,运行时都不再直接 import 该模块读取配置。
|
|
320
|
+
运行时只认:
|
|
321
|
+
|
|
322
|
+
- `settings_center.settings.<app_id>`
|
|
323
|
+
|
|
324
|
+
规则:
|
|
325
|
+
|
|
326
|
+
- `configs/default.jsonc` 负责 settings init/update 的默认值模板
|
|
327
|
+
- `configs/default.env` 负责 settings init/update 的密钥/密码/令牌/Key 占位模板
|
|
328
|
+
- `settings.py` / `settings/` 负责 schema、校验、normalize、`__runtime_overrides__`
|
|
329
|
+
- `AppSettings` 不再承载默认值真相源
|
|
330
|
+
- 子包新增默认配置键后,必须通过 `pz settings update` 显式同步到项目根配置;启动阶段不得回退读取包内模板补值
|
|
331
|
+
- 启动期缺必需配置时,错误应指向缺失配置路径并提示执行 `pz settings update`,而不是隐式读取 `configs/default.jsonc`
|
|
332
|
+
- settings namespace 固定等于 `app.id`
|
|
333
|
+
- 不允许在 `apps.py` 里再声明独立的 `settings_namespace`
|
|
334
|
+
- 新建子包默认让 `app.id` 等于最小子包目录名;历史子包已发布的 `app.id` 以稳定值为准
|
|
335
|
+
- `pages/` 不属于 settings 默认值真相源;settings 可以放协议开关,但不得承载完整页面声明列表
|
|
336
|
+
|
|
337
|
+
不允许:
|
|
338
|
+
|
|
339
|
+
- `apps.py` 再带一套默认配置
|
|
340
|
+
- `urls.py` 或 `urls/` 再带一套默认配置
|
|
341
|
+
- `public.py` 再带一套默认配置
|
|
342
|
+
- `services/*` 直接成为第二套 settings 真相源
|
|
343
|
+
- `settings.py` / `settings/` 在 import 阶段读取包内 `configs/default.jsonc`、包内 `configs/default.env`、项目 `.env*` 或进程环境变量
|
|
344
|
+
- `configs/default.jsonc` 塞入页面列表、菜单列表、资源列表、页面 registry 或 payload blocks
|
|
345
|
+
|
|
346
|
+
补充规则:
|
|
347
|
+
|
|
348
|
+
- 脚手架应直接用最小子包目录名初始化 `app.id`
|
|
349
|
+
- 进入运行时后,正式 namespace 只认 `<app_id>`
|
|
350
|
+
- 没有最小子包身份的内部模块,不允许自己读一套独立配置
|
|
351
|
+
- 这类模块只能:
|
|
352
|
+
- 读取所属最小子包的 settings namespace,也就是 `settings_center.settings.<app_id>`
|
|
353
|
+
- 或读取 `settings_center.settings.project.<field>`
|
|
354
|
+
|
|
355
|
+
补充 auth 路由注册纪律:
|
|
356
|
+
|
|
357
|
+
- 子包若需要声明“哪些 URL 由系统 auth 接管”,默认正式入口是 `configs/default.jsonc` 经 `pz settings init/update` 同步到项目 settings 后,再由 `settings.py -> route_policy` 完成 schema 与 normalize。
|
|
358
|
+
- 只有当 app 的路由裁决超出 `route_policy` 的配置表达力时,才使用 `extensions() -> auth.route_policy_provider`。
|
|
359
|
+
- `route_policy` 的常规字段固定为 `default_mode`、`auth_paths`、`public_paths`、`delegate_paths`;普通子包优先用这组配置表达,不要为常规受保护面再发明第二套私有 guard 语法。
|
|
360
|
+
- 已安装的正式 auth owner 负责消费 `route_policy` 与 `auth.route_policy_provider`,并把已挂载路由编译成请求期唯一 route-policy 真相源。
|
|
361
|
+
- `default_mode = "auth"` 表示“整个子包默认注册为受保护面,再用 `public_paths` / `delegate_paths` 开例外”;`default_mode = "public"` 表示“只注册 `auth_paths` / `delegate_paths` 等显式配置的 URL”。
|
|
362
|
+
- 对需要稳定 delegate owner / public owner 的高级场景,可在 `route_policy` 里额外声明 `auth_owner`、`public_owner`、`delegate_owner`;这仍然属于正式 auth owner 的配置编译输入,不需要再包一层私有 provider。
|
|
363
|
+
- `route_policy` 是否进入线上治理面,由 settings runtime owner 按 `AppSettings.__runtime_overrides__` 与内建治理规则消费,不由业务 app 自建第二套白名单。
|
|
364
|
+
- 推荐每个最小子包都预留 `<app>/auth/` 空包作为高级 auth 注册保留位;默认保持为空,不要在尚未需要高级规则时把它变成第二套认证入口。
|
|
365
|
+
- `auth.route_policy_provider` 只在 app 需要超出配置表达力的自定义规则时使用;不要为普通的默认公开面/受保护面再写第二套私有 middleware。
|
|
366
|
+
- 但这些字段只属于编译输入;请求期唯一真相源仍是 auth owner 编译后的 runtime route policies。
|
|
367
|
+
- 不允许再在业务 app 里保留第二套 `auth_guard` middleware、白名单中间件或独立未登录跳转协议。
|
|
368
|
+
- 请求期认证细节属于正式 auth owner;普通子包不要假设 auth middleware 会为旧 token 或缺 claims token 在每个请求里回库补救。若确实需要最新用户资料,显式使用 auth owner 暴露的 model 依赖或公开面,而不是把默认 identity 解析当 ORM loader。
|
|
369
|
+
|
|
370
|
+
推荐做法:
|
|
371
|
+
|
|
372
|
+
- 初始模板默认生成 `configs/default.jsonc`、`configs/default.env` 与 `settings.py`
|
|
373
|
+
- 只有当配置拆分需求变多时,再升级成 `settings/`
|
|
374
|
+
|
|
375
|
+
项目级配置分层固定如下:
|
|
376
|
+
|
|
377
|
+
- `configs/default.jsonc`:各 app 的 settings init/update 默认值模板,不是启动期读取来源
|
|
378
|
+
- `configs/default.env`:各 app 的 Git 可追踪 `.env` 层密钥占位模板,不是启动期读取来源
|
|
379
|
+
- `pz.jsonc`:项目基础装配与全局静态覆盖
|
|
380
|
+
- `pz.{env}.jsonc`:按环境的项目静态覆盖,例如 `pz.develop.jsonc`、`pz.test.jsonc`、`pz.product.jsonc`
|
|
381
|
+
- `.env`:项目基础 secrets 与初始化提示来源
|
|
382
|
+
- `.env.{env}`:按环境的 secrets 覆盖,例如 `.env.develop`、`.env.test`、`.env.product`
|
|
383
|
+
- 显式环境变量:覆盖 `.env.{env}`
|
|
384
|
+
|
|
385
|
+
推荐加载顺序:
|
|
386
|
+
|
|
387
|
+
1. settings 声明
|
|
388
|
+
2. `pz.jsonc`
|
|
389
|
+
3. `pz.{PZ_ENV}.jsonc`
|
|
390
|
+
4. `.env`
|
|
391
|
+
5. `.env.{PZ_ENV}`
|
|
392
|
+
6. 显式环境变量
|
|
393
|
+
|
|
394
|
+
运行时正式读取顺序:
|
|
395
|
+
|
|
396
|
+
1. `SettingsSnapshot`
|
|
397
|
+
2. app settings provider
|
|
398
|
+
3. settings runtime override provider
|
|
399
|
+
|
|
400
|
+
规则:
|
|
401
|
+
|
|
402
|
+
- 未显式指定 `PZ_ENV` 时,正式默认环境固定为 `develop`
|
|
403
|
+
- `product` 环境必须在启动命令或进程环境中显式指定
|
|
404
|
+
- `.env.{PZ_ENV}` 只在环境已经确定后参与加载,不负责反向决定当前环境
|
|
405
|
+
- `.env + .env.{PZ_ENV} + 显式环境变量` 构成受保护 env layer,静态 TOML 只补充它未给出的值
|
|
406
|
+
- `pz.jsonc` / `pz.{env}.jsonc` / app `configs/default.jsonc` 的字段名统一使用 `lower_snake_case`;其中 app `configs/default.jsonc` 只作为 settings 工具模板来源
|
|
407
|
+
- `.env` / `.env.{env}` 中用于 settings override 的路径键统一使用 `lower_snake_case`,例如 `project__base_url`、`<app_id>__secret_key`、`<app_id>__nested__password`
|
|
408
|
+
- 真正的进程级环境变量保留大写命名,例如 `PZ_ENV`、`PZ_HOST_PROJECT_DIR`、`APP_PORT`
|
|
409
|
+
- `APP_PORT` / `BASE_URL` 属于宿主 `main.py` 与 host settings override 的启动特例,不代表通用 settings env 应改成大写扁平键
|
|
410
|
+
- 项目级 JSONC 负责项目装配与静态覆盖,不负责替代 secrets 输入
|
|
411
|
+
- 项目正式配置文件名固定为 `pz.jsonc`
|
|
412
|
+
- JSONC 的正式读写必须统一通过 `pz/utils/project_jsonc/`
|
|
413
|
+
- 工具注释独占前缀固定为 `// @pz:`
|
|
414
|
+
- `pz settings init` 生成 `pz.jsonc` 时,应通过 `// @pz:managed:start <path>` / `// @pz:managed:end <path>` 维护托管区块
|
|
415
|
+
- 正式托管区块固定为 `installed_apps`、`middlewares`
|
|
416
|
+
- `middlewares` 托管区由工具按当前已发现 app 编译 conventional middleware manifest 生成;`pz settings update` 时若项目已写入显式 middleware 顺序,则应保留该顺序并把新发现 middleware 按 conventional 顺序补入
|
|
417
|
+
- 若项目需要主动回收历史 middleware 顺序漂移,正式入口是 `pz settings update --reset-middlewares-order`
|
|
418
|
+
- grouped init 命令复用的底层 scaffold service 默认同时生成 `pz.jsonc`、`pz.develop.jsonc`、`pz.test.jsonc`、`pz.product.jsonc`、`.env`、`.env.develop`、`.env.test`、`.env.product`
|
|
419
|
+
- `pz settings init` 在这些文件缺失时也必须补齐,但不得覆盖已存在的 env / jsonc 文件
|
|
420
|
+
- `.env` 必须写入默认 env 模板;`.env.develop` / `.env.test` / `.env.product` 只写环境差异覆盖模板
|
|
421
|
+
- 已安装 app 的 `configs/default.env` 必须在 `pz settings init/update` 时合并进项目 `.env*`:`.env` 写入未注释占位符,`.env.{PZ_ENV}` 写入注释占位符;若项目文件已有同名变量,必须保留现有值
|
|
422
|
+
- `pz.develop.jsonc` / `pz.test.jsonc` / `pz.product.jsonc` 必须写入环境级 JSONC 覆盖骨架,承载非敏感静态覆盖;不要把它们留成长期空对象
|
|
423
|
+
- 默认 `.env` 模板应参考当前正式项目常用 env 键,并排除 `PZ_AGENT*`、`SEO_OPS*` 这类非当前正式 settings 真相源配置
|
|
424
|
+
- `project_default.jsonc` 属于 `pz` 的通用作者面默认骨架,不属于 app 私有默认值真相源
|
|
425
|
+
- `project_default.jsonc` 当前没有正式扩展点;不要试图为它增加 app / contrib / 宿主侧补丁入口
|
|
426
|
+
- `configs/default.jsonc` 属于各最小子包自己的 settings 作者面默认模板,是 `pz settings init/update` 的输入,不是启动期 settings 编译输入
|
|
427
|
+
- `configs/default.jsonc` 可以声明受控的顶层路由默认值 `routing.default_language`、`routing.all_supported_languages`、`routing.language_aliases`;`pz settings init/update` 会把它们提升到根 `routing`,并从 `apps.<app_id>` 私有配置里剥离
|
|
428
|
+
- `configs/default.jsonc` 可以声明受控的顶层快捷命令 `commands`;`pz settings init/update` 会把它们提升到根 `commands`,同名命令以 app 默认刷新,未声明的项目自定义命令保留
|
|
429
|
+
- `configs/default.jsonc` 可以声明受控的项目装配状态 `apps.*.sources.source_mode`;`pz settings init/update` 会把该字段提升到根 `apps` 对应 owner,作为 local/git/package 源码策略的单一真相
|
|
430
|
+
- `configs/default.env` 属于各最小子包自己的 secret 模板,是项目 `.env*` 的生成提示来源,不是启动期 settings 编译输入
|
|
431
|
+
- 当前版本不定义正式的初始化骨架扩展协议;不要为业务默认值发明第二套 init 命令、profile 参数或隐式注册器
|
|
432
|
+
- app 私有作者面模板固定为自己的 `configs/default.jsonc` / `configs/default.env`;项目级通用默认骨架只来自 `pz` 内建的 `project_default.jsonc`
|
|
433
|
+
- 未知 `// @pz:*` 工具注释应识别但默认忽略,不得作为当前版本的报错原因
|
|
434
|
+
- 普通 `// ...` 注释只作为说明文本,不参与配置语义;工具写回时应尽量保留,但不承诺字节级原样保留
|
|
435
|
+
|
|
436
|
+
宿主入口快捷环境变量补充规则:
|
|
437
|
+
|
|
438
|
+
- 宿主入口允许读取的大写快捷变量固定为:
|
|
439
|
+
- `APP_HOST`
|
|
440
|
+
- `APP_PORT`
|
|
441
|
+
- `APP_RELOAD`
|
|
442
|
+
- `APP_WORKERS`
|
|
443
|
+
- `UVICORN_LOOP`
|
|
444
|
+
- `UVICORN_HTTP_IMPL`
|
|
445
|
+
- `UVICORN_LOG_LEVEL`
|
|
446
|
+
- `UVICORN_PROXY_HEADERS`
|
|
447
|
+
- `UVICORN_FORWARDED_ALLOW_IPS`
|
|
448
|
+
- `UVICORN_ACCESS_LOG`
|
|
449
|
+
- 其中与 `project` 小写字段语义相同的快捷变量,必须在 settings 编译链中以最高优先级回写到最终 `SettingsSnapshot.project`
|
|
450
|
+
- 当前正式映射固定为:
|
|
451
|
+
- `APP_HOST -> project.app_host`
|
|
452
|
+
- `APP_PORT -> project.app_port`
|
|
453
|
+
- `APP_WORKERS -> project.app_workers`
|
|
454
|
+
- `APP_RELOAD -> project.app_reload`
|
|
455
|
+
- `BASE_URL -> project.base_url`
|
|
456
|
+
- `STATIC_TARGET_PATH -> project.static_target_path`
|
|
457
|
+
- `STATIC_TARGET_OWNER -> project.static_target_owner`
|
|
458
|
+
- 上述映射只描述宿主启动快捷层;项目配置文件和 `.env` 内的正式 settings 键仍然使用 `project__...` / `<app_id>__...`
|
|
459
|
+
- 这意味着 `settings_center.settings.project.app_host` / `app_port` / `app_workers` / `app_reload` 读取到的是环境变量覆盖后的最终值
|
|
460
|
+
- `APP_HOST` 只表示监听地址,不属于 `project.base_url` 的别名
|
|
461
|
+
- `base_url` 表示站点对外访问基址;监听地址与对外基址是两个不同语义,不允许混用
|
|
462
|
+
- 若项目根已存在 `pz.jsonc`,`pz settings init` 默认必须拒绝覆盖;只有显式传入 `--force` 时,才允许强制重置项目 settings 配置
|
|
463
|
+
- `pz settings init --force` 必须在删除/重建前自动备份当前 `pz.jsonc`、`pz.{env}.jsonc` 与 `.env*` 到 `.runtime/backups/pz-update-settings/`
|
|
464
|
+
- `pz settings init --force` 的语义是删除相关 settings 配置后按当前可发现 app 集重新生成,而不是在旧 `pz.jsonc` 上做保留式 merge
|
|
465
|
+
- `pz settings init --force` 发现 app 时必须重新扫描当前项目 `apps/` 容器;若旧 `installed_apps` 显式声明了外部正式 app,也应作为发现输入保留,但不得因此漏掉当前本地 app
|
|
466
|
+
- `pz settings update` 只补充默认值中新出现的键,不覆盖项目已写入的现有值
|
|
467
|
+
- 上述“不覆盖”只适用于项目作者显式写入的业务值;如果 app 在 `settings.py` / `settings/` 中声明了正式 normalize 规则,`pz settings update` 只允许做当前稳定合同内部的归一化,不再承担历史别名或旧键迁移
|
|
468
|
+
- 这类收口必须由 app 自己在 `settings.py` / `settings/` 中声明,不允许宿主手改 `pz.jsonc` 作为长期方案
|
|
469
|
+
- `pz settings update <target>` 可只同步指定最小子包的默认配置
|
|
470
|
+
- `pz settings update --reset-middlewares-order` 可按当前 conventional middleware manifest 重写托管区顺序,用于从历史显式顺序回收到当前正式默认序
|
|
471
|
+
|
|
472
|
+
如果某些最小子包配置只能在 app 启动后确定:
|
|
473
|
+
|
|
474
|
+
- 优先通过 `extensions()` 声明 `settings.provider`
|
|
475
|
+
- 不要自己再造一套 settings service
|
|
476
|
+
- provider 只补充运行态值,不承担 DB/Redis/基础 client 连接配置真相源
|
|
477
|
+
- 如果底层存在 `register_app_settings_provider(...)` 一类 API,也只应作为正式 `settings.provider` 实现内部细节,而不是业务侧第二入口
|
|
478
|
+
|
|
479
|
+
如果某些字段允许线上热更新:
|
|
480
|
+
|
|
481
|
+
- 统一在 `AppSettings.__runtime_overrides__` 里写详细配置对象
|
|
482
|
+
- 由 settings runtime owner 从 `AppSettings.__runtime_overrides__` 提取 schema,并通过正式 `settings.provider` patch 进入统一 settings 合并链
|
|
483
|
+
- schema 注册后,settings runtime owner 只认这套声明;不会再接受手写旁路白名单
|
|
484
|
+
- 这些热更新字段应在 `configs/default.jsonc` 中给出对应 settings init/update 模板默认值;若字段允许 `None` 且 TOML 不能表达空值,可暂时省略
|
|
485
|
+
|
|
486
|
+
mode 语义固定为:
|
|
487
|
+
|
|
488
|
+
- `live`
|
|
489
|
+
- 当前进程立即生效
|
|
490
|
+
- `refresh`
|
|
491
|
+
- 写入后等待运行态 refresh 链路生效
|
|
492
|
+
- `restart_required`
|
|
493
|
+
- 写入后等待进程重启生效
|
|
494
|
+
|
|
495
|
+
额外约束:
|
|
496
|
+
|
|
497
|
+
- `__runtime_overrides__` 里每个 key 都必须真的存在于 `AppSettings`
|
|
498
|
+
- 每个 value 都必须是详细配置对象,不能再写字符串、tuple/list 简写
|
|
499
|
+
- 错误 mode、错误字段名、错误 type hint 必须在注册期直接报错
|
|
500
|
+
- `route_policy` 属于正式保留的 runtime override surface;app 若声明了该字段,settings runtime owner 可自动暴露该对象,不要求每个 app 再手写第二份 route-policy 白名单。
|
|
501
|
+
|
|
502
|
+
推荐:
|
|
503
|
+
|
|
504
|
+
```python
|
|
505
|
+
__runtime_overrides__ = {
|
|
506
|
+
"enabled": {
|
|
507
|
+
"mode": "live",
|
|
508
|
+
"label": "启用开关",
|
|
509
|
+
},
|
|
510
|
+
"limit": {
|
|
511
|
+
"mode": "refresh",
|
|
512
|
+
"label": "数量限制",
|
|
513
|
+
"description": "调整后,新请求会读取新的限制。",
|
|
514
|
+
},
|
|
515
|
+
}
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
不要继续:
|
|
519
|
+
|
|
520
|
+
- 手写一份脱离 `AppSettings` 的 runtime override 白名单
|
|
521
|
+
- 让 settings runtime owner 自己猜哪些字段可热更新
|
|
522
|
+
- 同时保留 tuple/list 简写、字段级标记等多套声明方式
|
|
523
|
+
|
|
524
|
+
### 4.1 共享站点首帧与会话启动约定
|
|
525
|
+
|
|
526
|
+
首屏展示与首帧稳定的统一真相源固定为 `23-首屏展示与首帧稳定规范.md`。
|
|
527
|
+
本节只保留高频开发提醒;若与 `30` 冲突,以 `30` 为准。
|
|
528
|
+
|
|
529
|
+
站点型页面固定遵循一条正式主线:
|
|
530
|
+
|
|
531
|
+
- 首帧稳定由 shared site UI owner 负责
|
|
532
|
+
- 用户态启动链由 shared site UI owner 的正式 user runtime 统一负责
|
|
533
|
+
- 用户态变更事件只允许由 shared site UI owner 的正式 user runtime 派发
|
|
534
|
+
|
|
535
|
+
业务 app 的页面脚本不要做这些事:
|
|
536
|
+
|
|
537
|
+
- 在页面启动时再次主动调用共享会话检查
|
|
538
|
+
- 在业务脚本里自行派发共享用户态变更事件
|
|
539
|
+
- 用旧 DOM overlay、refresh shell、快照壳一类旧页面状态复用方案解决刷新抖动
|
|
540
|
+
|
|
541
|
+
共享层能统一治理的内容:
|
|
542
|
+
|
|
543
|
+
- SSR 首帧必须直接输出稳定可见状态
|
|
544
|
+
- 能从当前请求直接判定的状态,必须优先 SSR 输出最终态
|
|
545
|
+
- 只有依赖纯客户端判定的临时 UI,SSR 才默认隐藏,初始化后再 reveal
|
|
546
|
+
- 首屏关键资源用 eager / high priority
|
|
547
|
+
- 初始化测量阶段不暴露中间态动画
|
|
548
|
+
- 对外部可见且需要 SEO 的 HTML 页面,首屏所需稳定翻译必须优先由后端按当前页面和当前语言直接输出
|
|
549
|
+
- 上述页面的前端翻译请求只能用于非首屏交互区块、异步组件或缺词补拉,不得作为首屏正文真相源
|
|
550
|
+
|
|
551
|
+
业务侧仍需自己遵守的内容:
|
|
552
|
+
|
|
553
|
+
- 业务自定义 widget 如果依赖异步配置,默认先隐藏,拿到确认配置后再渲染
|
|
554
|
+
- 业务脚本不得再加第二条用户态启动链
|
|
555
|
+
- 业务组件若必须按会话状态分支,只读取共享层已经收敛后的最终状态
|
|
556
|
+
- 如果状态可由 `cookie`、`Accept-Language`、请求头、`module_config`、已编译 settings 直接判定,不要再做“SSR 先隐藏,客户端再纠正”的二次方案,应直接让 SSR 输出最终首帧
|
|
557
|
+
- 如果页面可被 CDN/代理缓存,SSR 输出与缓存头必须对齐最终受众,不能让“匿名缓存首帧 + 客户端再纠正登录态”成为正式方案
|
|
558
|
+
- 匿名公开 HTML 的正式口径是:
|
|
559
|
+
- 浏览器侧 `Cache-Control: public, max-age=0, must-revalidate`
|
|
560
|
+
- CDN 侧 `Cloudflare-CDN-Cache-Control: public, max-age=<ttl_seconds>`,TTL 唯一来自 `apps.pz_foundation.cache_control.policies["http.public_html_cdn"].ttl_seconds`
|
|
561
|
+
- 用户态 HTML 的正式口径是:
|
|
562
|
+
- `Cache-Control: private, no-store, no-cache, must-revalidate, max-age=0, s-maxage=0`
|
|
563
|
+
- `Cloudflare-CDN-Cache-Control: no-store`
|
|
564
|
+
- `Vary: Cookie, Authorization`
|
|
565
|
+
- 若页面用户态依赖认证 cookie 集合,这个集合必须进入 app 的正式 settings/defaults/normalize 链;不要把“框架内建识别一部分、宿主配置再补一部分、CDN 规则再猜一部分”拆成三套真相源
|
|
566
|
+
- 如果页面已有 SSR widget root / launcher / config,客户端只能 hydrate 现有节点,不要整块 `innerHTML` 重建
|
|
567
|
+
- 页面级 required-post、heartbeat、轮询端点必须按页面或模块显式开启,不要在全局模板里默认给所有页面注入
|
|
568
|
+
- cookie consent 只能处理合规展示、偏好持久化和 telemetry / ads / analytics consent 信号;不得控制支付、登录、上传、翻译、搜索、导航等页面功能
|
|
569
|
+
- 即使用户拒绝 consent,或浏览器拒绝 `localStorage` / cookie 持久化,页面核心功能也必须继续可用;失败只能退化为“偏好无法持久化”
|
|
570
|
+
- 公开页共享运行时里的会话检查、登出桥和通用请求头能力,应留在共享 base/runtime 正式主链;认证页专用动作和认证页表单逻辑应留在 `auth.js` 或认证页自己的页面脚本里,不要让首页/header 为了用户态检查继续捆绑认证页脚本。
|
|
571
|
+
- 公开页共享运行时里的语言切换路径解析、语言偏好持久化和 `window.changeLanguage` 正式入口,也应留在共享 base/runtime 主链;`nav.js` 只负责导航 DOM 交互,不要再让 header、footer banner、语言推荐各自长出第二套语言跳转实现。
|
|
572
|
+
- 共享公开页的 source 资产是作者真相源;develop / test 默认加载 source,不因为 `site.min.js` / `site.min.css` 文件存在而自动消费旧聚合产物。
|
|
573
|
+
- `site.min.js` / `site.min.css` 只在 product 或显式 `ASSETS_COMBINE` 下作为正式构建产物加载;产物缺失时应走正式构建、检查失败或保守 source 回退,不用 inline 补丁掩盖旧聚合产物。
|
|
574
|
+
- 共享 bundle 自身也应继续收敛职责边界:`site.min.js` 这类共享公开页 bundle 只应承载真正共享且偏首屏/偏全局的公共能力;像页脚增强、cookie banner、语言推荐、评论区、示例区块这类非首屏增强,不应因为“同属共享层”就继续默认卷进共享首屏 bundle。
|
|
575
|
+
- 若上述共享增强脚本仍需要统一加载,应继续复用共享 runtime 现有的声明式加载主链,例如 header/footer/page deferred loader;不要因为 bundle 变轻,就在模板里再长出第四套散落 `<script>` 清单。
|
|
576
|
+
- 对没有正式运行时职责的历史空壳脚本或兼容标记脚本,应优先从正式 bundle 和正式 loader 清单中移除,而不是长期保留在公开页主链里增加请求和解析负担。
|
|
577
|
+
- 页面级正式聚合产物必须声明在页面 `page_config.asset_bundle`,再由 app 通过 `site_assets.page_bundle` 扩展公开给共享层统一构建;不要在模板、模块配置或宿主里再维护第二套页面资源清单。
|
|
578
|
+
- `page_config.asset_bundle` 至少应稳定声明页面 bundle `key` 与正式输出路径;页面模板默认优先消费共享层已生成的页面 bundle,只有在 bundle 缺失时才允许短期 fallback 到拆分资源。
|
|
579
|
+
- 若页面需要把非首屏交互脚本延后到接近视口再加载,正式声明位固定为页面 `page_config.deferred_js`;共享 runtime 统一按声明加载,不允许在模板里手写第三套脚本清单。
|
|
580
|
+
- `page_config.deferred_js` 只用于非首屏增强脚本,例如下半屏组件、评论区、示例区块等;首屏关键交互仍应留在 `page_config.asset_bundle` 的 critical bundle 中。
|
|
581
|
+
- 已安装的检查 / 预检 owner 可以通过 `public.get_check_facade()` 接入 `pz check`;页面 bundle 等具体 blocking 检查项归其 owning 子包文档维护,新增子包必须通过正式检查链,而不是依赖宿主兜底。
|
|
582
|
+
- 若业务覆盖了首屏 hero 的图片源,必须同时在页面 `hints` block 中为首屏图片补对应的 `rel="preload" as="image"`;共享层只能为约定内的共享默认 hero 资源提供通用预加载
|
|
583
|
+
- 若页面属于 SEO/landing/公开关键词页/博客/帮助页等可缓存公开页面,首屏翻译网络请求目标应为 `0`
|
|
584
|
+
- 不要为了图省事把整模块或整站翻译全集注入 HTML;只允许输出当前页面首屏实际需要的最小词条集合
|
|
585
|
+
- `admin`、内部运营页、自用后台、强登录态工作台可按交互效率优先,不强制要求首屏翻译请求为 `0`
|
|
586
|
+
|
|
587
|
+
## 5. `apps.py` 约定
|
|
588
|
+
|
|
589
|
+
`apps.py` 是最小子包元信息入口。
|
|
590
|
+
|
|
591
|
+
标准形态固定为:
|
|
592
|
+
|
|
593
|
+
- 定义一个 app class
|
|
594
|
+
- class 上声明 `id`、`name`、`version`、`required_pz`
|
|
595
|
+
- `id` 使用静态字符串,不依赖 settings 常量、环境变量、函数调用或运行时计算
|
|
596
|
+
- 模块级暴露 `app = <AppClass>()`
|
|
597
|
+
- `app.id` 是 settings namespace、migration owner、extension owner 与诊断身份的唯一真相源
|
|
598
|
+
|
|
599
|
+
`apps.py` 是 app 入口保留名。非 app 模块不应使用这个文件名;例如 runtime CLI、普通命令分发、工具集合、路由聚合等应使用更具体的文件名。
|
|
600
|
+
|
|
601
|
+
应主要放:
|
|
602
|
+
|
|
603
|
+
- `id`
|
|
604
|
+
- `name`
|
|
605
|
+
- `version`
|
|
606
|
+
- `required_pz`
|
|
607
|
+
- `requires_apps`
|
|
608
|
+
- `extensions()`
|
|
609
|
+
- `startup()/shutdown()`
|
|
610
|
+
|
|
611
|
+
不应把这些东西继续塞回去:
|
|
612
|
+
|
|
613
|
+
- URL 主链
|
|
614
|
+
- 默认配置模板来源或运行时读取入口
|
|
615
|
+
- 本地 host 壳
|
|
616
|
+
|
|
617
|
+
`extensions()` 返回值应统一采用结构化扩展对象,而不是裸函数、隐式 import path 或模块级全局注册。
|
|
618
|
+
|
|
619
|
+
扩展对象应是声明期 contract object;其中挂载的 provider、builder、tool、handler 必须是稳定对象实现,不能依赖 import-time 副作用完成注册。
|
|
620
|
+
|
|
621
|
+
推荐所有扩展点共享一个最小公共头:
|
|
622
|
+
|
|
623
|
+
- `kind`
|
|
624
|
+
- `id`
|
|
625
|
+
- `owner_app`
|
|
626
|
+
- `api_version`
|
|
627
|
+
- `priority`
|
|
628
|
+
|
|
629
|
+
然后再挂各自专用字段。
|
|
630
|
+
|
|
631
|
+
最常用的三类扩展对象形状固定如下:
|
|
632
|
+
|
|
633
|
+
- `host.builder`
|
|
634
|
+
- `builder.build(ctx) -> HostBuildResult`
|
|
635
|
+
- 用于产出唯一最终 host
|
|
636
|
+
- `settings.provider`
|
|
637
|
+
- `provider.provide(ctx) -> tuple[SettingsPatch, ...]`
|
|
638
|
+
- 用于补充运行时 settings patch
|
|
639
|
+
- `cli.tool`
|
|
640
|
+
- `tool.configure(parser) -> None`
|
|
641
|
+
- `tool.run(ctx) -> int`
|
|
642
|
+
- 用于暴露正式 app CLI 命令
|
|
643
|
+
|
|
644
|
+
不要在 `extensions()` 里返回:
|
|
645
|
+
|
|
646
|
+
- 裸字符串
|
|
647
|
+
- 任意 import path
|
|
648
|
+
- 未带 `kind/id/owner_app` 的匿名函数
|
|
649
|
+
- 依赖 import 时副作用才能生效的隐式注册器
|
|
650
|
+
|
|
651
|
+
## 6. `public.py` 约定
|
|
652
|
+
|
|
653
|
+
`public.py` 是稳定公开面。
|
|
654
|
+
|
|
655
|
+
只有明确要给其它 app 依赖的 facade / helper 才能从这里出。
|
|
656
|
+
|
|
657
|
+
优先从 `public.py` 暴露窄 facade、Protocol、命令对象、结果对象或 factory;不要把内部 service、repository、adapter、SDK client、session、pool、临时 cache 作为跨包公开面。
|
|
658
|
+
|
|
659
|
+
跨子包依赖规则固定如下:
|
|
660
|
+
|
|
661
|
+
- `pz_contrib` 可以直接 import `pz`
|
|
662
|
+
- 其它最小子包可以依赖 `pz` 的正式公开面
|
|
663
|
+
- 除 `* -> pz` 外,其它业务子包默认不要直接 import 另一个子包的内部实现
|
|
664
|
+
- 非官方维护最小子包的跨包依赖,优先走对方的 `public.py`
|
|
665
|
+
- 如果是进程边界或服务边界能力,优先走 HTTP / API / 明确声明的 provider
|
|
666
|
+
- 只有明确声明为正式公开面的模块,才允许成为跨子包依赖入口
|
|
667
|
+
|
|
668
|
+
不要让其它最小子包直接 import:
|
|
669
|
+
|
|
670
|
+
- `service.py`
|
|
671
|
+
- `manager.py`
|
|
672
|
+
- 内部 adapter
|
|
673
|
+
- 临时 runtime helper
|
|
674
|
+
- `apps.py`
|
|
675
|
+
- `urls.py` 或 `urls/`
|
|
676
|
+
- `settings.py` 或 `settings/` 内部实现细节
|
|
677
|
+
|
|
678
|
+
补充规则:
|
|
679
|
+
|
|
680
|
+
- `settings.provider` 属于扩展点合同,不属于 `public.py` 读取入口
|
|
681
|
+
- 跨包读取配置仍然只能走 `pz_settings_center`
|
|
682
|
+
- `host.builder`、`settings.provider` 等 registry 扩展的具体实现可以放在子模块中,但正式暴露应通过 `extensions()` 返回结构化对象
|
|
683
|
+
- app CLI 的正式暴露面固定为 `<app>/cli/<tool>.py` 中的模块级 `tool = CliToolSpec(...)`,并由 `pz` 编译进 `RuntimePlan.cli_manifest`
|
|
684
|
+
|
|
685
|
+
## 7. 新增功能时的决策顺序
|
|
686
|
+
|
|
687
|
+
按这个顺序判断:
|
|
688
|
+
|
|
689
|
+
1. 能不能只改最小子包自己的 `urls.py` 或 `urls/` / `settings` / `extensions()`
|
|
690
|
+
2. 能不能在 `pz_contrib` 增加官方通用处理
|
|
691
|
+
3. 只有正式合同缺位时,才改 `pz`
|
|
692
|
+
|
|
693
|
+
动代码前先补一个对象边界判断:
|
|
694
|
+
|
|
695
|
+
- 谁是 owner?
|
|
696
|
+
- 状态生命周期归谁?
|
|
697
|
+
- 依赖是否已有 public surface / Protocol / provider contract?
|
|
698
|
+
- 输出是否是结构化对象?
|
|
699
|
+
- 是否会形成第二套 registry、loader、settings reader、path helper 或 runtime owner?
|
|
700
|
+
|
|
701
|
+
一句话:
|
|
702
|
+
|
|
703
|
+
- 先改子包
|
|
704
|
+
- 再改 `pz_contrib` 容器里的官方通用实现
|
|
705
|
+
- 最后才允许改 `pz`
|
|
706
|
+
|
|
707
|
+
## 8. 工作区开发流程
|
|
708
|
+
|
|
709
|
+
共享包改动必须直接在解析后的正式源码仓完成;当前工作区默认以项目 `.sources/...` 下解析出的真实 git 根为准,不在宿主里打补丁。
|
|
710
|
+
|
|
711
|
+
标准流程:
|
|
712
|
+
|
|
713
|
+
1. 在源仓修改
|
|
714
|
+
2. 执行 `pz env sync`
|
|
715
|
+
3. 跑宿主验证
|
|
716
|
+
4. 执行 `pz run`
|
|
717
|
+
5. 再看 `/health`
|
|
718
|
+
|
|
719
|
+
标准命令:
|
|
720
|
+
|
|
721
|
+
```bash
|
|
722
|
+
pz env sync --project-dir /path/to/project
|
|
723
|
+
pz run
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
补充说明:
|
|
727
|
+
|
|
728
|
+
- 上述流程是当前工作区的共享包开发规则
|
|
729
|
+
- 它不等于所有 `pz` 项目都必须把源码直接改在宿主目录里
|
|
730
|
+
- 对普通 `pz` 项目,上层编排命令可以提供“把正式最小子包源码拉到项目本地开发目录”的能力
|
|
731
|
+
- 但这类开发态源码拉取,不改变当前工作区“共享包真相源在解析后的正式源码仓”的规则
|
|
732
|
+
- 宿主依赖同步的唯一作者面真相源应回到项目 `pyproject.toml` 的 `[tool.uv.sources]`
|
|
733
|
+
- `apps/.sources/` 若存在,也只作为 `pz dev` 管理的开发态 checkout 承载区;它不再需要额外的工作区包清单
|
|
734
|
+
|
|
735
|
+
### 8.1 项目开发态与发布态命令边界
|
|
736
|
+
|
|
737
|
+
针对普通 `pz` 项目,上层命令边界固定如下:
|
|
738
|
+
|
|
739
|
+
- `pz startapp <app_id>`
|
|
740
|
+
- 在项目 `apps/` 下创建一个正式最小子包骨架
|
|
741
|
+
- 只负责脚手架,不负责 distribution 安装与发布
|
|
742
|
+
- `pz dev init`
|
|
743
|
+
- 作为新建开发项目的唯一正式编排入口
|
|
744
|
+
- 默认创建或复用项目骨架与默认本地 app,通过 `dev source cache` 固定官方 `pz_apps` revision,并把全部正式最小叶子导入 `apps/<app_id>` 交给业务仓管理
|
|
745
|
+
- 显式 `--link` 才保留原 bootstrap checkout;必须且只能搭配 `--clone-sources` 或 `--source-dir`
|
|
746
|
+
- 创建或复用项目根 `.venv`
|
|
747
|
+
- 所有源码原子阶段后只执行一次 settings、环境和 IDE 投影收敛;`--no-sync` 只跳过环境安装
|
|
748
|
+
- `pz app init`
|
|
749
|
+
- 作为生产装配模型下的项目默认包初始化命令
|
|
750
|
+
- 只从已有 `apps/.sources` checkout 读取默认包名与内部 PyPI 安装位置
|
|
751
|
+
- 默认把 `.sources` 中所有正式最小 app 子包加入 `installed_apps`
|
|
752
|
+
- 创建或复用项目根 `.venv`
|
|
753
|
+
- 默认把 `pz-cli` 与这些正式最小 app 的已发布 distribution 安装进项目 `.venv`
|
|
754
|
+
- 安装必须走内部 PyPI / devpi,不得从源码 editable 安装
|
|
755
|
+
- 默认安装位置来自 `.sources` 中各包最近 source-root 的 `tool.pz.publish.default_target` 与对应 `targets.<target>.check_url`;若默认包清单跨多个内部 PyPI / devpi index,`app init` 会把这些 index 去重后交给同一次安装解析
|
|
756
|
+
- 安装项使用 `.sources` 中各包 `pyproject.toml` 的 distribution 名,具体版本由内部 PyPI / devpi 与项目锁仓策略解析;`app init` 不把源码 checkout 的当前 version 当作生产安装 pin
|
|
757
|
+
- `--no-sync` / `-S` 是跳过安装开关;默认行为会安装 distribution
|
|
758
|
+
- 按 source app 的 `requires_apps` 依赖关系写入 `installed_apps`
|
|
759
|
+
- 不 clone 源码、不发布 package
|
|
760
|
+
- `pz app add <app_id>`
|
|
761
|
+
- 编排 `app versions -> app declare -> app install -> settings update`
|
|
762
|
+
- 默认解析并安装兼容的最新稳定版本;`--version <version>` 精确指定版本
|
|
763
|
+
- 把解析后的精确 distribution/version 写入项目声明和 `uv.lock`,不得把 `latest` 作为持久化值
|
|
764
|
+
- 不把源码写入项目 `apps/`
|
|
765
|
+
- `pz app rm <app_id>`
|
|
766
|
+
- 编排 `app undeclare -> env sync -> settings update`,移除项目声明并收敛环境
|
|
767
|
+
- `pz app update <app_id>`
|
|
768
|
+
- 默认把已声明 app 升级到兼容的最新稳定版本;`--version <version>` 精确切换版本
|
|
769
|
+
- 复用与 `app add` 相同的版本解析、精确声明和安装原子能力
|
|
770
|
+
- `pz app versions <app_id>`
|
|
771
|
+
- 只读列出索引中的可用版本、当前声明版本、当前锁定版本与已安装版本
|
|
772
|
+
- 按 PEP 440 排序并标记 prerelease 和 yanked;默认最新版本选择不把它们作为候选
|
|
773
|
+
- `pz app declare/undeclare/install`
|
|
774
|
+
- 分别拥有精确依赖声明、撤销声明和依赖环境收敛的原子事务
|
|
775
|
+
- `declare` 不接管 `apps/<app_id>` 中业务拥有的真实源码;同名真实目录存在时先用业务 Git 工作流收敛源码所有权
|
|
776
|
+
- 不隐式串联 settings 或其它阶段;上层 `app add/update/rm/init` 负责完整编排
|
|
777
|
+
- 编排跨原子阶段采用前向续跑:已完成阶段保留,失败时报告精确版本、`completed_stages` 与 `next_stage`
|
|
778
|
+
- `pz dev add <app-or-source>`
|
|
779
|
+
- 编排 `dev source cache -> dev source import -> settings update -> env sync -> dev ide sync`;非 Git 本地输入跳过 cache
|
|
780
|
+
- 输入必须唯一解析到一个正式最小子包
|
|
781
|
+
- Git 来源默认使用 `develop` 分支;cache 命中时先更新,未命中时 clone,再从固定 full revision 导出目标源码
|
|
782
|
+
- 最终源码原子落位到 `apps/<app_id>`,不保留 `.git`、虚拟环境、cache 或构建产物
|
|
783
|
+
- 导入源码由业务仓统一管理;PZ 不自动更新、覆盖或删除
|
|
784
|
+
- 项目 `apps.<app_id>.sources.source_mode`、app provenance、宿主 dependency 与指向 `apps/<app_id>` 的 editable `tool.uv.sources` 在同一事务收敛
|
|
785
|
+
- Git provenance 记录无凭据 canonical source、full revision 与 leaf subpath;本地目录 provenance 只记录 `source = "local"`、不可逆 source identity 与 leaf subpath
|
|
786
|
+
- 允许传非 Git 本地目录,但遵守相同的唯一叶子解析、排除和校验合同
|
|
787
|
+
- 任意真实本地 app、linked binding 或其他项目本地路径已暴露相同 `app_id` 时直接失败;冲突按 app 身份判断,不按路径名称判断
|
|
788
|
+
- `pz dev link <repo-or-app>`
|
|
789
|
+
- 编排 `dev source link -> settings update -> env sync -> dev ide sync`
|
|
790
|
+
- Git checkout 固定落在 `apps/.sources/<repo_key>`,受管 checkout 的目标分支固定为当前项目 `business_app_id`
|
|
791
|
+
- `apps/.sources/<repo_key>` 若已是外部共享 Git checkout 的符号链接,则直接只读复用,不切换共享目标分支
|
|
792
|
+
- 默认基础分支是 `develop`,显式替代值只有 `main`
|
|
793
|
+
- 允许直接传非 Git 本地目录;此时记录 direct link,不切换外部目录的分支
|
|
794
|
+
- 目录输入在 `apps/<repo_key>` 建立目录 binding;叶子输入在 `apps/<app_id>` 建立叶子 binding
|
|
795
|
+
- runtime 只发现 `apps/` 下的真实本地 app 和可见 binding,物理 `apps/.sources` 不贡献 app
|
|
796
|
+
- `pz dev branch <repo-or-app>`
|
|
797
|
+
- 编排 `dev source branch -> env sync -> dev ide sync`
|
|
798
|
+
- 原子 source 阶段把已有受管 checkout 切到 manifest 业务分支
|
|
799
|
+
- worktree 必须 clean;不自动 stash、commit、merge、rebase、reset 或 push
|
|
800
|
+
- 共享 checkout 返回 `PZ_DEV_SHARED_CHECKOUT_READ_ONLY`
|
|
801
|
+
- `pz dev update <repo-or-app>`
|
|
802
|
+
- 编排 `dev source update -> env sync -> dev ide sync`
|
|
803
|
+
- 只在 clean 的业务分支上执行 upstream fast-forward-only 更新
|
|
804
|
+
- 共享 checkout 返回 `PZ_DEV_SHARED_CHECKOUT_READ_ONLY`,由共享仓库 owner 在真实目录更新
|
|
805
|
+
- `pz dev unlink <binding>`
|
|
806
|
+
- 编排 `dev source unlink -> settings update -> env sync -> dev ide sync`
|
|
807
|
+
- 只删除精确 binding 和对应快捷方式,默认保留 checkout
|
|
808
|
+
- `--delete-checkout` 仅在最后一个 binding 且没有未推送提交时允许
|
|
809
|
+
- 共享 checkout 的 `--delete-checkout` 只解除 `apps/.sources/<repo_key>` 链接,不删除外部目标
|
|
810
|
+
- 不删除 `pz dev add` 导入的业务仓真实源码
|
|
811
|
+
- `pz dev source cache/import/link/branch/update/unlink`
|
|
812
|
+
- 分别拥有可丢弃 Git 获取 cache、业务源码导入、binding、linked Git 分支/更新和解除 binding 的原子事务
|
|
813
|
+
- `source cache` 只在当前用户隔离的 `<system-temp>/pz/users/<user-key>/git/` 创建或刷新 checkout;root、lock、checkout 与 owner marker 必须验证当前用户 owner、限制权限和非符号链接
|
|
814
|
+
- cache 结果必须携带 canonical `repo_key`、full commit 与不可变 resolution ref;失败续跑只验证已有 exact ref,cache/ref 丢失时明确失败
|
|
815
|
+
- Git 模式 `source import` 必须显式消费 `repo_key + full revision` 并验证 resolution ref,绝不读取 branch、`HEAD` 或 cache 最新状态;本地目录模式不需要 revision
|
|
816
|
+
- 不刷新 settings、环境或 IDE;日常人工入口使用上层 `dev add/link/branch/update/unlink`
|
|
817
|
+
- `pz dev ide sync`
|
|
818
|
+
- 编译一次可见最小子包投影,并分别委托 PyCharm 与 VS Code provider
|
|
819
|
+
- PyCharm 原子维护 `.idea/pz.generated.sources.iml` 与 `modules.xml` 中唯一 PZ entry;linked app 登记 canonical 叶子根
|
|
820
|
+
- VS Code 结构化维护 `.vscode/settings.json` 中 PZ 插入的 `python.analysis.extraPaths`,使用项目可见叶子路径,并以 `pz.generated.sources.json` 区分用户与 PZ entries
|
|
821
|
+
- 不登记整个 `.sources` checkout,不复制源码,不进入运行时/发布真相;对应 IDE 目录不存在时 provider 返回 `not_applicable`
|
|
822
|
+
- `pz pub <app_id>`
|
|
823
|
+
- 作为单包发布编排命令,依次执行目标子包 commit、package tag 与 build/upload
|
|
824
|
+
- 优先接受当前 Git 仓库根 distribution 的精确匹配,否则枚举 `apps/.sources` 并复用正式 workspace 子包发现程序搜索目标 app
|
|
825
|
+
- 不从业务仓复制目录发布;没有 Git、找不到目标、多个仓库重复命中分别失败
|
|
826
|
+
- 根 distribution 提交显式 `.` 整仓 scope;嵌套子包只提交目标路径,不提交同仓其他子包修改,不执行 `git push`
|
|
827
|
+
- `pz pub commit <app_id>`
|
|
828
|
+
- 原子提交目标子包当前可发布源码,commit subject 固定为 `release(<app_id>): <version>`
|
|
829
|
+
- `pz pub tag <app_id>`
|
|
830
|
+
- 原子创建 `<distribution-name>@<version>` annotated tag
|
|
831
|
+
- `pz pub upload <app_id>`
|
|
832
|
+
- 要求当前 checkout clean 且 HEAD 等于 package tag commit,直接构建、验证 version/wheel 后 upload,不创建临时 worktree
|
|
833
|
+
- `pz pub rm <app_id>`
|
|
834
|
+
- 只负责从目标 publish index 删除这个 distribution 的全部已发布版本
|
|
835
|
+
- `pz pub batch <app_id>...`
|
|
836
|
+
- 作为上层编排命令,按显式给定顺序为每个 app 串行执行完整 `pub` 编排
|
|
837
|
+
|
|
838
|
+
不要混淆:
|
|
839
|
+
|
|
840
|
+
- `startapp` 是本地最小子包脚手架命令
|
|
841
|
+
- `dev init` 是新项目开发编排命令
|
|
842
|
+
- `app init` 默认是已有 `.sources` 到 distribution 安装清单、项目声明和项目 `.venv` 的生产装配命令;`--no-sync` 只完成声明与 settings 刷新,不创建 `.venv`
|
|
843
|
+
- `app init` / `app add` / `app rm` / `app update` 是项目装配编排命令;`app versions` / `app declare` / `app undeclare` / `app install` 是可独立调用的版本与声明原子命令
|
|
844
|
+
- `app update` 会刷新当前项目解释器环境中的已发布 distribution,而不改源码 checkout
|
|
845
|
+
- `dev add` / `dev link` / `dev branch` / `dev update` / `dev unlink` 是日常编排命令;`dev source ...` 与 `dev ide sync` 是可单独验证和续跑的原子命令
|
|
846
|
+
- `pub` 是 commit/tag/upload 发布编排命令
|
|
847
|
+
- `pub commit` / `pub tag` / `pub upload` 是发布态原子命令
|
|
848
|
+
- `pub rm` 是发布态删除命令
|
|
849
|
+
- `pub batch` 是发布态编排命令
|
|
850
|
+
- `app add` 的安装目标是当前项目解释器环境,不是 `apps/` 本地源码容器
|
|
851
|
+
- `app add` / `app update` 永远按 distribution 名称安装,也不回退为源码仓 `editable` 安装
|
|
852
|
+
- `app add` / `app update` 未指定版本时解析兼容的最新稳定版本,指定 `--version` 时只接受该精确版本;两种路径最终都必须持久化精确版本并更新 `uv.lock`
|
|
853
|
+
- `app rm` 只允许移除已声明 app;若目标 app 未声明,命令必须报错,不得返回误导性的成功提示
|
|
854
|
+
- 若项目 `publish` 或对应 source-root 的 `tool.pz.publish.targets.<name>.check_url` 已声明正式私有索引,`app add` / `app update` 可以只复用这份索引元数据补全安装认证;这不改变 distribution 安装语义
|
|
855
|
+
- `app add` / `dev add` / `dev link` 完成后必须检查结果状态;若多个项目本地源码位置同时暴露同一 `app_id`,命令直接报错,同时给出标准人工处理指引
|
|
856
|
+
- 运行时发现阶段若项目源码扫描范围与环境中已安装 distribution 同时都可导入同名最小子包,正式优先级固定为项目源码优先
|
|
857
|
+
- `dev add` 的业务源码统一落在 `apps/<app_id>`;`dev link` 的真实 Git checkout 统一落在 `apps/.sources/<repo_key>`,输入粒度只决定 `apps/` 下建立目录 binding 还是叶子 binding
|
|
858
|
+
- 叶子最小子包 repo 直接暴露目录;只有 monorepo 才需要仓库级 checkout 复用模型
|
|
859
|
+
- 仅仅“同步源码到本地”不等于“可以发布”
|
|
860
|
+
- `--base-branch` 只允许 `develop` 或 `main`,只决定业务分支首次创建点;direct link 不进入 Git 分支协议
|
|
861
|
+
- 批量发布不推断依赖顺序;若存在依赖链,顺序必须由调用方显式给出
|
|
862
|
+
- 单包 `pub` 不自动升级 `project.version` 或生成 changelog;版本必须在调用前明确
|
|
863
|
+
- `pub` 只创建本地 release commit 与 annotated tag,不执行 `git push`
|
|
864
|
+
- 后续阶段失败时保留已完成 commit/tag;重跑必须复用精确匹配状态并从未完成阶段继续
|
|
865
|
+
|
|
866
|
+
正式要求:
|
|
867
|
+
|
|
868
|
+
- 发布前必须有明确版本号
|
|
869
|
+
- 发布前必须满足当前 Git root distribution 精确匹配,或 `apps/.sources` 中目标子包唯一命中
|
|
870
|
+
- `project.version` 是 release version 真相源;tag 固定为 `<distribution-name>@<version>`
|
|
871
|
+
- Git index 必须预先 clean;只允许目标 app path 进入 release commit
|
|
872
|
+
- 其他正式最小子包的 unstaged 修改可以保留,未归属任何最小子包的 shared-root 未提交修改阻止单包发布
|
|
873
|
+
- package tag commit 必须就是当前 clean HEAD;tag、`project.version`、wheel metadata 与远端 artifact identity 必须一致
|
|
874
|
+
- `publish` 不得直接消费未版本化的源码目录状态
|
|
875
|
+
- 顶层不再设计 `pz sync`;linked checkout 更新统一使用 `pz dev update`,业务仓源码更新统一走业务 Git 工作流
|
|
876
|
+
- 若 `A`、`B` 两个最小子包属于同一个 Git 仓库,`pz dev link B` 必须复用因 `A` 已存在的本地仓库 checkout,而不是再次 clone 第二份;`pz dev add` 每次只导入调用方明确选择的一个最小子包,不保留共享 checkout
|
|
877
|
+
- 根 distribution 可直接从自身 Git root 发布;嵌套目标源码仓仍通过宿主 `apps/.sources/<repo_key>` 暴露。源码仓根在 `pyproject.toml` 的 `tool.pz.publish` 下声明发布目标
|
|
878
|
+
- 若调用时未在项目 `pz.jsonc` 中声明 `publish`,`pz pub` 必须从目标包根向上查找最近的 source-root `pyproject.toml`,并使用其中的 `tool.pz.publish`
|
|
879
|
+
- 当前正式 devpi 发布目标分为 `devpi_dev`、`devpi_prod` 与 `devpi_pub`
|
|
880
|
+
- 非敏感目标配置写在各源码仓根 `pyproject.toml` 的 `tool.pz.publish`,凭证通过当前 deploy 仓的 `docker-compose/devpi/.env` 或等价 shell 环境变量注入
|
|
881
|
+
- `devpi_dev` 用于开发态验证与依赖闭环;`devpi_prod` 用于正式私有发布;`devpi_pub` 用于正式公开发布
|
|
882
|
+
- `pz pub` 默认使用目标上的 `username_env` / `password_env` 解析上传凭据;若当前项目根存在 `.env`,CLI 可以把其中同名凭据补入子进程环境,但不覆盖已显式导出的环境变量
|
|
883
|
+
- `pz pub rm` 与 `pz pub` 复用同一份 target 与凭据解析;当前正式只支持 devpi target
|
|
884
|
+
- `pz app add` / `pz app update` 若命中正式 devpi `check_url`,默认使用 `DEVPI_INSTALL_USER` / `DEVPI_INSTALL_PASSWORD` 作为安装认证;若 target 显式声明 `install_username_env` / `install_password_env`,则以 target 配置为准
|
|
885
|
+
|
|
886
|
+
发布锁:
|
|
887
|
+
|
|
888
|
+
- 项目可以在 `pz.jsonc` 顶层 `extensions.publish_lock.targets` 里声明允许发布到特定索引的包名单
|
|
889
|
+
- key 既可以写 index 分类:`pub` / `prod` / `dev`
|
|
890
|
+
- 也可以写精确 publish target 名:例如 `devpi_pub` / `devpi_prod` / `devpi_dev`
|
|
891
|
+
- 当 `pz pub` 命中对应锁时,只有 allowlist 里的 app / distribution 才允许继续发布
|
|
892
|
+
|
|
893
|
+
示例:
|
|
894
|
+
|
|
895
|
+
```jsonc
|
|
896
|
+
{
|
|
897
|
+
"extensions": {
|
|
898
|
+
"publish_lock": {
|
|
899
|
+
"targets": {
|
|
900
|
+
"pub": ["pz-cli"],
|
|
901
|
+
"devpi_prod": ["pz"],
|
|
902
|
+
"dev": ["example-app"]
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
发布目标示例:
|
|
910
|
+
|
|
911
|
+
```toml
|
|
912
|
+
[tool.pz.publish]
|
|
913
|
+
default_target = "devpi_pub"
|
|
914
|
+
|
|
915
|
+
[tool.pz.publish.targets.devpi_pub]
|
|
916
|
+
publish_url = "https://packages.example.test/packages/pub/"
|
|
917
|
+
check_url = "https://packages.example.test/packages/pub/+simple/"
|
|
918
|
+
username_env = "DEVPI_OWNER"
|
|
919
|
+
password_env = "DEVPI_OWNER_PASSWORD"
|
|
920
|
+
install_username_env = "DEVPI_INSTALL_USER"
|
|
921
|
+
install_password_env = "DEVPI_INSTALL_PASSWORD"
|
|
922
|
+
|
|
923
|
+
[tool.pz.publish.targets.devpi_prod]
|
|
924
|
+
publish_url = "https://packages.example.test/packages/prod/"
|
|
925
|
+
check_url = "https://packages.example.test/packages/prod/+simple/"
|
|
926
|
+
username_env = "DEVPI_OWNER"
|
|
927
|
+
password_env = "DEVPI_OWNER_PASSWORD"
|
|
928
|
+
install_username_env = "DEVPI_INSTALL_USER"
|
|
929
|
+
install_password_env = "DEVPI_INSTALL_PASSWORD"
|
|
930
|
+
|
|
931
|
+
[tool.pz.publish.targets.devpi_dev]
|
|
932
|
+
publish_url = "https://packages.example.test/packages/pub/"
|
|
933
|
+
check_url = "https://packages.example.test/packages/pub/+simple/"
|
|
934
|
+
username_env = "DEVPI_OWNER"
|
|
935
|
+
password_env = "DEVPI_OWNER_PASSWORD"
|
|
936
|
+
install_username_env = "DEVPI_INSTALL_USER"
|
|
937
|
+
install_password_env = "DEVPI_INSTALL_PASSWORD"
|
|
938
|
+
```
|
|
939
|
+
|
|
940
|
+
发布示例:
|
|
941
|
+
|
|
942
|
+
```bash
|
|
943
|
+
cd <pz_apps_repo_root>
|
|
944
|
+
set -a
|
|
945
|
+
source <deploy_repo_root>/docker-compose/devpi/.env
|
|
946
|
+
set +a
|
|
947
|
+
pz pub example-app --target devpi_dev --project-dir .
|
|
948
|
+
```
|
|
949
|
+
|
|
950
|
+
正式版本发布示例:
|
|
951
|
+
|
|
952
|
+
```bash
|
|
953
|
+
cd <pz_apps_repo_root>
|
|
954
|
+
set -a
|
|
955
|
+
source <deploy_repo_root>/docker-compose/devpi/.env
|
|
956
|
+
set +a
|
|
957
|
+
pz pub example-app --target devpi_prod --project-dir .
|
|
958
|
+
```
|
|
959
|
+
|
|
960
|
+
正式要求补充:
|
|
961
|
+
|
|
962
|
+
- `devpi_prod` 只允许发布已经显式升级 `project.version` 的版本
|
|
963
|
+
- 进入 `devpi_prod` 前,应先在 `devpi_dev` 完成上传与安装闭环验证
|
|
964
|
+
- 若一个最小子包依赖同批次其它新版本子包,应先把依赖链发布到同一目标索引,再发布上层包
|
|
965
|
+
|
|
966
|
+
## 9. 什么时候必须补契约测试
|
|
967
|
+
|
|
968
|
+
这些场景必须补 source-repo 或宿主契约测试:
|
|
969
|
+
|
|
970
|
+
- 改了 `urls.py` / `urls/` / URL 装配规则
|
|
971
|
+
- 改了 `settings.py` / `settings/` 声明协议或 `settings_center.settings.<app_id>` 读取规则
|
|
972
|
+
- 改了 provider 补值逻辑或热更新字段白名单
|
|
973
|
+
- 改了 `public.py` 公开面
|
|
974
|
+
- 改了 host builder / host assembly
|
|
975
|
+
- 改了 scaffold 模板
|
|
976
|
+
|
|
977
|
+
测试要锁死的是:
|
|
978
|
+
|
|
979
|
+
- 正式真相源在哪里
|
|
980
|
+
- 哪些旧写法不允许回流
|
|
981
|
+
- 新人照着模板写不会再次走偏
|
|
982
|
+
|
|
983
|
+
## 10. 最常见的错误
|
|
984
|
+
|
|
985
|
+
不要再做这些事:
|
|
986
|
+
|
|
987
|
+
- 把官方实现塞回 `pz`
|
|
988
|
+
- 让多个最小子包各自提供独立启动壳
|
|
989
|
+
- 在 `apps.py` 里重新拼 URL
|
|
990
|
+
- 在 app 里造自己的 FastAPI host
|
|
991
|
+
- 在多个文件里分散默认配置
|
|
992
|
+
- 为了“灵活”再造一层别人看不懂的声明 DSL
|
|
993
|
+
- 改了共享包却只在本地源码目录验证,不重装宿主环境
|
|
994
|
+
|
|
995
|
+
## 11. 快速判断
|
|
996
|
+
|
|
997
|
+
如果你只想快速判断一个改动应该落哪里:
|
|
998
|
+
|
|
999
|
+
- 改 URL 聚合:先看 `urls.py` 或 `urls/`
|
|
1000
|
+
- 改默认配置模板:先看 `configs/default.jsonc`,再看 `settings.py` / `settings/`
|
|
1001
|
+
- 改 HTML / CDN 缓存边界:先看共享 middleware,再看对应 app 的 `configs/default.jsonc` 模板与 `settings.py` / `settings/`
|
|
1002
|
+
- 改公开依赖面:只看 `public.py`
|
|
1003
|
+
- 改官方通用 host:看 `pz_contrib`
|
|
1004
|
+
- 改合同层:最后才看 `pz`
|