plonex 0.0.1a0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. plonex-0.0.1a0/PKG-INFO +1558 -0
  2. plonex-0.0.1a0/README.md +1508 -0
  3. plonex-0.0.1a0/pyproject.toml +142 -0
  4. plonex-0.0.1a0/pyproject.toml.orig +122 -0
  5. plonex-0.0.1a0/src/plonex/__init__.py +1 -0
  6. plonex-0.0.1a0/src/plonex/_logger.py +49 -0
  7. plonex-0.0.1a0/src/plonex/base.py +969 -0
  8. plonex-0.0.1a0/src/plonex/cli/__init__.py +231 -0
  9. plonex-0.0.1a0/src/plonex/cli/__main__.py +4 -0
  10. plonex-0.0.1a0/src/plonex/cli/aliases.py +96 -0
  11. plonex-0.0.1a0/src/plonex/cli/commands.py +134 -0
  12. plonex-0.0.1a0/src/plonex/cli/cron.py +122 -0
  13. plonex-0.0.1a0/src/plonex/cli/db.py +87 -0
  14. plonex-0.0.1a0/src/plonex/cli/dependencies.py +106 -0
  15. plonex-0.0.1a0/src/plonex/cli/describe.py +52 -0
  16. plonex-0.0.1a0/src/plonex/cli/job_view.py +39 -0
  17. plonex-0.0.1a0/src/plonex/cli/jobs.py +355 -0
  18. plonex-0.0.1a0/src/plonex/cli/options.py +28 -0
  19. plonex-0.0.1a0/src/plonex/cli/parser.py +203 -0
  20. plonex-0.0.1a0/src/plonex/cli/runtime.py +269 -0
  21. plonex-0.0.1a0/src/plonex/cli/setup_info.py +529 -0
  22. plonex-0.0.1a0/src/plonex/cli/shell.py +340 -0
  23. plonex-0.0.1a0/src/plonex/cli/testing.py +129 -0
  24. plonex-0.0.1a0/src/plonex/config.py +165 -0
  25. plonex-0.0.1a0/src/plonex/configuration.py +164 -0
  26. plonex-0.0.1a0/src/plonex/environment.py +47 -0
  27. plonex-0.0.1a0/src/plonex/files.py +73 -0
  28. plonex-0.0.1a0/src/plonex/inspection.py +211 -0
  29. plonex-0.0.1a0/src/plonex/preparation/__init__.py +1 -0
  30. plonex-0.0.1a0/src/plonex/preparation/dependencies.py +348 -0
  31. plonex-0.0.1a0/src/plonex/preparation/engine.py +263 -0
  32. plonex-0.0.1a0/src/plonex/preparation/supervisor.py +375 -0
  33. plonex-0.0.1a0/src/plonex/presentation.py +52 -0
  34. plonex-0.0.1a0/src/plonex/services/__init__.py +41 -0
  35. plonex-0.0.1a0/src/plonex/services/adduser/__init__.py +48 -0
  36. plonex-0.0.1a0/src/plonex/services/compile/__init__.py +24 -0
  37. plonex-0.0.1a0/src/plonex/services/cron/__init__.py +357 -0
  38. plonex-0.0.1a0/src/plonex/services/cron/browser.py +114 -0
  39. plonex-0.0.1a0/src/plonex/services/cron/configure.zcml +15 -0
  40. plonex-0.0.1a0/src/plonex/services/cron/installation.py +56 -0
  41. plonex-0.0.1a0/src/plonex/services/cron/model.py +226 -0
  42. plonex-0.0.1a0/src/plonex/services/cron/plone_runner.py +48 -0
  43. plonex-0.0.1a0/src/plonex/services/cron/profiles/default/controlpanel.xml +8 -0
  44. plonex-0.0.1a0/src/plonex/services/cron/runner.py +303 -0
  45. plonex-0.0.1a0/src/plonex/services/cron/runtime.py +449 -0
  46. plonex-0.0.1a0/src/plonex/services/describe/__init__.py +452 -0
  47. plonex-0.0.1a0/src/plonex/services/describe/formatting.py +180 -0
  48. plonex-0.0.1a0/src/plonex/services/describe/report.py +819 -0
  49. plonex-0.0.1a0/src/plonex/services/describe/templates/plonex_description.md.j2 +124 -0
  50. plonex-0.0.1a0/src/plonex/services/describe/templates/report.html.j2 +47 -0
  51. plonex-0.0.1a0/src/plonex/services/directory/__init__.py +21 -0
  52. plonex-0.0.1a0/src/plonex/services/doctor/__init__.py +751 -0
  53. plonex-0.0.1a0/src/plonex/services/edit/__init__.py +184 -0
  54. plonex-0.0.1a0/src/plonex/services/edit/tui.py +85 -0
  55. plonex-0.0.1a0/src/plonex/services/init/__init__.py +119 -0
  56. plonex-0.0.1a0/src/plonex/services/init/templates/plonex.yml.j2 +87 -0
  57. plonex-0.0.1a0/src/plonex/services/install/__init__.py +994 -0
  58. plonex-0.0.1a0/src/plonex/services/install/preview.py +160 -0
  59. plonex-0.0.1a0/src/plonex/services/install/report.py +120 -0
  60. plonex-0.0.1a0/src/plonex/services/invocation.py +119 -0
  61. plonex-0.0.1a0/src/plonex/services/legacy/__init__.py +146 -0
  62. plonex-0.0.1a0/src/plonex/services/profile/__init__.py +74 -0
  63. plonex-0.0.1a0/src/plonex/services/pytest/__init__.py +28 -0
  64. plonex-0.0.1a0/src/plonex/services/registry.py +88 -0
  65. plonex-0.0.1a0/src/plonex/services/robotserver/__init__.py +32 -0
  66. plonex-0.0.1a0/src/plonex/services/robottest/__init__.py +38 -0
  67. plonex-0.0.1a0/src/plonex/services/runwsgi/__init__.py +102 -0
  68. plonex-0.0.1a0/src/plonex/services/runwsgi/templates/wsgi.ini.j2 +80 -0
  69. plonex-0.0.1a0/src/plonex/services/snapshot/__init__.py +532 -0
  70. plonex-0.0.1a0/src/plonex/services/sources/__init__.py +789 -0
  71. plonex-0.0.1a0/src/plonex/services/sources/installation.py +204 -0
  72. plonex-0.0.1a0/src/plonex/services/sources/testing.py +71 -0
  73. plonex-0.0.1a0/src/plonex/services/supervisor/__init__.py +250 -0
  74. plonex-0.0.1a0/src/plonex/services/supervisor/templates/program.conf.j2 +7 -0
  75. plonex-0.0.1a0/src/plonex/services/supervisor/templates/supervisord.conf.j2 +25 -0
  76. plonex-0.0.1a0/src/plonex/services/template/__init__.py +94 -0
  77. plonex-0.0.1a0/src/plonex/services/upgrade/__init__.py +197 -0
  78. plonex-0.0.1a0/src/plonex/services/zconsole/__init__.py +40 -0
  79. plonex-0.0.1a0/src/plonex/services/zeoserver/__init__.py +105 -0
  80. plonex-0.0.1a0/src/plonex/services/zeoserver/backup.py +198 -0
  81. plonex-0.0.1a0/src/plonex/services/zeoserver/templates/zeo.conf.j2 +21 -0
  82. plonex-0.0.1a0/src/plonex/services/zope_configuration/templates/__init__.py +0 -0
  83. plonex-0.0.1a0/src/plonex/services/zope_configuration/templates/site.zcml.j2 +27 -0
  84. plonex-0.0.1a0/src/plonex/services/zope_configuration/templates/zope.conf.j2 +34 -0
  85. plonex-0.0.1a0/src/plonex/services/zopetest/__init__.py +42 -0
@@ -0,0 +1,1558 @@
1
+ Metadata-Version: 2.3
2
+ Name: plonex
3
+ Version: 0.0.1a0
4
+ Summary: Deploy a manage Plone site based on some configuration files
5
+ Keywords: plone,deployment
6
+ Author: Alessandro Pisa
7
+ Author-email: Alessandro Pisa <pisa@syslab.com>
8
+ License: GPL-3.0
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Programming Language :: Python :: 3.15
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Dist: doit>=0.36,<0.37
21
+ Requires-Dist: argcomplete
22
+ Requires-Dist: jinja2
23
+ Requires-Dist: packaging
24
+ Requires-Dist: pip-requirements-parser
25
+ Requires-Dist: pygments>=2.13,<3
26
+ Requires-Dist: pyyaml
27
+ Requires-Dist: requests
28
+ Requires-Dist: rich
29
+ Requires-Dist: rich-argparse
30
+ Requires-Dist: sh
31
+ Requires-Dist: types-pyyaml
32
+ Requires-Dist: types-requests
33
+ Requires-Dist: types-setuptools
34
+ Requires-Dist: mypy ; extra == 'test'
35
+ Requires-Dist: pytest ; extra == 'test'
36
+ Requires-Dist: pytest-cov ; extra == 'test'
37
+ Requires-Dist: supervisor ; extra == 'test'
38
+ Requires-Dist: huey[stats]>=3.5,<4 ; extra == 'test'
39
+ Requires-Dist: zodb ; extra == 'test'
40
+ Maintainer: Alessandro Pisa
41
+ Maintainer-email: Alessandro Pisa <pisa@syslab.com>
42
+ Requires-Python: >=3.11
43
+ Project-URL: Homepage, https://github.com/collective/plonex
44
+ Project-URL: Documentation, https://plonex.readthedocs.io/
45
+ Project-URL: Repository, https://github.com/collective/plonex
46
+ Project-URL: Issues, https://github.com/collective/plonex/issues
47
+ Project-URL: Changelog, https://github.com/collective/plonex/blob/main/CHANGES.md
48
+ Provides-Extra: test
49
+ Description-Content-Type: text/markdown
50
+
51
+ # plonex
52
+
53
+ Plone deployment CLI.
54
+
55
+ `plonex` is a helper tool designed to simplify everyday project operations with Plone.
56
+
57
+ Read the [documentation](https://plonex.readthedocs.io/) for guides and reference material.
58
+
59
+ > [!NOTE]
60
+ > At the moment `plonex` is in early development and may have breaking changes. Use with caution and report issues.
61
+
62
+ ## Quickstart
63
+
64
+ If you just want to test `plonex`, you can initialize a new project and run the ZEO server and client with these commands:
65
+
66
+ ```sh
67
+ plonex project init myproject
68
+ cd myproject
69
+ plonex supervisor start
70
+ ```
71
+
72
+ The generated configuration starts ZEO and the WSGI client under Supervisor in
73
+ the background. Use `plonex supervisor status` to check them. Runtime files are
74
+ generated from your YAML configuration.
75
+
76
+ Your site will be available at `http://localhost:8080`.
77
+
78
+ ## The magic behind the scenes
79
+
80
+ When you run a command, `plonex` does more than simply spawn a process:
81
+
82
+ - it finds the correct project root (a parent folder containing a file named `etc/plonex.yml`)
83
+ - it loads and merges configuration from all supported files
84
+ - it prepares temporary/generated config files when needed
85
+ - it exports environment variables defined in configuration
86
+ - it runs the appropriate executable from your virtualenv
87
+
88
+ This means commands are reproducible and configuration-driven.
89
+ You change behavior in YAML files (or CLI flags), and `plonex` applies that
90
+ consistently across services.
91
+
92
+ ## Command layout
93
+
94
+ Use `plonex RESOURCE ACTION [options]` across the CLI:
95
+
96
+ ```sh
97
+ plonex project compile
98
+ plonex sources list
99
+ plonex sources update
100
+ plonex dependencies install --update-sources
101
+ plonex dependencies sync
102
+ plonex instance debug
103
+ plonex db backup
104
+ plonex test pytest my.package -k login
105
+ ```
106
+
107
+ `project`, `dependencies`, `sources`, `instance`, `supervisor`, `db`, `cron`, and `test`
108
+ provide command families. `shell` and `options` provide interactive access and
109
+ inspection. Each operation has one built-in spelling. Removed shortcuts and flags are listed
110
+ in the [CLI structure and migration guide](docs/cli.md). Bare command families show help.
111
+
112
+ ## Autocomplete
113
+
114
+ Add this to your shell startup file:
115
+
116
+ ```sh
117
+ eval "$(register-python-argcomplete plonex)"
118
+ ```
119
+
120
+ ## Interactive shell
121
+
122
+ Start a prompt for your current project with `plonex shell`, or choose a project:
123
+
124
+ ```sh
125
+ plonex shell --target /path/to/project
126
+ ```
127
+
128
+ Enter the same commands and arguments you use at the command line, without the
129
+ `plonex` prefix:
130
+
131
+ ```text
132
+ plonex[myproject]> supervisor status
133
+ plonex[myproject]> dependencies sync
134
+ plonex[myproject]> sources list "plone.*"
135
+ plonex[myproject]> test robot tests/robot/example.robot --test "Login works"
136
+ plonex[myproject]> help dependencies
137
+ plonex[myproject]> exit
138
+ ```
139
+
140
+ - Tab completes commands, subcommands, flags, and paths when readline is available.
141
+ - Up/Down recall commands from this project's current and previous sessions.
142
+ - `history` lists remembered commands. History is stored in
143
+ `var/plonex-shell-history` under the target project with owner-only permissions.
144
+ Commands with recognizable credentials are omitted, but avoid placing secrets
145
+ in command arguments: detection cannot recognize every possible secret.
146
+ - `help` lists commands; `help COMMAND` and `COMMAND --help` show command help.
147
+ - `exit`, `quit`, or Ctrl-D leave the shell. Ctrl-C cancels the current input or
148
+ interrupts a running command and returns to the prompt.
149
+ - In an interactive terminal, Ctrl-Z returns to the prompt **without stopping the
150
+ current command**. The server stays available for requests and upgrades.
151
+ - Blank lines and lines beginning with `#` are ignored.
152
+ - Quoting, escaped spaces, and `--` work as they do in the CLI. Arguments are
153
+ passed directly to plonex; pipes, redirects, variable expansion, and wildcard
154
+ expansion are not interpreted by this prompt.
155
+
156
+ The shell uses the selected project as its working directory. Each command runs
157
+ in a fresh process using the same Python installation as plonex, so configuration
158
+ is reloaded and command-specific environment variables do not leak into later
159
+ commands. Errors leave the prompt open.
160
+
161
+ Startup `--verbose` or `--quiet` flags become session defaults. An explicit `-v`
162
+ or `-q` on a command overrides those defaults for that command. To run a command
163
+ against another project without changing the session target, use
164
+ `--target /other/project COMMAND`. The optional `plonex` prefix is also accepted.
165
+
166
+ ### Keeping a foreground instance running
167
+
168
+ You can return to the prompt to run an upgrade, then reconnect to the same
169
+ instance:
170
+
171
+ ```text
172
+ plonex[myproject]> instance debug
173
+ ... instance starts; press Ctrl-Z ...
174
+ plonex[myproject]> db upgrade
175
+ plonex[myproject]> fg
176
+ ... back to the same instance and its live output ...
177
+ ```
178
+
179
+ The shell keeps detached commands running and records their output under
180
+ `var/log/shell/` (or your configured `var_folder`). Background logs do not print
181
+ over the prompt. On reconnect, the shell shows up to 64 KiB of output you missed,
182
+ then streams live output. Full logs remain on disk.
183
+
184
+ - `jobs`: show a Markdown-style table of job IDs, service names, coloured states,
185
+ process IDs, commands, and log locations. Running jobs are green, successful
186
+ exits cyan, failures red, and signal exits yellow. Terminal colour settings,
187
+ including `NO_COLOR`, are respected.
188
+ - `jobs --markdown`: export a plain Markdown table without ANSI colour codes.
189
+ - `fg` or `attach`: reconnect to the most recent running job.
190
+ - `fg %1` or `attach %1`: reconnect to a specific job.
191
+ - `logs %1`: show that job's latest output without reconnecting.
192
+ - `options %1`: inspect the resolved service options captured when that job started.
193
+ - `bg %1`: leave or resume a job in the background.
194
+ - `kill %1`: stop the job and its foreground process group.
195
+ - `instance debug &`: start an instance directly in the background. A trailing, unquoted
196
+ `&` preceded by a space works with other plonex commands too.
197
+
198
+ Job IDs also accept plain numbers. Commands that take a job ID default to the
199
+ most recent running job. `instance debug --port 8081` starts another instance.
200
+ Ctrl-C while attached stops the foreground command; Ctrl-Z detaches it.
201
+ Exiting the shell stops jobs it owns, including detached instances. Job control
202
+ requires an interactive terminal; piped shell commands retain their usual behavior.
203
+
204
+ ### Inspecting service and job options
205
+
206
+ Preview a service using the same defaults, merged configuration, aliases, and
207
+ argument handling as normal execution:
208
+
209
+ ```sh
210
+ plonex options instance debug --port 8081
211
+ plonex options instance start --name worker --config etc/worker.yml
212
+ plonex options test pytest my.package -k login
213
+ plonex options --markdown dependencies sync
214
+ ```
215
+
216
+ The report includes service constructor arguments, command flags, configuration
217
+ file locations, and resolved options, including nested environment settings.
218
+ The service is constructed but not entered or run: inspection does not launch
219
+ the service, install dependencies, or render runtime files. Existing constructors
220
+ may create directories, and normal configuration resolution still applies,
221
+ including fetching remote Git profiles when configured.
222
+
223
+ In the shell, use a job ID to see its **startup snapshot**, or a command to
224
+ preview **current configuration**:
225
+
226
+ ```text
227
+ plonex[myproject]> jobs
228
+ plonex[myproject]> options %1
229
+ plonex[myproject]> options %1 --markdown
230
+ plonex[myproject]> options instance debug --port 8082
231
+ ```
232
+
233
+ Job snapshots are captured by the service process before it exports environment
234
+ variables or runs helper services. They remain available after the job exits and
235
+ do not change when YAML files are edited. A snapshot may be unavailable while a
236
+ job is starting, or if it fails before entering its service. Help/version jobs
237
+ do not have service options.
238
+
239
+ Inspection masks conventional password, token, and secret keys, known credential
240
+ arguments, and passwords in URLs. Snapshots are stored with owner-only file
241
+ permissions next to job logs. Ordinary process output in log files is unchanged.
242
+
243
+ ## Project aliases
244
+
245
+ Define shortcuts in `etc/plonex.yml`:
246
+
247
+ ```yaml
248
+ aliases:
249
+ start: supervisor start
250
+ stop: supervisor stop
251
+ test_whatever: pytest my.package
252
+ test_unit: [pytest, my.package, -k, "not integration"]
253
+ ```
254
+
255
+ Then run them as ordinary commands:
256
+
257
+ ```sh
258
+ plonex start
259
+ plonex test_whatever -k login -x
260
+ plonex --target /path/to/project test_unit -q
261
+ ```
262
+
263
+ An alias is one command, expressed as a quoted command string or a list of
264
+ argument tokens. Extra arguments are appended to its expansion. In the examples,
265
+ `test_whatever -k login -x` runs `pytest my.package -k login -x`. Values may
266
+ optionally start with `plonex`. Quotes preserve arguments containing spaces;
267
+ commands are not evaluated as shell code.
268
+
269
+ Aliases can refer to other aliases, but loops produce an error. Built-in command
270
+ names and shell commands such as `help`, `history`, `exit`, `jobs`, and `attach` cannot be replaced.
271
+ Aliases appear in project help and Tab completion, work in `plonex shell`, and can
272
+ be used by `default_action` or `default_actions`. Edits take effect on the next
273
+ shell command or completion.
274
+
275
+ Aliases use the same profile and local configuration precedence as other
276
+ options. To extend or override individual aliases inherited from a profile, use
277
+ the existing `+` mapping syntax:
278
+
279
+ ```yaml
280
+ +aliases:
281
+ test_whatever: pytest my.other_package
282
+ ```
283
+
284
+ ## Global options
285
+
286
+ All commands accept these options:
287
+
288
+ ```text
289
+ plonex [--target PATH] [--verbose] [--quiet] [--version] <command> ...
290
+ ```
291
+
292
+ - `-t, --target`: target project folder (defaults to current directory).
293
+ - `-v, --verbose`: set log level to `DEBUG`.
294
+ - `-q, --quiet`: reduce log output (`WARNING` and above).
295
+ - `-V, --version`: print installed plonex version.
296
+
297
+ For all commands except `init`, `plonex` resolves the target by walking upward until it finds `etc/plonex.yml`.
298
+
299
+ If you run `plonex` without a command, it displays the help message unless a
300
+ default action is configured in `etc/plonex.yml`.
301
+
302
+ ## How to think about plonex
303
+
304
+ `plonex` separates intent from implementation.
305
+ You declare intent with simple commands such as `plonex instance start` or
306
+ `plonex db pack`, and `plonex` translates that into the correct low-level command,
307
+ with the correct paths, environment variables, and generated configuration files.
308
+
309
+ This approach has a few practical benefits:
310
+
311
+ - You do not need to remember long binary paths or many flags.
312
+ - Team members can share the same commands while keeping local overrides in YAML.
313
+ - Automation in CI can reuse exactly the same command set used locally.
314
+
315
+ In other words, `plonex` behaves like an orchestration layer over project runtime
316
+ tools, while still letting you control details through configuration.
317
+
318
+ You can still see what happens when you run a command passing the `-v/--verbose` flag or setting `log_level: debug` in YAML.
319
+ This is useful for troubleshooting and understanding the underlying behavior.
320
+
321
+ ## Default actions
322
+
323
+ You can configure what happens when you run `plonex` with no explicit command.
324
+
325
+ By default, `plonex` shows the command help.
326
+
327
+ If you want a different behavior, set either `default_action` or
328
+ `default_actions` in `etc/plonex.yml`.
329
+
330
+ For example, to ensure supervisor is started and then run WSGI client is run in the foreground, you can set:
331
+
332
+ ```yaml
333
+ default_actions:
334
+ - supervisor start
335
+ - supervisor status
336
+ - instance debug
337
+ ```
338
+
339
+ ## Profiles
340
+
341
+ You can extend a project from one or more profiles declared in `etc/plonex.yml`.
342
+
343
+ Example:
344
+
345
+ ```yaml
346
+ profiles:
347
+ - /path/to/plonex-profile
348
+ - https://github.com/example/plonex-profile.git
349
+
350
+ http_port: 8081
351
+ ```
352
+
353
+ Profile configuration is loaded before the local project configuration, so the
354
+ local `etc/plonex.yml` remains the highest-precedence place for site-specific
355
+ overrides such as ports, hostnames, and service settings.
356
+
357
+ Profiles can themselves declare `profiles`, which allows a profile to extend
358
+ another profile before the local project overrides both.
359
+
360
+ Relative profile paths in the project `etc/plonex.yml` are resolved from the
361
+ project root. Relative profile paths declared inside a profile are resolved from
362
+ that profile root.
363
+
364
+ One practical way to organize profiles inside a repository is:
365
+
366
+ ```text
367
+ myproject/
368
+ ├── etc/
369
+ │ └── plonex.yml
370
+ └── profiles/
371
+ ├── default/
372
+ │ └── etc/
373
+ │ └── plonex.yml
374
+ ├── production/
375
+ │ └── etc/
376
+ │ └── plonex.yml
377
+ └── development/
378
+ └── etc/
379
+ └── plonex.yml
380
+ ```
381
+
382
+ For example, `profiles/default/etc/plonex.yml` can hold shared defaults used by
383
+ every environment:
384
+
385
+ ```yaml
386
+ plone_version: 6.2.0
387
+ http_address: 0.0.0.0
388
+ zeo_address: var/zeo.sock
389
+ environment_vars:
390
+ TZ: Europe/Rome
391
+ ```
392
+
393
+ Then `profiles/production/etc/plonex.yml` can extend `default` and add
394
+ production-specific values:
395
+
396
+ ```yaml
397
+ profiles:
398
+ - ../default
399
+
400
+ http_port: 8080
401
+ debug_mode: false
402
+ ```
403
+
404
+ And `profiles/development/etc/plonex.yml` can also extend `default` but keep
405
+ more developer-friendly settings:
406
+
407
+ ```yaml
408
+ profiles:
409
+ - ../default
410
+
411
+ http_port: 8081
412
+ debug_mode: true
413
+ log_level: debug
414
+ ```
415
+
416
+ Finally, the project `etc/plonex.yml` chooses which profile to inherit from and
417
+ keeps the final local overrides:
418
+
419
+ ```yaml
420
+ profiles:
421
+ - profiles/development
422
+
423
+ http_port: 8082
424
+ ```
425
+
426
+ In this example the resulting `http_port` is `8082`, because the local
427
+ `etc/plonex.yml` overrides `profiles/development`, which overrides
428
+ `profiles/default`.
429
+
430
+ ## Base Constraints
431
+
432
+ `plonex` generates a merged `var/constraints.txt` for installation.
433
+
434
+ By default it starts from the Plone release constraints selected by
435
+ `plone_version`. The generated `etc/plonex.yml` does not include a
436
+ `plonex_base_constraint` entry unless you decide to add one.
437
+
438
+ Default behavior:
439
+
440
+ ```yaml
441
+ plone_version: 6.2.0
442
+ ```
443
+
444
+ This means the base constraint is effectively:
445
+
446
+ ```yaml
447
+ https://dist.plone.org/release/{{ plone_version }}/constraints.txt
448
+ ```
449
+
450
+ If you want to override that base, add
451
+ `plonex_base_constraint`.
452
+
453
+ Supported values are:
454
+
455
+ - remote URLs
456
+ - local files relative to the project root
457
+ - `resource://package:path` references
458
+
459
+ Examples:
460
+
461
+ ```yaml
462
+ plonex_base_constraint: https://example.org/constraints/base.txt
463
+ ```
464
+
465
+ ```yaml
466
+ plonex_base_constraint: etc/constraints/base.txt
467
+ ```
468
+
469
+ ```yaml
470
+ plonex_base_constraint: resource://my.package:constraints/base.txt
471
+ ```
472
+
473
+ Use a plain URL in YAML, without Markdown link syntax. For GitHub files, copy the
474
+ **Raw** URL from `raw.githubusercontent.com`, rather than the `/blob/` page URL.
475
+ Remote constraints, including nested `-c` and `-r` references, are merged into
476
+ `var/constraints.txt`. Local pins override remote pins, and packages installed in
477
+ editable mode are excluded. HTML responses and invalid remote requirements cause
478
+ a warning and stop generation, preserving the previous constraints file.
479
+
480
+ Generated package names use lowercase and replace runs of dots, underscores, or
481
+ hyphens with a single hyphen: `zope.sendmail==7.2` becomes
482
+ `zope-sendmail==7.2`. This also applies to suggested and saved pins, added packages,
483
+ and direct `pip_requirements`. Versions, environment markers, paths, and URLs keep
484
+ their original spelling.
485
+
486
+ > [!NOTE]
487
+ > If an old `etc/constraints.d/000-plonex.txt` file still exists, `plonex` warns
488
+ > that it is ignored and suggests removing it. For compatibility, if neither
489
+ > `plone_version` nor `plonex_base_constraint` is configured, `plonex` still tries
490
+ > to infer `plone_version` from that legacy file.
491
+
492
+ Single action examples:
493
+
494
+ ```yaml
495
+ default_action: project describe
496
+ ```
497
+
498
+ ```yaml
499
+ default_action: instance start
500
+ ```
501
+
502
+ Equivalent list form:
503
+
504
+ ```yaml
505
+ default_action:
506
+ - instance start
507
+ ```
508
+
509
+ If you want to run multiple commands in sequence, use `default_actions`:
510
+
511
+ ```yaml
512
+ default_actions:
513
+ - supervisor start
514
+ - instance start
515
+ ```
516
+
517
+ This is useful when you want plain `plonex` to bootstrap a common local
518
+ workflow, for example starting Supervisor first and then bringing up the client.
519
+
520
+ Notes:
521
+
522
+ - `default_actions` runs in order.
523
+ - each action can be written as a shell-like string or a list of tokens.
524
+ - command-line global options such as `-v` and `-q` still apply.
525
+
526
+ ## Development FAQ
527
+
528
+ ### How do I start a new project?
529
+
530
+ ```sh
531
+ plonex project init myproject
532
+ cd myproject
533
+ plonex supervisor start
534
+ plonex supervisor status
535
+ ```
536
+
537
+ Supervisor starts the generated ZEO and WSGI programs in the background. For
538
+ interactive development, the shell workflow below gives direct debugger input.
539
+
540
+ ### How do I run Plone with a debugger and still run commands?
541
+
542
+ ```text
543
+ plonex shell
544
+ plonex[myproject]> db start &
545
+ plonex[myproject]> instance debug
546
+ ... press Ctrl-Z to return to the prompt ...
547
+ plonex[myproject]> options %2
548
+ plonex[myproject]> db upgrade
549
+ plonex[myproject]> fg %2
550
+ ```
551
+
552
+ Ctrl-Z detaches without stopping the server. `jobs` lists the actual IDs;
553
+ `logs %2` reads output and `kill %2` stops that job. `fg` only reattaches jobs;
554
+ start new instances with `instance debug`. Exiting the shell stops its jobs.
555
+ Use an already-running ZEO server instead of starting a second one when your
556
+ project is configured with a shared database.
557
+
558
+ ### How do I develop an add-on from a checkout?
559
+
560
+ Declare its repository and editable installation together:
561
+
562
+ ```yaml
563
+ sources:
564
+ my.addon:
565
+ repo: git@github.com:example/my.addon.git
566
+ rev: main
567
+ install: true
568
+ extras: [test]
569
+ ```
570
+
571
+ ```sh
572
+ plonex sources clone
573
+ plonex dependencies install
574
+ plonex sources list
575
+ ```
576
+
577
+ Editable installs use the checkout directly. A monorepo can add `subdirectory`;
578
+ `path` selects a custom checkout folder. Remove corresponding manual `-e` entries
579
+ when migrating. Distribution names such as `my-addon` are canonicalized, while
580
+ Python import names such as `my.addon` retain their dots.
581
+
582
+ ### Why do Python changes not appear in the application?
583
+
584
+ Check both the declaration and actual installation:
585
+
586
+ ```sh
587
+ plonex project describe sources
588
+ plonex project describe dependencies
589
+ ```
590
+
591
+ The source should have `install: true`, and its installed metadata should identify
592
+ the expected checkout as editable. A running process can still hold imported
593
+ Python modules; restart the instance after code changes when required. Dependency,
594
+ extra, or packaging changes need another `dependencies install`. An ordinary
595
+ `editable: false` installation contains built code rather than live source files.
596
+
597
+ ### How do I update dependencies and remove obsolete packages?
598
+
599
+ ```sh
600
+ plonex sources update
601
+ plonex dependencies install
602
+ plonex dependencies sync
603
+ plonex dependencies outdated
604
+ ```
605
+
606
+ `install` preserves unrelated installed packages. `sync` resolves the declared
607
+ requirements and removes packages outside that environment. To combine checkout
608
+ updates with installation, use `dependencies install --update-sources`.
609
+ Output reports actual changes; an unchanged run prints `No package changes.`
610
+
611
+ ### Where should additional requirements and pins go?
612
+
613
+ Use `pip_requirements` or `etc/requirements.d/*.txt` for requirements and
614
+ `etc/constraints.d/*.txt` for pins. These can also come from profiles.
615
+ When constraint fragments in the same directory pin the same package, the first
616
+ filename in alphabetical order takes precedence. Project constraints take
617
+ precedence over profile constraints.
618
+ `dependencies add 'my-addon[test]'` records a requirement and installs it.
619
+ Save missing pins with `dependencies install --persist-local`; the local pin file
620
+ stays separate from shared constraints. Use a raw text URL for
621
+ `plonex_base_constraint`, rather than a GitHub `/blob/` HTML page.
622
+
623
+ ### Which file set this option, and why did it change?
624
+
625
+ ```sh
626
+ plonex project describe configuration --key http_port
627
+ plonex project describe configuration --key http_port instance debug --port 8081
628
+ plonex project describe analytics instance debug
629
+ plonex project describe profiles
630
+ ```
631
+
632
+ The configuration report includes the complete compiled YAML, resolved values,
633
+ winning definitions, earlier values, and Jinja references. In the HTML report,
634
+ select a YAML key to open its definition history and links to the source files.
635
+ Without a command, the YAML uses the same configuration as `project compile`;
636
+ adding a command shows that command's defaults, service-specific files, extra
637
+ files, and CLI overrides. Analytics shows override chains and configuration
638
+ dependencies. Sensitive fields and derived secrets are redacted in every format.
639
+ Use `--format json` for automation.
640
+
641
+ ### How do I keep personal settings out of a shared profile?
642
+
643
+ Put machine-specific overrides in `etc/plonex.local.yml`. Mappings and lists
644
+ normally replace earlier values. `+sources` adds or replaces complete source
645
+ entries, and `+pip_requirements` appends requirements. A source entry is itself
646
+ replaced, so include its repository and other settings when overriding it.
647
+ Use `project describe configuration --key sources` to inspect the result.
648
+
649
+ ### How do I open a useful project report?
650
+
651
+ ```sh
652
+ plonex project describe --format html --browse
653
+ plonex project describe runtime --browse
654
+ plonex project describe jobs --browse
655
+ ```
656
+
657
+ The complete report is `var/plonex_description/index.html`. It has responsive
658
+ layout, section navigation, search, file links, and definition history. Markdown
659
+ and JSON exports sit alongside it. Focused reports use their section name and
660
+ preserve the complete report. Existing file paths in the HTML report are clickable,
661
+ including configuration history. Open the report on the project host: browsers may
662
+ block local file links from a web server, and they decide whether to display or
663
+ download each file. Inspection does not install, compile, export the configured
664
+ environment, or start application services.
665
+
666
+ ### Why is my project not working as expected?
667
+
668
+ ```sh
669
+ plonex project doctor
670
+ plonex project doctor --format json
671
+ plonex supervisor start --plan
672
+ ```
673
+
674
+ The doctor reports configuration, virtualenv, source installation, constraint,
675
+ preparation, and local service problems with suggested next steps. It does not
676
+ install packages or change the project. Use `--strict` to make warnings fail a CI
677
+ check. See the [doctor reference](docs/cli.md#project-doctor) for its scope and
678
+ limitations.
679
+
680
+ ### How do I compare or recreate an environment?
681
+
682
+ ```sh
683
+ plonex project snapshot create --output deployment.json
684
+ plonex project snapshot compare deployment.json
685
+ plonex project snapshot prepare deployment.json --to /path/to/new-venv
686
+ ```
687
+
688
+ Snapshots capture installed versions, source revisions, and dependency inputs.
689
+ Preparation verifies supported inputs before creating a separate environment.
690
+ See [project snapshots](docs/snapshots.md) for limitations and reproducibility.
691
+
692
+ ### Can I inspect an existing mxdev or Buildout project?
693
+
694
+ Run `plonex --target /path/to/project project inspect-legacy`. The report reads
695
+ local configuration without running recipes or modifying the project. The
696
+ [migration guide](docs/migration.md) covers Buildout and mxdev projects, including
697
+ how to keep mxdev in charge of its generated files during a transition.
698
+
699
+ ### How do I run tests and stop in a failing test?
700
+
701
+ ```sh
702
+ plonex dependencies add pytest
703
+ plonex test pytest my.addon -k login -x --pdb
704
+ plonex test zope my.addon -t Login
705
+ plonex test robot tests/robot/login.robot --browser firefox
706
+ ```
707
+
708
+ Install the add-on's testing extras and the runner it needs. Use Python import
709
+ names for package-based tests. Arguments after the package in `test pytest`
710
+ go to pytest, including `-k`, `-x`, and `--pdb`. The shell preserves terminal input
711
+ for interactive debugging.
712
+
713
+ ### How do I run a script or inspect the ZODB?
714
+
715
+ ```sh
716
+ plonex instance run -- scripts/maintenance.py --argument value
717
+ plonex instance console
718
+ ```
719
+
720
+ The console provides `app`. To establish a Plone site context:
721
+
722
+ ```python
723
+ from Testing.makerequest import makerequest
724
+ from zope.component.hooks import setSite
725
+ from zope.globalrequest import setRequest
726
+
727
+ app = makerequest(app)
728
+ setRequest(app.REQUEST)
729
+ site = app["Plone"]
730
+ setSite(site)
731
+ ```
732
+
733
+ Use `plone.api.env.adopt_user(username="your-user")` for an explicit application
734
+ user context when needed. Commit intended database changes with
735
+ `transaction.commit()`; abort discarded changes with `transaction.abort()`.
736
+ See [Plone's console documentation](https://6.docs.plone.org/admin-guide/run-plone.html#run-a-debug-console).
737
+
738
+ ### How do I run upgrade steps?
739
+
740
+ Run `plonex db upgrade` with the ZEO server running. Install and enable
741
+ `collective.ftw.upgrade` in the target project and site. Plonex starts a temporary
742
+ local instance with private authentication, runs the upgrade, and stops it. Output
743
+ is recorded in `var/log/upgrade.logs`.
744
+
745
+ To use an already running instance instead, pass
746
+ `--url http://127.0.0.1:8081/` and configure its upgrade authentication. Review
747
+ and back up the database before planned deployment upgrades.
748
+
749
+ ### Why does Supervisor startup prepare dependencies and templates?
750
+
751
+ `plonex supervisor start` ensures that declared dependencies and generated runtime
752
+ files are current before starting Supervisor. It skips unchanged work. Requirement
753
+ and constraint changes trigger installation checks; changing an HTTP port only
754
+ regenerates affected runtime files.
755
+
756
+ ```sh
757
+ plonex supervisor start --plan
758
+ plonex project describe preparation
759
+ ```
760
+
761
+ The plan explains stale tasks and their prerequisites without executing them.
762
+ Preparation never implicitly updates Git sources, removes unused packages, runs
763
+ Plone upgrades, or restarts existing instances. Use `dependencies sync` for package
764
+ removal and explicit Supervisor commands to apply changes to running services.
765
+ See [incremental preparation](docs/preparation.md) for caching, failures, and
766
+ remote profile behavior.
767
+
768
+ ### How do I apply Supervisor configuration changes?
769
+
770
+ ```sh
771
+ plonex supervisor reread
772
+ plonex supervisor update
773
+ plonex supervisor status
774
+ ```
775
+
776
+ `update` applies changed program declarations. `restart` restarts all managed
777
+ programs; `graceful --interval 2` restarts them sequentially. Neither should be
778
+ confused with editing the currently running process's configuration in memory.
779
+ `options %JOB` reports a shell job's startup snapshot even after YAML changes.
780
+
781
+ ### How can I schedule work without a curl password or a CSRF exemption?
782
+
783
+ ```yaml
784
+ cron_timezone: Europe/Rome
785
+ cron_jobs:
786
+ StoreSessionStats:
787
+ schedule: "15 2 * * *"
788
+ callable: my.addon.jobs:store_session_stats
789
+ site: /Plone
790
+ context: sectors
791
+ user: scheduler
792
+ timeout: 900
793
+ lock: zope
794
+ lock_timeout: 300
795
+ enabled: false
796
+ ```
797
+
798
+ Move the business logic into the callable, installed in the project virtualenv.
799
+ Create the named Plone user with the roles it needs; no password belongs in this
800
+ job declaration. Test with `cron run StoreSessionStats`, inspect `cron logs` and
801
+ `cron history`, then enable the schedule and apply Supervisor configuration.
802
+
803
+ Plonex installs the job runtime and Huey into the target virtualenv
804
+ when jobs are configured. Supervisor launches
805
+ the target interpreter directly. Manual-only job declarations still keep a worker
806
+ for explicitly queued tasks.
807
+
808
+ A protected browser view can call the same function after POST and authenticator
809
+ checks. The job runs through a dedicated ZEO client and owns its transaction;
810
+ it does not invoke the browser view or disable CSRF. See the
811
+ [complete scheduled-job guide](docs/jobs.md), including a protected view example.
812
+
813
+ ### How do I check scheduling times, failures, and overlap?
814
+
815
+ ```sh
816
+ plonex cron list
817
+ plonex cron status
818
+ plonex cron queue
819
+ plonex cron history StoreSessionStats
820
+ plonex cron logs StoreSessionStats --tail 100
821
+ plonex project describe jobs --browse
822
+ ```
823
+
824
+ Schedules use explicit IANA timezones, defaulting to UTC. Common lock names
825
+ serialize related jobs; manual and daemon runs share those locks. Timeouts stop
826
+ the job subprocess group and record the outcome. Retries default to zero; enable
827
+ them only for work that tolerates repeating after a failure. The queue survives
828
+ restarts, but missed periodic schedules are not replayed.
829
+
830
+ ### How do I inspect or retry queued work?
831
+
832
+ ```sh
833
+ plonex cron status
834
+ plonex cron queue
835
+ plonex cron inspect TASK_ID
836
+ plonex cron retry TASK_ID
837
+ plonex cron revoke TASK_ID
838
+ plonex project describe queue
839
+ ```
840
+
841
+ Monitoring uses Huey's statistics extension in the target environment. Task IDs
842
+ link events to individual attempts and logs. `retry` creates a new task using the
843
+ current definition; `revoke` prevents a queued execution and does not kill a
844
+ running process. Add `--format json` for complete task IDs and structured output.
845
+ See the [queue guide](docs/jobs.md#queue-monitoring-and-controls).
846
+
847
+ ### What changed in the command names?
848
+
849
+ There are no built-in synonyms such as both `--sync` and `sync`. Use
850
+ `dependencies sync`, `dependencies outdated`, `instance debug`, and
851
+ `project describe`. Update aliases, default command lists, deployment scripts,
852
+ and Supervisor `command` values from older projects. Configuration identities
853
+ such as `run_for: fg` and `etc/plonex-runwsgi.yml` retain their meaning.
854
+ The [CLI migration table](docs/cli.md#breaking-migration) lists every removed form.
855
+
856
+ ## Typical workflows
857
+
858
+ ### Local development
859
+
860
+ ```sh
861
+ plonex project init .
862
+ plonex supervisor start
863
+ plonex shell
864
+ ```
865
+
866
+ What happens:
867
+
868
+ - `init` creates project skeleton and base config.
869
+ - `init` creates `.venv` and installs the configured dependencies.
870
+ - `supervisor start` starts the configured ZEO and WSGI services in the background.
871
+ - `shell` opens a prompt for project commands. Use `supervisor status` there to
872
+ check managed services. `jobs` lists processes launched by the current shell;
873
+ Supervisor services are managed separately.
874
+
875
+ ### Running maintenance tasks
876
+
877
+ ```sh
878
+ plonex db pack --days N
879
+ plonex db backup
880
+ ```
881
+
882
+ What happens:
883
+
884
+ - `db pack` reads `zeo_address` from merged options and calls `zeopack`.
885
+ - `db backup` runs `repozo` against the project Data.fs location.
886
+
887
+ ### Working with supervisor
888
+
889
+ ```sh
890
+ plonex supervisor start
891
+ plonex supervisor status
892
+ plonex supervisor restart
893
+ plonex supervisor graceful --interval 2.0
894
+ ```
895
+
896
+ What happens:
897
+
898
+ - `plonex` generates `tmp/supervisor/etc/supervisord.conf` and service templates if needed.
899
+ - it runs `supervisord` and `supervisorctl` from your project virtualenv.
900
+ - `plonex supervisor graceful` restarts each configured service one by one in the
901
+ order reported by `supervisorctl status`.
902
+ - `--interval` controls the pause between service restarts. The default interval is `1` second.
903
+
904
+ You can also set the default interval in YAML:
905
+
906
+ ```yaml
907
+ supervisor_graceful_interval: 10
908
+ ```
909
+
910
+ Put that in `etc/plonex.yml` or another merged plonex config file.
911
+ The CLI flag
912
+ still wins over YAML when both are provided.
913
+
914
+ ## Commands
915
+
916
+ ### Setup and info
917
+
918
+ `shell [--target PATH]`
919
+
920
+ - Open the interactive project shell, including aliases, completion, job control,
921
+ logs, and startup option inspection. See [Interactive shell](#interactive-shell).
922
+
923
+ `options [--markdown] <command> [arguments ...]`
924
+
925
+ - Preview effective service arguments and options without running the service.
926
+ - In the interactive shell, `options [JOB]` inspects a recorded job snapshot;
927
+ omitting the job ID selects the most recent running job.
928
+
929
+ `project init [target]`
930
+
931
+ - Initialize project folders and base config.
932
+ - If `target` is omitted, `plonex` prompts for a folder and suggests the current directory as the default.
933
+
934
+ `project compile`
935
+
936
+ - Compile merged configuration into `var/plonex.yml`.
937
+ - If `sources` is configured, compile also generates `var/gitman.yml`.
938
+ - The default checkout location is `src` when `sources_location` is omitted.
939
+
940
+ Example:
941
+
942
+ ```yaml
943
+ sources:
944
+ my.package:
945
+ repo: https://github.com/example/my.package.git
946
+ rev: main
947
+ ```
948
+
949
+ `project describe`
950
+
951
+ - Render a project description from current configuration.
952
+ - Always export responsive HTML, Markdown, and JSON under `var/plonex_description/`.
953
+ - Select a section: `overview`, `configuration`, `analytics`, `profiles`,
954
+ `environment`, `sources`, `dependencies`, `services`, `runtime`, `files`, `jobs`, or `preparation`.
955
+ - `--format terminal|markdown|html|json` controls stdout; `--browse` opens HTML.
956
+ - `configuration --key OPTION [COMMAND ...]` explains definitions and overrides.
957
+ - Inspection does not compile configuration, install packages, or start services.
958
+
959
+ `dependencies {install|sync|outdated} [options]`
960
+
961
+ - Install from merged requirements/constraints.
962
+ - `sync`: compile requirements/constraints and run `uv pip sync` using the compiled requirements file as positional input.
963
+ - `outdated`: run `uv pip list --outdated`. Use this explicit action instead of an installation flag.
964
+ - `-p, --persist`: save auto-detected missing constraints into the project's `etc/constraints.d/`.
965
+ - `--persist-local`: save auto-detected missing constraints into a local (git-ignored) `etc/constraints.d/999-autoinstalled.local.txt`.
966
+ - `--persist-profile`: save auto-detected missing constraints into the first configured profile's `etc/constraints.d/`.
967
+ - `--update-sources`: update sources before installing dependencies (uses Gitman under the hood).
968
+
969
+ Dependency installation prints only package changes and relevant diagnostics:
970
+
971
+ ```text
972
+ Added my-package==1.0
973
+ Upgraded zope-sendmail: 7.2 → 7.3
974
+ Removed unused-package==2.0
975
+ ```
976
+
977
+ An unchanged environment prints `No package changes.` Reinstalls, downgrades,
978
+ and source changes are reported as well. Use `--verbose` to see full uv output.
979
+ The internal package inventory used for checking pins is captured silently;
980
+ missing-pin suggestions and resolver/build failures remain visible.
981
+
982
+ To enable source updates by default in project configuration:
983
+
984
+ ```yaml
985
+ sources_update_before_dependencies: true
986
+ sources:
987
+ my.package:
988
+ repo: https://github.com/example/my.package.git
989
+ rev: main
990
+ ```
991
+
992
+ To install a checkout in the project virtualenv, set `install: true`. Installs
993
+ are editable by default, so Python code changes are available without reinstalling:
994
+
995
+ ```yaml
996
+ sources:
997
+ ploneintranet:
998
+ repo: git@github.com:quaive/ploneintranet.git
999
+ rev: master
1000
+ install: true
1001
+ extras: [test]
1002
+ pas.plugins.oidc:
1003
+ repo: git@github.com:collective/pas.plugins.oidc.git
1004
+ install: true
1005
+ reference.package:
1006
+ repo: https://github.com/example/reference.package.git
1007
+ install: false
1008
+ ```
1009
+
1010
+ Run `plonex sources clone` followed by `plonex dependencies install`, or use
1011
+ `plonex dependencies install --update-sources` to update checkouts and install in one step.
1012
+ `plonex dependencies sync` also supports these selections. Source declarations
1013
+ without `install: true` remain checkout only. `sources list` shows each source's
1014
+ installation selection alongside checkout health; it does not inspect the virtualenv.
1015
+
1016
+ Each selected source also accepts:
1017
+
1018
+ - `extras: [test, debug]`: optional dependency extras, with canonical package naming.
1019
+ - `editable: false`: build and install the local project normally; rerun dependencies
1020
+ after changing its code.
1021
+ - `path: vendor/my-checkout`: override the checkout path, relative to the project
1022
+ (an absolute path is also supported). Otherwise use `sources_location`, default `src`.
1023
+ - `subdirectory: packages/backend`: install a Python project inside a repository.
1024
+ This must be a relative path within the checkout.
1025
+
1026
+ Package names come from static `pyproject.toml`, `setup.cfg`, or literal `setup.py`
1027
+ metadata, falling back to the project directory name. Selected packages are removed
1028
+ from both base and local constraints, so release pins cannot override the local
1029
+ checkout. Missing checkouts and directories without `pyproject.toml` or `setup.py`
1030
+ produce an actionable error before installation. Source updates regenerate the
1031
+ dependency files after reading the updated metadata.
1032
+ uv currently cannot resolve editable checkout paths containing `#` or `?`; move
1033
+ the checkout or use `editable: false` in that case.
1034
+
1035
+ Put machine-specific selections in `etc/plonex.local.yml`. Configuration uses the
1036
+ normal mapping replacement rules: if you override `sources`, include the entries
1037
+ you want to keep; `+sources` adds or replaces complete source entries. Remove old
1038
+ `-e src/...` entries from `pip_requirements` and `etc/requirements.d/` when migrating
1039
+ to source declarations, including their extras. Those requirement mechanisms remain
1040
+ supported. Disabling a source stops requesting it; use `dependencies sync` to
1041
+ remove it if nothing else requires it.
1042
+
1043
+ See [development next steps](docs/development.md) for proposed environment checks,
1044
+ test selection, and debugging improvements.
1045
+
1046
+ `dependencies add <package> [<package> ...]`
1047
+
1048
+ - Add one or more packages and run dependency installation.
1049
+
1050
+ `sources {list|clone|update|missing|tainted|suggest} [options]`
1051
+
1052
+ - Manage configured source checkouts (uses Gitman under the hood).
1053
+ - `update`: run a regular sources update (executed by Gitman).
1054
+ - `update --force`: run a forced update (`--force`), asking for confirmation by
1055
+ default.
1056
+ - `update --force --yes`: skip confirmation.
1057
+ - `tainted`: list checkouts with local changes.
1058
+ - `list`: show configured checkouts and whether each is clean, tainted, or missing.
1059
+ - `missing`: show configured checkouts that are not present on disk.
1060
+ - `clone`: clone missing configured checkouts (using `repo` and optional `rev`).
1061
+ - `clone --yes`: skip confirmation prompt before cloning.
1062
+ - `suggest`: inspect existing unmanaged checkouts and print a ready-to-paste
1063
+ YAML snippet for `etc/plonex.yml` with inferred `repo` and `rev`.
1064
+ - `suggest --apply`: write inferred entries into `etc/plonex.yml`.
1065
+ - `suggest --apply-local`: write inferred entries into
1066
+ `etc/plonex-sources.local.yml`.
1067
+ - `suggest --apply-profile`: write inferred entries into
1068
+ the `etc/plonex.yml` of the first configured profile.
1069
+
1070
+ When unmanaged existing checkouts are detected, `sources list` and `sources tainted`
1071
+ also print a suggested `sources` section to add into `etc/plonex.yml`.
1072
+ `sources list` includes unmanaged checkouts in the table, marked `unmanaged` with
1073
+ their branch, repository, and checkout health. The highlighted YAML suggestion
1074
+ appears in a separate panel. Name filters apply to both rows and suggestions;
1075
+ Git inspection output stays inside the report.
1076
+
1077
+ `db upgrade`
1078
+
1079
+ - Run Plone upgrade steps.
1080
+
1081
+ ### Configuration editing
1082
+
1083
+ There is a dedicated `plonex project edit` command to help you edit the configuration files for your `plonex` project.
1084
+ Use the arrow keys to move through the file picker, Enter to open the highlighted
1085
+ file, or `q` to cancel. With non-interactive input, enter one file number. Use
1086
+ `--all` to open each discovered file in a separate editor invocation, one after
1087
+ another. Editors that return immediately may leave multiple windows open.
1088
+ The selected editor:
1089
+
1090
+ 1. with the `--editor` option in the command line
1091
+ 1. with the `editor` option if specified in the `etc/plonex.yml` configuration file
1092
+ 1. can be configured with the `EDITOR` environment variable
1093
+ 1. use the `open` command as a fallback if none of the above is specified.
1094
+
1095
+ You can of course edit the configuration files manually, but `plonex project edit` helps you discover and open them quickly.
1096
+
1097
+ ### Runtime
1098
+
1099
+ `db start`
1100
+
1101
+ - Start ZEO server.
1102
+
1103
+ `instance start [options] [args ...]`
1104
+
1105
+ - Options:
1106
+ - `-n, --name`: service name (default: `runwsgi`)
1107
+ - `-c, --config`: extra config file (repeatable)
1108
+ - `-p, --port`: HTTP port
1109
+ - `--host`: HTTP host
1110
+
1111
+ `instance console [options]`
1112
+
1113
+ - Options:
1114
+ - `-n, --name`: service name (default: `zconsole`)
1115
+ - `-c, --config`: extra config file (repeatable)
1116
+ - `-p, --port`: HTTP port
1117
+ - `--host`: HTTP host
1118
+ - Open an interactive Zope console. Use `instance run` for scripts.
1119
+
1120
+ `instance run [args ...]`
1121
+
1122
+ - Run a script through `zconsole run` with generated runtime config.
1123
+
1124
+ `instance add-user [-c FILE] <username> [password]`
1125
+
1126
+ - Create a Zope user (password can be omitted to auto-generate one).
1127
+
1128
+ `supervisor {status|start|stop|restart|graceful|reread|update}`
1129
+
1130
+ - Manage supervisord for project services.
1131
+ - `graceful` accepts `--interval SECONDS` and falls back to
1132
+ `supervisor_graceful_interval` from YAML, defaulting to `1.0`.
1133
+ - `reread` detects changed program declarations; `update` applies them.
1134
+ - `restart PROGRAM ...` restarts selected programs; omit names to restart all.
1135
+
1136
+ ### Database
1137
+
1138
+ `db backup`
1139
+
1140
+ - Create a verified offline snapshot of FileStorage and blob storage.
1141
+
1142
+ `db pack [--days DAYS]`
1143
+
1144
+ - Pack ZODB revisions older than `DAYS` (default: `7`).
1145
+ - Uses merged `zeo_address` from your config files.
1146
+
1147
+ `db restore --to NEW_DIRECTORY [--from SNAPSHOT_DIRECTORY]`
1148
+
1149
+ - Recover into a new directory without replacing the live database. See
1150
+ [database backup and recovery](docs/recovery.md).
1151
+
1152
+ ### Tests
1153
+
1154
+ `test pytest <package-or-path> [pytest arguments ...]`
1155
+
1156
+ - Runs `.venv/bin/python -m pytest --pyargs PACKAGE` in the target project, with
1157
+ its configured environment and pytest settings. Install pytest in that
1158
+ project's virtualenv first.
1159
+ - Accepts importable Python package names, test directories, files, and node IDs.
1160
+ - Arguments after the package are passed to pytest, including `-k`, `-m`, `-x`,
1161
+ `-q`, `-v`, and plugin flags. An optional `--` after the package is accepted.
1162
+ - Put plonex logging flags before `pytest`; for example,
1163
+ `plonex -v test pytest my.package -q` enables plonex diagnostics and quiet pytest
1164
+ output. Test failures preserve pytest's exit status.
1165
+
1166
+ ```sh
1167
+ plonex test pytest my.package -k login -x
1168
+ plonex test pytest src/my.package/tests -q
1169
+ plonex test pytest tests/test_login.py::test_login -v
1170
+ ```
1171
+
1172
+ `test zope <package> [-t TEST]`
1173
+
1174
+ - Run `zope-testrunner` for a package.
1175
+
1176
+ `test robot-server [--layer LAYER]`
1177
+
1178
+ - Start robot test server.
1179
+
1180
+ `test robot <paths...> [--browser BROWSER] [-t TEST]`
1181
+
1182
+ - Run Robot Framework tests.
1183
+
1184
+ ## Configuration and option precedence
1185
+
1186
+ Options are merged in this order (higher wins):
1187
+
1188
+ 1. CLI options
1189
+ 1. Files passed with `-c/--config`
1190
+ 1. `etc/plonex-<service>.*.yml`
1191
+ 1. `etc/plonex-<service>.yml`
1192
+ 1. `etc/plonex.*.yml` (alphabetical precedence)
1193
+ 1. `etc/plonex.yml`
1194
+ 1. Profiles declared by `etc/plonex.yml` and nested profile `plonex.yml` files
1195
+ 1. Service defaults
1196
+
1197
+ Why this matters:
1198
+
1199
+ - you can keep sane defaults in `etc/plonex.yml`,
1200
+ - inherit shared defaults from one or more profiles,
1201
+ - add machine- or developer-specific overrides in `etc/plonex.local.yml`,
1202
+ - and still force one-off values from the command line when needed.
1203
+
1204
+ This is especially useful for paths like `zeo_address`, ports, or feature flags
1205
+ that can vary by environment.
1206
+
1207
+ Useful settings include:
1208
+
1209
+ - `log_level`: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`
1210
+ - `environment_vars`: environment variables exported before service execution
1211
+ - `zeo_address`: socket/host used by runtime services and `db pack`
1212
+ - `blobstorage`: blob storage path
1213
+ - `http_port`, `http_address`
1214
+
1215
+ Example:
1216
+
1217
+ ```yaml
1218
+ zeo_address: /srv/components/zeo/var/zeo.socket
1219
+ blobstorage: /srv/components/zeo/var/blobstorage
1220
+ environment_vars:
1221
+ TZ: Europe/Rome
1222
+ ```
1223
+
1224
+ Service-specific overrides are useful when one command needs different settings.
1225
+ For example:
1226
+
1227
+ - `etc/plonex.yml`: shared defaults.
1228
+ - `etc/plonex.local.yml`: local machine overrides (socket paths, local ports).
1229
+ - `etc/plonex-runwsgi.yml`: runwsgi-only adjustments.
1230
+
1231
+ Because these files are merged with precedence, you can keep common settings in
1232
+ one place and make only small targeted overrides where needed.
1233
+
1234
+ ### Project environment variables
1235
+
1236
+ Plonex supplies these variables to Zope instances, console sessions, and scripts:
1237
+
1238
+ - `PLONEX_TARGET`: absolute project root.
1239
+ - `PLONEX_INSTANCE_NAME`: runtime service name, selected with `--name` where
1240
+ supported (for example, `instance1`). Defaults are `runwsgi`, `fg`, and
1241
+ `zconsole` for start, debug, and console or run, respectively.
1242
+ - `PLONEX_INSTANCE_HOME`: absolute path of the generated instance runtime directory.
1243
+ - `PLONEX_COMMAND`: `instance start`, `instance debug`, `instance console`, or
1244
+ `instance run`. Internal Zope script jobs use `instance run`.
1245
+ - `PLONEX_VERSION`: version of Plonex launching the process.
1246
+
1247
+ These values take precedence over values inherited from the shell or defined
1248
+ in configuration.
1249
+
1250
+ Before running a command, plonex reads the optional `.env` and `.env.local` files
1251
+ in the project root (the directory containing `etc/plonex.yml`). Both supply
1252
+ variables to plonex and the services it starts, including Supervisor and its
1253
+ child processes. `.env.local` overrides `.env`, so it can hold machine-specific
1254
+ values. Other names, such as `.env.production`, are not loaded automatically.
1255
+
1256
+ Each file contains one `NAME=value` assignment per line. Empty lines, comments,
1257
+ optional `export`, and shell-style single or double quotes are supported. Quote
1258
+ values containing spaces or `#`. Values are literal: variable expansion, command
1259
+ substitution, and multiline values are not supported. Either file may be absent.
1260
+
1261
+ When the same variable is defined in multiple places, the highest applicable
1262
+ setting wins:
1263
+
1264
+ 1. A job's `environment` settings, for that job. A managed process may also
1265
+ define its own environment settings.
1266
+ 1. The service's resolved `environment_vars` in YAML or configuration overrides.
1267
+ YAML files and `-c/--config` follow the configuration precedence above.
1268
+ 1. Variables already exported in the shell that starts plonex (including an
1269
+ explicitly exported empty value).
1270
+ 1. The project root `.env.local`.
1271
+ 1. The project root `.env`.
1272
+
1273
+ For example, start with these files in the project root:
1274
+
1275
+ `.env`:
1276
+
1277
+ ```dotenv
1278
+ COLOR=blue
1279
+ TZ=Europe/Rome
1280
+ ```
1281
+
1282
+ `.env.local`:
1283
+
1284
+ ```dotenv
1285
+ COLOR=green
1286
+ ```
1287
+
1288
+ With no other definition of `COLOR`, a service receives `COLOR=green` and
1289
+ `TZ=Europe/Rome`. Remove `COLOR` from `.env.local` and the service receives
1290
+ `COLOR=blue`. To override both files for one command, run:
1291
+
1292
+ ```shell
1293
+ COLOR=red plonex instance start
1294
+ ```
1295
+
1296
+ The service now receives `COLOR=red`. To override it for a particular service,
1297
+ add the following to `etc/plonex-runwsgi.yml`:
1298
+
1299
+ ```yaml
1300
+ environment_vars:
1301
+ COLOR: purple
1302
+ ```
1303
+
1304
+ That service receives `COLOR=purple`, even when `COLOR=red` is exported in the
1305
+ shell. Other services use their own resolved configuration. To override a YAML
1306
+ setting for one invocation, create `etc/one-off.yml`:
1307
+
1308
+ ```yaml
1309
+ environment_vars:
1310
+ COLOR: yellow
1311
+ ```
1312
+
1313
+ Then run `plonex instance start --config etc/one-off.yml`; that invocation
1314
+ receives `COLOR=yellow`.
1315
+
1316
+ A scheduled job can override the service value for its own process. For example,
1317
+ in the applicable cron configuration:
1318
+
1319
+ ```yaml
1320
+ cron_jobs:
1321
+ example:
1322
+ schedule: "*/15 * * * *"
1323
+ command: ["/usr/bin/env"]
1324
+ environment:
1325
+ COLOR: orange
1326
+ ```
1327
+
1328
+ This job receives `COLOR=orange`. Other jobs retain their own resolved values.
1329
+
1330
+ The environment files supply process environment variables; they do not
1331
+ substitute values into YAML options. YAML `environment_vars` is an option mapping:
1332
+ a higher-priority definition normally replaces the mapping, while `+environment_vars` can extend it in profile and additional project
1333
+ configuration files that support `+` merges.
1334
+ Plonex does not automatically load separate service-specific `.env` files.
1335
+ Services may implement their own environment handling.
1336
+
1337
+ Keep files containing secrets out of version control and give them appropriate
1338
+ permissions; plonex does not create or configure the project's ignore rules.
1339
+ Restart running processes to pick up changes. The interactive shell reloads the
1340
+ files for each command.
1341
+
1342
+ ## Dependency-driven services
1343
+
1344
+ `plonex` supports declarative helper services in `etc/plonex.yml`.
1345
+ These services are not intended to be run directly as a top-level command.
1346
+ Instead, they run automatically as dependencies of runtime commands.
1347
+
1348
+ This is useful when a command needs generated files before execution, for
1349
+ example Supervisor program snippets or other rendered templates.
1350
+
1351
+ Example:
1352
+
1353
+ ```yaml
1354
+ services:
1355
+ - template:
1356
+ run_for: supervisor
1357
+ source_path: resource://plonex.services.supervisor.templates:program.conf.j2
1358
+ target_path: tmp/supervisor/etc/supervisor/runwsgi.conf
1359
+ options:
1360
+ program: runwsgi
1361
+ command: plonex instance start
1362
+ process_name: runwsgi
1363
+ directory: "{{ target }}"
1364
+ priority: 2
1365
+ ```
1366
+
1367
+ In this example:
1368
+
1369
+ - the template is rendered only when running a command mapped to `supervisor`,
1370
+ - the rendered file is prepared before `supervisord`/`supervisorctl` is called,
1371
+ - and the dependency is fully controlled from YAML.
1372
+
1373
+ `run_for` accepts either a string or a list of command names:
1374
+
1375
+ ```yaml
1376
+ services:
1377
+ - template:
1378
+ run_for: [supervisor, runwsgi]
1379
+ source: etc/templates/runtime.conf.j2
1380
+ target: tmp/runtime.conf
1381
+ ```
1382
+
1383
+ Notes:
1384
+
1385
+ - service entries must be a list of single-key mappings,
1386
+ - unknown service names raise a configuration error,
1387
+ - relative `source`/`target` paths are resolved from the project target folder,
1388
+ - `resource://...` template sources are supported.
1389
+
1390
+ Recommended pattern:
1391
+
1392
+ - keep long-lived defaults in `etc/plonex.yml`,
1393
+ - keep machine-specific path overrides in `etc/plonex.local.yml`,
1394
+ - add `run_for` only where generation must be scoped to specific commands.
1395
+
1396
+ More examples:
1397
+
1398
+ - Add extra ZCML includes for runtime services:
1399
+
1400
+ ```yaml
1401
+ zcml_additional:
1402
+ - etc/zcml/010-my-package-meta.zcml
1403
+ - etc/zcml/020-my-package-configure.zcml
1404
+ - etc/zcml/030-my-package-overrides.zcml
1405
+ ```
1406
+
1407
+ These files are rendered or copied into `tmp/runwsgi/etc/package-includes/`
1408
+ before `plonex instance start ...` runs. File names matter: `plonex` expects the
1409
+ usual `-meta.zcml`, `-configure.zcml`, or `-overrides.zcml` suffixes.
1410
+
1411
+ - Add extra `zope.conf` snippets for runtime services:
1412
+
1413
+ ```yaml
1414
+ zope_conf_additional:
1415
+ - etc/zopeconf/cache.j2
1416
+ - etc/zopeconf/instancesettings.conf
1417
+ ```
1418
+
1419
+ For example, `etc/zopeconf/cache.j2` could contain:
1420
+
1421
+ ```text
1422
+ <product-config my.package>
1423
+ some-setting value
1424
+ </product-config>
1425
+ ```
1426
+
1427
+ Each listed file is appended to the generated runtime `zope.conf` file.
1428
+ Jinja templates can use the current service context.
1429
+
1430
+ - Generate a Supervisor program for an auxiliary service such as a websocket
1431
+ worker, Celery worker, or custom script:
1432
+
1433
+ ```yaml
1434
+ services:
1435
+ - template:
1436
+ run_for: supervisor
1437
+ source_path: resource://plonex.services.supervisor.templates:program.conf.j2
1438
+ target_path: tmp/supervisor/etc/supervisor/worker.conf
1439
+ options:
1440
+ program: worker
1441
+ command: .venv/bin/python -m mypackage.worker
1442
+ process_name: worker
1443
+ directory: "{{ target }}"
1444
+ priority: 20
1445
+ ```
1446
+
1447
+ That way `plonex supervisor start` manages not only `zeoserver` and
1448
+ `runwsgi`, but also your additional long-running process.
1449
+
1450
+ - Combine service dependencies and runtime extras in the same project:
1451
+
1452
+ ```yaml
1453
+ zcml_additional:
1454
+ - etc/zcml/020-my-package-configure.zcml
1455
+
1456
+ zope_conf_additional:
1457
+ - etc/zopeconf/cache.j2
1458
+
1459
+ services:
1460
+ - template:
1461
+ run_for: supervisor
1462
+ source: etc/templates/worker.conf.j2
1463
+ target: tmp/supervisor/etc/supervisor/worker.conf
1464
+ ```
1465
+
1466
+ This keeps all project-specific runtime wiring in `etc/plonex.yml` while still
1467
+ letting the generated files live under `tmp/`.
1468
+
1469
+ ## Under the hood
1470
+
1471
+ Services remain dataclasses based on `BaseService`. Commands resolve the project,
1472
+ merge configuration, enter the service (including helper services), then run the
1473
+ appropriate executable from the project virtualenv.
1474
+
1475
+ | Command | Main service | Execution |
1476
+ | ---------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------ |
1477
+ | `instance debug`, `instance start` | `RunWSGI` | `.venv/bin/runwsgi tmp/<name>/etc/wsgi.ini` |
1478
+ | `instance console`, `instance run` | `ZConsole` | `.venv/bin/zconsole ACTION tmp/<name>/etc/zope.conf ...` |
1479
+ | `db start` | `ZeoServer` | `.venv/bin/runzeo -C tmp/zeoserver/etc/zeo.conf` |
1480
+ | `supervisor start` | `Supervisor` | `.venv/bin/supervisord -c tmp/supervisor/etc/supervisord.conf` |
1481
+ | `supervisor status` | `Supervisor` | `.venv/bin/supervisorctl -c tmp/supervisor/etc/supervisord.conf status` |
1482
+ | `dependencies install` | `InstallService` | `uv pip install -r var/requirements.txt -c var/constraints.txt` |
1483
+ | `dependencies sync` | `InstallService` | `uv pip compile ... --output-file var/compiled-requirements.txt`, then `uv pip sync var/compiled-requirements.txt` |
1484
+ | `dependencies outdated` | `InstallService` | `uv pip list --outdated` |
1485
+ | `test pytest PACKAGE` | `PytestService` | `.venv/bin/python -m pytest --pyargs PACKAGE ...` |
1486
+ | `test zope PACKAGE` | `ZopeTest` | `.venv/bin/zope-testrunner --all --quiet -pvc --package PACKAGE --auto-path ...` |
1487
+ | `db pack` | `ZeoServer` | `.venv/bin/zeopack -u <zeo_address> -d <days>` |
1488
+ | `options COMMAND` | Inspection | Construct the same service and read its resolved options |
1489
+
1490
+ Paths shown above use default folder and service names. Dependency inspection
1491
+ uses `uv pip freeze` with the target project's Python, so printed package names
1492
+ are canonical. Runtime templates and configured helper services can add other
1493
+ preparation steps.
1494
+
1495
+ The shared service registry and invocation binding are also used by inspection.
1496
+ The shell owns terminal-backed jobs; services continue to run in isolated CLI
1497
+ processes. Tables and inspection reports share a Markdown-capable presenter.
1498
+ See [Project decisions](docs/decisions.md) for ownership boundaries, snapshot
1499
+ semantics, and how to add commands while preserving these patterns.
1500
+
1501
+ ## Troubleshooting
1502
+
1503
+ ### "Could not find etc/plonex.yml"
1504
+
1505
+ `plonex` looks for `etc/plonex.yml` by walking up from the target directory.
1506
+ Use one of these approaches:
1507
+
1508
+ - run commands from inside your project tree,
1509
+ - pass `--target /path/to/project`,
1510
+ - or initialize a project first with `plonex project init`.
1511
+
1512
+ ### Command uses unexpected values
1513
+
1514
+ Use `plonex options COMMAND` with the same flags as the command you are
1515
+ troubleshooting. For a running or completed shell job, `options %1` shows the
1516
+ options it actually started with. Use `jobs` to find the job ID.
1517
+
1518
+ `plonex project compile` also writes merged configuration to `var/plonex.yml`, but that
1519
+ file is not a substitute for inspecting a particular service and its CLI flags.
1520
+
1521
+ Typical causes:
1522
+
1523
+ - a higher-priority config file overrides your value,
1524
+ - a command-line option is overriding YAML,
1525
+ - or a service-specific file exists and takes precedence for that service.
1526
+
1527
+ ### `db pack` cannot connect to ZEO
1528
+
1529
+ Check `zeo_address` in your merged config and ensure the socket/host exists.
1530
+ If needed, set it in `etc/plonex.local.yml` for machine-specific environments.
1531
+
1532
+ ### Missing binaries in `.venv/bin`
1533
+
1534
+ If commands fail because binaries are missing, run:
1535
+
1536
+ ```sh
1537
+ plonex dependencies install
1538
+ ```
1539
+
1540
+ This recreates or updates environment-installed tooling used by runtime commands.
1541
+
1542
+ ## Current limitations
1543
+
1544
+ - Database snapshots require stopped FileStorage writers and a coordinated blob
1545
+ store; legacy repozo backups require their original restore procedure.
1546
+ - Interactive job control requires a Unix terminal. Piped commands run without
1547
+ terminal job control.
1548
+ - Jobs and snapshots belong to one shell session. Job logs and snapshots remain
1549
+ on disk, but a new shell does not adopt old processes or job IDs.
1550
+ - Option previews construct services and may create directories; they do not run
1551
+ services. Startup snapshots are not live views into a process's Python state.
1552
+
1553
+ ## AI Assistance
1554
+
1555
+ Parts of this project were developed with the assistance of AI-based tools.
1556
+
1557
+ All generated content was reviewed, tested, and modified by human maintainers
1558
+ before inclusion in the repository.