pglift-cli 3.0.0__tar.gz → 3.2.0__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 (50) hide show
  1. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/PKG-INFO +1 -1
  2. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/src/pglift_cli/main.py +42 -28
  3. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/src/pglift_cli/model.py +26 -15
  4. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/src/pglift_cli/pgconf.py +1 -1
  5. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/src/pglift_cli/util.py +2 -2
  6. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/tests/expect/test-cli-walkthrough.t +26 -23
  7. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/tests/expect/test-demote.t +8 -6
  8. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/tests/expect/test-help.t +9 -7
  9. pglift_cli-3.2.0/tests/expect/test-instance-name.t +45 -0
  10. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/tests/expect/test-port-validation.t +18 -15
  11. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/tests/expect/test-prometheus.t +3 -3
  12. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/tests/expect/test-standby-pgbackrest.t +20 -17
  13. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/tests/expect/test-standby.t +8 -6
  14. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/tests/expect/test-transactions.t +0 -1
  15. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/tests/expect/test-upgrade.t +6 -4
  16. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/tests/unit/test_model.py +82 -4
  17. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/.gitignore +0 -0
  18. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/README.md +0 -0
  19. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/hatch.toml +0 -0
  20. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/pyproject.toml +0 -0
  21. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/pytest.ini +0 -0
  22. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/src/pglift_cli/__init__.py +0 -0
  23. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/src/pglift_cli/__main__.py +0 -0
  24. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/src/pglift_cli/_settings.py +0 -0
  25. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/src/pglift_cli/_site.py +0 -0
  26. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/src/pglift_cli/base.py +0 -0
  27. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/src/pglift_cli/console.py +0 -0
  28. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/src/pglift_cli/database.py +0 -0
  29. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/src/pglift_cli/hookspecs.py +0 -0
  30. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/src/pglift_cli/instance.py +0 -0
  31. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/src/pglift_cli/patroni.py +0 -0
  32. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/src/pglift_cli/pgbackrest/__init__.py +0 -0
  33. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/src/pglift_cli/pgbackrest/repo_path.py +0 -0
  34. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/src/pglift_cli/pghba.py +0 -0
  35. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/src/pglift_cli/pm.py +0 -0
  36. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/src/pglift_cli/postgres.py +0 -0
  37. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/src/pglift_cli/prometheus.py +0 -0
  38. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/src/pglift_cli/py.typed +0 -0
  39. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/src/pglift_cli/role.py +0 -0
  40. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/src/pglift_cli/wal.py +0 -0
  41. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/tests/expect/.gitignore +0 -0
  42. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/tests/expect/test-base.t +0 -0
  43. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/tests/unit/__init__.py +0 -0
  44. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/tests/unit/conftest.py +0 -0
  45. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/tests/unit/test__site.py +0 -0
  46. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/tests/unit/test_audit.py +0 -0
  47. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/tests/unit/test_cli.py +0 -0
  48. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/tests/unit/test_main.py +0 -0
  49. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/tests/unit/test_pm.py +0 -0
  50. {pglift_cli-3.0.0 → pglift_cli-3.2.0}/tests/unit/test_util.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pglift_cli
3
- Version: 3.0.0
3
+ Version: 3.2.0
4
4
  Summary: Command-line interface for pglift
5
5
  Project-URL: Documentation, https://pglift.readthedocs.io/
6
6
  Project-URL: Source, https://gitlab.com/dalibo/pglift/
@@ -11,7 +11,7 @@ import warnings
11
11
  from functools import partial
12
12
  from importlib.metadata import version
13
13
  from pathlib import Path
14
- from typing import Literal
14
+ from typing import Any, Literal
15
15
 
16
16
  import click
17
17
  import click.exceptions
@@ -24,7 +24,6 @@ from rich.highlighter import NullHighlighter
24
24
  from rich.syntax import Syntax
25
25
 
26
26
  from pglift import ui
27
- from pglift._compat import assert_never
28
27
  from pglift.system import install
29
28
  from pglift.types import LogLevel
30
29
 
@@ -234,35 +233,50 @@ def site_settings(
234
233
  console.print(syntax)
235
234
 
236
235
 
237
- @cli.command(
238
- "site-configure",
239
- hidden=True,
240
- )
241
- @click.argument(
242
- "action",
243
- type=click.Choice(["install", "uninstall", "check", "list"]),
244
- default="install",
236
+ @cli.group(hidden=True)
237
+ def site_configure(**kwargs: Any) -> None:
238
+ """Manage installation of extra data files for pglift.
239
+
240
+ This is an INTERNAL command.
241
+ """
242
+
243
+
244
+ @site_configure.command("install")
245
+ @click.option(
246
+ "--force",
247
+ is_flag=True,
248
+ default=False,
249
+ help="Force the overwrite of installed files",
245
250
  )
246
251
  @click.pass_obj
252
+ @async_command
253
+ async def site_configure_install(obj: Obj, force: bool) -> None:
254
+ with obj.lock, audit():
255
+ await install.do(_site.SETTINGS, force=force)
256
+
257
+
258
+ @site_configure.command("uninstall")
259
+ @click.pass_obj
260
+ @async_command
261
+ async def site_configure_uninstall(obj: Obj) -> None:
262
+ with obj.lock, audit():
263
+ await install.undo(_site.SETTINGS)
264
+
265
+
266
+ @site_configure.command("check")
267
+ @click.pass_obj
247
268
  @click.pass_context
248
269
  @async_command
249
- async def site_configure(
250
- context: click.Context, obj: Obj, action: Literal["install", "uninstall", "check"]
251
- ) -> None:
252
- """Manage installation of extra data files for pglift.
270
+ async def site_configure_check(context: click.Context, obj: Obj) -> None:
271
+ with obj.lock, audit():
272
+ if not install.check(_site.SETTINGS):
273
+ context.exit(1)
253
274
 
254
- This is an INTERNAL command.
255
- """
275
+
276
+ @site_configure.command("list")
277
+ @click.pass_obj
278
+ @async_command
279
+ async def site_configure_list(obj: Obj) -> None:
256
280
  with obj.lock, audit():
257
- if action == "install":
258
- await install.do(_site.SETTINGS)
259
- elif action == "uninstall":
260
- await install.undo(_site.SETTINGS)
261
- elif action == "check":
262
- if not install.check(_site.SETTINGS):
263
- context.exit(1)
264
- elif action == "list":
265
- for p in install.ls(_site.SETTINGS):
266
- click.echo(p)
267
- else:
268
- assert_never(action)
281
+ for p in install.ls(_site.SETTINGS):
282
+ click.echo(p)
@@ -18,10 +18,11 @@ import pydantic
18
18
  from pydantic.fields import FieldInfo
19
19
  from pydantic_core import ErrorDetails
20
20
 
21
- from pglift.annotations import cli
21
+ from pglift.annotations import CreateOnly, cli
22
22
  from pglift.exceptions import MutuallyExclusiveError
23
23
  from pglift.models.helpers import is_optional, optional_type
24
24
  from pglift.models.interface import PresenceState
25
+ from pglift.settings import Settings
25
26
  from pglift.types import (
26
27
  Operation,
27
28
  field_annotation,
@@ -33,6 +34,7 @@ from . import _site, logger
33
34
 
34
35
  ModelType = type[pydantic.BaseModel]
35
36
  T = TypeVar("T", bound=pydantic.BaseModel)
37
+ V = TypeVar("V")
36
38
  Callback = Callable[..., Any]
37
39
  ClickDecorator = Callable[[Callback], Callback]
38
40
  DEFAULT = object()
@@ -167,7 +169,7 @@ class ParamSpec(ABC):
167
169
  loc: tuple[str, ...]
168
170
  description: str | None = None
169
171
 
170
- objtype: ClassVar[type[click.Parameter]] = click.Parameter
172
+ objtype: ClassVar[type[click.Parameter]]
171
173
 
172
174
  @property
173
175
  def param(self) -> click.Parameter:
@@ -296,6 +298,15 @@ def _paramspecs_from_model(
296
298
  return DEFAULT
297
299
  return value
298
300
 
301
+ def default_factory(
302
+ _ctx: click.Context,
303
+ _param: click.Argument,
304
+ value: V,
305
+ *,
306
+ callback: Callable[[Settings, V], V],
307
+ ) -> V:
308
+ return callback(_site.SETTINGS, value)
309
+
299
310
  def add_state_field_callback(
300
311
  ctx: click.Context,
301
312
  _param: click.Argument,
@@ -329,11 +340,7 @@ def _paramspecs_from_model(
329
340
  for fname, field in model_type.model_fields.items():
330
341
  if field_annotation(field, cli.Hidden):
331
342
  continue
332
- if (
333
- operation == "update"
334
- and isinstance(field.json_schema_extra, dict)
335
- and field.json_schema_extra.get("readOnly")
336
- ):
343
+ if field_annotation(field, CreateOnly) and operation == "update":
337
344
  continue
338
345
 
339
346
  modelname = argname = field.alias or fname
@@ -361,9 +368,6 @@ def _paramspecs_from_model(
361
368
  if not _parents and required:
362
369
  if origin_type is typing.Literal:
363
370
  choices = list(typing.get_args(ftype))
364
- if config is not None:
365
- assert isinstance(config, cli.Choices)
366
- choices = config.choices
367
371
  attrs["type"] = click.Choice(choices)
368
372
  if config is not None and isinstance(config, cli.Option):
369
373
  attrs["required"] = True
@@ -377,6 +381,17 @@ def _paramspecs_from_model(
377
381
  ),
378
382
  )
379
383
 
384
+ elif config is not None and isinstance(config, cli.OptionalArgument):
385
+ attrs["required"] = False
386
+ attrs["callback"] = functools.partial(
387
+ default_factory,
388
+ callback=config.default_factory,
389
+ )
390
+
391
+ yield (
392
+ (modelname, argname),
393
+ ArgumentSpec((argname,), field, attrs, loc=(modelname,)),
394
+ )
380
395
  else:
381
396
  yield (
382
397
  (modelname, argname),
@@ -386,11 +401,7 @@ def _paramspecs_from_model(
386
401
  )
387
402
 
388
403
  else:
389
- if (
390
- config
391
- and isinstance(config, cli.Argument | cli.Option)
392
- and config.metavar is not None
393
- ):
404
+ if config and isinstance(config, cli.Option) and config.metavar is not None:
394
405
  metavar = config.metavar
395
406
  else:
396
407
  metavar = argname
@@ -58,7 +58,7 @@ def show_configuration_changes(
58
58
  @cli.command("show")
59
59
  @click.argument("parameter", nargs=-1)
60
60
  @pass_postgresql_instance
61
- def show(instance: PostgreSQLInstance, parameter: tuple[str]) -> None:
61
+ def show(instance: PostgreSQLInstance, parameter: tuple[str, ...]) -> None:
62
62
  """Show configuration (all parameters or specified ones).
63
63
 
64
64
  Only uncommented parameters are shown when no PARAMETER is specified. When
@@ -273,7 +273,7 @@ def nameversion_from_id(instance_id: str) -> tuple[str, PostgreSQLVersion | None
273
273
 
274
274
 
275
275
  def postgresql_instance_lookup(
276
- context: click.Context, param: click.Parameter, value: None | str | tuple[str]
276
+ context: click.Context, param: click.Parameter, value: None | str | tuple[str, ...]
277
277
  ) -> PostgreSQLInstance | tuple[PostgreSQLInstance, ...]:
278
278
  """Return one or more PostgreSQLInstance, possibly guessed if there
279
279
  is only one on system, depending on 'param' variadic flag (nargs).
@@ -764,7 +764,7 @@ class PluggableCommandGroup(abc.ABC, Group):
764
764
  if obj is None:
765
765
  obj = context.ensure_object(Obj)
766
766
  if obj is None:
767
- return
767
+ return # type: ignore[unreachable]
768
768
  self.register_plugin_commands(obj)
769
769
  self._plugin_commands_loaded = True
770
770
 
@@ -26,7 +26,7 @@ Site settings:
26
26
  > postgresql:
27
27
  > auth:
28
28
  > local: md5
29
- > passfile: '$TMPDIR/.pgpass'
29
+ > passfile: '$TMPDIR/pgpass/.pgpass'
30
30
  > password_command:
31
31
  > - jq
32
32
  > - -r
@@ -65,7 +65,7 @@ Make sure password_command works:
65
65
  "local": "md5",
66
66
  "host": "trust",
67
67
  "hostssl": "trust",
68
- "passfile": "$TMPDIR/.pgpass",
68
+ "passfile": "$TMPDIR/pgpass/.pgpass",
69
69
  "password_command": [
70
70
  "jq",
71
71
  "-r",
@@ -89,10 +89,11 @@ Make sure password_command works:
89
89
  $ pglift site-configure install
90
90
  INFO creating base pgBackRest configuration directory: $TMPDIR/etc/pgbackrest
91
91
  INFO installing base pgBackRest configuration
92
- INFO creating pgBackRest include directory
92
+ INFO creating pgBackRest include directory: $TMPDIR/etc/pgbackrest/conf.d
93
93
  INFO creating pgBackRest repository backups and archive directory: $TMPDIR/pgbackrest
94
94
  INFO creating pgBackRest log directory: $TMPDIR/log/pgbackrest
95
95
  INFO creating pgBackRest spool directory: $TMPDIR/srv/pgbackrest/spool
96
+ INFO creating pgpass file directory: $TMPDIR/pgpass
96
97
  INFO creating PostgreSQL log directory: $TMPDIR/log/postgresql
97
98
 
98
99
  $ trap "pglift --non-interactive instance drop main; \
@@ -116,10 +117,11 @@ Audit log:
116
117
  DEBUG - pglift.util - using 'pgbackrest/pgbackrest.conf' configuration file from distribution
117
118
  INFO - pglift.util - creating base pgBackRest configuration directory: $TMPDIR/etc/pgbackrest
118
119
  INFO - pglift.pgbackrest - installing base pgBackRest configuration
119
- INFO - pglift.pgbackrest - creating pgBackRest include directory
120
+ INFO - pglift.util - creating pgBackRest include directory: $TMPDIR/etc/pgbackrest/conf.d
120
121
  INFO - pglift.util - creating pgBackRest repository backups and archive directory: $TMPDIR/pgbackrest
121
122
  INFO - pglift.util - creating pgBackRest log directory: $TMPDIR/log/pgbackrest
122
123
  INFO - pglift.util - creating pgBackRest spool directory: $TMPDIR/srv/pgbackrest/spool
124
+ INFO - pglift.util - creating pgpass file directory: $TMPDIR/pgpass
123
125
  INFO - pglift.util - creating PostgreSQL log directory: $TMPDIR/log/postgresql
124
126
  INFO - pglift_cli.audit - command completed \(\d+.\d+ seconds\) (re)
125
127
 
@@ -166,13 +168,13 @@ Error cases for instance operations
166
168
  Error: instance already exists
167
169
  [1]
168
170
  $ pglift instance create 'in/va/lid' --pgbackrest-stanza=xxx --surole-password=s3per
169
- Usage: pglift instance create [OPTIONS] NAME
171
+ Usage: pglift instance create [OPTIONS] [NAME]
170
172
  Try 'pglift instance create --help' for help.
171
173
 
172
- Error: Invalid value for 'NAME': String should match pattern '^[^/-]+$'
174
+ Error: Invalid value for '[NAME]': String should match pattern '^[^/-]+$'
173
175
  [2]
174
176
  $ pglift instance create stdby --standby-for='port 1234' --pgbackrest-stanza=stddy --surole-password=s3per
175
- Usage: pglift instance create [OPTIONS] NAME
177
+ Usage: pglift instance create [OPTIONS] [NAME]
176
178
  Try 'pglift instance create --help' for help.
177
179
 
178
180
  Error: Invalid value for '--standby-for': missing "=" after "port" in connection info string
@@ -240,7 +242,6 @@ Reload, restart:
240
242
  INFO stopping Prometheus postgres_exporter 1\d-main (re)
241
243
  $ pglift instance restart
242
244
  INFO restarting instance 1\d\/main (re)
243
- INFO stopping Prometheus postgres_exporter 1\d-main (re)
244
245
  INFO restarting PostgreSQL
245
246
  INFO stopping PostgreSQL 1\d\/main (re)
246
247
  INFO starting PostgreSQL 1\d\/main (re)
@@ -341,7 +342,7 @@ Stop, alter, (re)start an instance:
341
342
  Instance environment, and program execution:
342
343
 
343
344
  $ pglift instance env | grep PASSFILE
344
- PGPASSFILE=$TMPDIR/.pgpass
345
+ PGPASSFILE=$TMPDIR/pgpass/.pgpass
345
346
  $ pglift instance env -o json | jq '.PGBACKREST_STANZA, .PGHOST, .PGPASSWORD'
346
347
  "main"
347
348
  "$TMPDIR/run/postgresql"
@@ -518,14 +519,14 @@ Add and manipulate roles:
518
519
 
519
520
  $ pglift role -i main create dba --login --pgpass --password=qwerty --in-role=pg_read_all_stats --dry-run
520
521
  INFO creating role 'dba'
521
- INFO adding an entry for 'dba' in \$TMPDIR\/.pgpass \(port=\d+\) (re)
522
+ INFO adding an entry for 'dba' in \$TMPDIR\/pgpass\/.pgpass \(port=\d+\) (re)
522
523
  DRY RUN: no changes made
523
524
 
524
525
  $ pglift role -i main create dba --login --pgpass --password=qwerty --in-role=pg_read_all_stats
525
526
  INFO creating role 'dba'
526
- INFO adding an entry for 'dba' in \$TMPDIR\/.pgpass \(port=\d+\) (re)
527
+ INFO adding an entry for 'dba' in \$TMPDIR\/pgpass\/.pgpass \(port=\d+\) (re)
527
528
 
528
- $ cat $TMPDIR/.pgpass
529
+ $ cat $TMPDIR/pgpass/.pgpass
529
530
  \*:\d+:\*:dba:qwerty (re)
530
531
 
531
532
  $ pglift role get dba
@@ -555,8 +556,8 @@ Add and manipulate roles:
555
556
 
556
557
  $ pglift role alter dba --connection-limit=10 --inherit --no-pgpass --no-login --revoke=pg_read_all_stats --grant=pg_monitor --valid-until=2026-01-01 --dry-run
557
558
  INFO altering role 'dba'
558
- INFO removing entry for 'dba' in \$TMPDIR\/.pgpass \(port=\d+\) (re)
559
- INFO removing now empty $TMPDIR/.pgpass
559
+ INFO removing entry for 'dba' in \$TMPDIR\/pgpass\/.pgpass \(port=\d+\) (re)
560
+ INFO removing now empty $TMPDIR/pgpass/.pgpass
560
561
  DRY RUN: no changes made
561
562
 
562
563
  $ pglift role -i main create dba --password=qwerty --encrypted-password=md5azerty
@@ -568,8 +569,8 @@ Add and manipulate roles:
568
569
 
569
570
  $ pglift role alter dba --connection-limit=10 --inherit --no-pgpass --no-login --revoke=pg_read_all_stats --grant=pg_monitor --valid-until=2026-01-01
570
571
  INFO altering role 'dba'
571
- INFO removing entry for 'dba' in \$TMPDIR\/.pgpass \(port=\d+\) (re)
572
- INFO removing now empty $TMPDIR/.pgpass
572
+ INFO removing entry for 'dba' in \$TMPDIR\/pgpass\/.pgpass \(port=\d+\) (re)
573
+ INFO removing now empty $TMPDIR/pgpass/.pgpass
573
574
 
574
575
  $ pglift role get dba -o json
575
576
  {
@@ -1289,11 +1290,13 @@ Cleanup.
1289
1290
  INFO deconfiguring Prometheus postgres_exporter 1\d-main (re)
1290
1291
  INFO deleting pgBackRest stanza 'main'
1291
1292
  INFO deconfiguring pgBackRest stanza 'main'
1292
- INFO removing entries matching port=\d+ from \$TMPDIR\/.pgpass (re)
1293
- INFO deleting PostgreSQL data and WAL directories
1294
- INFO deleting pgBackRest include directory
1293
+ INFO removing entries matching port=\d+ from \$TMPDIR\/pgpass\/.pgpass (re)
1294
+ INFO deleting PostgreSQL data directory: \$TMPDIR\/srv\/pgsql\/1\d\/main/data (re)
1295
+ INFO deleting PostgreSQL WAL directory: \$TMPDIR\/srv\/pgsql\/1\d\/main/wal (re)
1296
+ INFO deleting pgBackRest include directory: $TMPDIR/etc/pgbackrest/conf.d
1295
1297
  INFO uninstalling base pgBackRest configuration
1296
- INFO deleting pgBackRest log directory
1297
- INFO deleting pgBackRest spool directory
1298
- INFO deleting PostgreSQL log directory
1299
- INFO deleting PostgreSQL socket directory (no-eol)
1298
+ INFO deleting pgBackRest log directory: $TMPDIR/log/pgbackrest
1299
+ INFO deleting pgBackRest spool directory: $TMPDIR/srv/pgbackrest/spool
1300
+ INFO deleting pgpass file parent directory
1301
+ INFO deleting PostgreSQL log directory: $TMPDIR/log/postgresql
1302
+ INFO deleting PostgreSQL socket directory: $TMPDIR/run/postgresql (no-eol)
@@ -220,11 +220,13 @@ And finally check replication is functional
220
220
 
221
221
  INFO dropping instance 1\d\/pg1 (re)
222
222
  INFO stopping PostgreSQL 1\d\/pg1 (re)
223
- INFO deleting PostgreSQL data and WAL directories
223
+ INFO deleting PostgreSQL data directory: \$TMPDIR\/1\/srv\/pgsql\/1\d\/pg1\/data (re)
224
+ INFO deleting PostgreSQL WAL directory: \$TMPDIR\/1\/srv\/pgsql\/1\d\/pg1\/wal (re)
224
225
  INFO dropping instance 1\d\/pg2 (re)
225
226
  INFO stopping PostgreSQL 1\d\/pg2 (re)
226
- INFO deleting PostgreSQL data and WAL directories
227
- INFO deleting PostgreSQL log directory
228
- INFO deleting PostgreSQL socket directory
229
- INFO deleting PostgreSQL log directory
230
- INFO deleting PostgreSQL socket directory (no-eol)
227
+ INFO deleting PostgreSQL data directory: \$TMPDIR\/2\/srv\/pgsql\/1\d\/pg2\/data (re)
228
+ INFO deleting PostgreSQL WAL directory: \$TMPDIR\/2\/srv\/pgsql\/1\d\/pg2\/wal (re)
229
+ INFO deleting PostgreSQL log directory: $TMPDIR/1/log/postgresql
230
+ INFO deleting PostgreSQL socket directory: $TMPDIR/1/run/postgresql
231
+ INFO deleting PostgreSQL log directory: $TMPDIR/2/log/postgresql
232
+ INFO deleting PostgreSQL socket directory: $TMPDIR/2/run/postgresql (no-eol)
@@ -228,7 +228,7 @@ Instance commands
228
228
  -o, --output-format [json] Specify the output format.
229
229
  --help Show this message and exit.
230
230
  $ pglift instance create --help
231
- Usage: pglift instance create [OPTIONS] NAME
231
+ Usage: pglift instance create [OPTIONS] [NAME]
232
232
 
233
233
  Initialize a PostgreSQL instance
234
234
 
@@ -249,13 +249,13 @@ Instance commands
249
249
  --locale LOCALE Default locale.
250
250
  --encoding ENCODING Character encoding of the PostgreSQL
251
251
  instance.
252
- --auth-local [trust|reject|md5|password|scram-sha-256|sspi|ident|peer|pam|ldap|radius]
252
+ --auth-local [trust|reject|md5|password|scram-sha-256|sspi|ident|pam|ldap|radius|peer]
253
253
  Authentication method for local-socket
254
254
  connections.
255
- --auth-host [trust|reject|md5|password|scram-sha-256|gss|sspi|ident|pam|ldap|radius]
255
+ --auth-host [trust|reject|md5|password|scram-sha-256|sspi|ident|pam|ldap|radius|gss]
256
256
  Authentication method for local TCP/IP
257
257
  connections.
258
- --auth-hostssl [trust|reject|md5|password|scram-sha-256|gss|sspi|ident|pam|ldap|radius|cert]
258
+ --auth-hostssl [trust|reject|md5|password|scram-sha-256|sspi|ident|pam|ldap|radius|gss|cert]
259
259
  Authentication method for SSL-encrypted
260
260
  TCP/IP connections.
261
261
  --surole-password PASSWORD Super-user role password.
@@ -912,7 +912,7 @@ PostgreSQL configuration commands
912
912
  Options:
913
913
  --help Show this message and exit.
914
914
 
915
- pg_hba.conf management commands
915
+ HBA configuration management commands
916
916
 
917
917
  $ pglift pghba --help
918
918
  Usage: pglift pghba [OPTIONS] COMMAND [ARGS]...
@@ -947,7 +947,8 @@ pg_hba.conf management commands
947
947
  --database DATABASE Database name(s). Multiple database names
948
948
  can be supplied by separating them with
949
949
  commas.
950
- --method TEXT Authentication method. [required]
950
+ --method [trust|reject|md5|password|scram-sha-256|sspi|ident|pam|ldap|radius|peer|gss|cert]
951
+ Authentication method. [required]
951
952
  --user USER User name(s). Multiple user names can be
952
953
  supplied by separating them with commas.
953
954
  --dry-run Simulate change operations.
@@ -970,7 +971,8 @@ pg_hba.conf management commands
970
971
  --database DATABASE Database name(s). Multiple database names
971
972
  can be supplied by separating them with
972
973
  commas.
973
- --method TEXT Authentication method. [required]
974
+ --method [trust|reject|md5|password|scram-sha-256|sspi|ident|pam|ldap|radius|peer|gss|cert]
975
+ Authentication method. [required]
974
976
  --user USER User name(s). Multiple user names can be
975
977
  supplied by separating them with commas.
976
978
  --dry-run Simulate change operations.
@@ -0,0 +1,45 @@
1
+ # SPDX-FileCopyrightText: 2026 Dalibo
2
+ #
3
+ # SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ Site settings and configuration
6
+
7
+ $ export PGLIFT_CLI__LOG_FORMAT="%(levelname)-4s %(message)s"
8
+ $ export PGLIFT_CLI__LOG_LEVEL=info
9
+ $ export PGLIFT_POSTGRESQL__AUTH__PASSFILE=$TMPDIR/pgpass/.pgpass
10
+ $ export PGLIFT_INSTANCE_DEFAULT_NAME=myinstance
11
+ $ export PGLIFT_PREFIX=$TMPDIR
12
+ $ export PGLIFT_RUN_PREFIX=$TMPDIR/run
13
+ $ alias pglift="pglift --log-level=INFO --non-interactive"
14
+
15
+ $ pglift site-configure install
16
+ INFO creating pgpass file directory: $TMPDIR/pgpass
17
+ INFO creating PostgreSQL log directory: $TMPDIR/log/postgresql
18
+
19
+ (Cleanup steps)
20
+ $ trap "pglift --non-interactive instance drop; \
21
+ > pglift --non-interactive site-configure uninstall; \
22
+ > port-for -u postgres" \
23
+ > EXIT
24
+
25
+ Define ports:
26
+ $ PGPORT=$(port-for pg1)
27
+
28
+ Create an instance without a name
29
+ $ pglift instance create --port=$PGPORT
30
+ INFO initializing PostgreSQL
31
+ INFO configuring PostgreSQL authentication
32
+ INFO configuring PostgreSQL
33
+ INFO creating PostgreSQL socket directory directory: $TMPDIR/run/postgresql
34
+ INFO starting PostgreSQL 1\d\/myinstance (re)
35
+ INFO creating instance dumps directory: \$TMPDIR\/srv\/dumps\/1\d-myinstance (re)
36
+
37
+ Cleanup.
38
+ INFO dropping instance 1\d\/myinstance (re)
39
+ INFO stopping PostgreSQL 1\d\/myinstance (re)
40
+ INFO removing entries matching port=\d+ from \$TMPDIR\/pgpass/.pgpass (re)
41
+ INFO deleting PostgreSQL data directory: \$TMPDIR\/srv\/pgsql\/1\d\/myinstance/data (re)
42
+ INFO deleting PostgreSQL WAL directory: \$TMPDIR\/srv\/pgsql\/1\d\/myinstance/wal (re)
43
+ INFO deleting pgpass file parent directory
44
+ INFO deleting PostgreSQL log directory: $TMPDIR/log/postgresql
45
+ INFO deleting PostgreSQL socket directory: $TMPDIR/run/postgresql (no-eol)
@@ -27,7 +27,7 @@
27
27
  $ pglift site-configure install
28
28
  INFO creating base pgBackRest configuration directory: $TMPDIR/etc/pgbackrest
29
29
  INFO installing base pgBackRest configuration
30
- INFO creating pgBackRest include directory
30
+ INFO creating pgBackRest include directory: $TMPDIR/etc/pgbackrest/conf.d
31
31
  INFO creating pgBackRest repository backups and archive directory: $TMPDIR/pgbackrest-not-used
32
32
  INFO creating pgBackRest log directory: $TMPDIR/log/pgbackrest
33
33
  INFO creating pgBackRest spool directory: $TMPDIR/srv/pgbackrest/spool
@@ -57,19 +57,19 @@ With custom ports
57
57
  INFO creating instance dumps directory: \$TMPDIR\/srv\/dumps\/1\d-main (re)
58
58
  INFO starting Prometheus postgres_exporter 1\d-main (re)
59
59
  $ pglift instance create other --port=$PG2PORT --prometheus-port=$PGE1PORT
60
- Usage: pglift instance create [OPTIONS] NAME
60
+ Usage: pglift instance create [OPTIONS] [NAME]
61
61
  Try 'pglift instance create --help' for help.
62
62
 
63
63
  Error: Invalid value for '--prometheus-port': port \d+ already in use (re)
64
64
  [2]
65
65
  $ pglift instance create other --port=$PG1PORT --prometheus-port=$PGE2PORT
66
- Usage: pglift instance create [OPTIONS] NAME
66
+ Usage: pglift instance create [OPTIONS] [NAME]
67
67
  Try 'pglift instance create --help' for help.
68
68
 
69
69
  Error: Invalid value for '--port': port \d+ already in use (re)
70
70
  [2]
71
71
  $ pglift instance create other --port=$PG1PORT --prometheus-port=$PGE1PORT
72
- Usage: pglift instance create [OPTIONS] NAME
72
+ Usage: pglift instance create [OPTIONS] [NAME]
73
73
  Try 'pglift instance create --help' for help.
74
74
 
75
75
  Error: Invalid value for '--prometheus-port': port \d+ already in use (re)
@@ -79,7 +79,8 @@ With custom ports
79
79
  INFO stopping PostgreSQL 1\d\/main (re)
80
80
  INFO stopping Prometheus postgres_exporter 1\d-main (re)
81
81
  INFO deconfiguring Prometheus postgres_exporter 1\d-main (re)
82
- INFO deleting PostgreSQL data and WAL directories
82
+ INFO deleting PostgreSQL data directory: \$TMPDIR\/srv\/pgsql\/1\d\/main\/data (re)
83
+ INFO deleting PostgreSQL WAL directory: \$TMPDIR\/srv\/pgsql\/1\d\/main\/wal (re)
83
84
 
84
85
  With a port set in postgresql.conf template
85
86
 
@@ -99,13 +100,13 @@ With a port set in postgresql.conf template
99
100
  INFO creating instance dumps directory: \$TMPDIR\/srv\/dumps\/1\d-main (re)
100
101
  INFO starting Prometheus postgres_exporter 1\d-main (re)
101
102
  $ pglift instance create other --prometheus-port=$PGE2PORT
102
- Usage: pglift instance create [OPTIONS] NAME
103
+ Usage: pglift instance create [OPTIONS] [NAME]
103
104
  Try 'pglift instance create --help' for help.
104
105
 
105
106
  Error: Invalid value for '--port': port \d+ already in use (re)
106
107
  [2]
107
108
  $ pglift instance create other --port=$PG2PORT --prometheus-port=$PGE1PORT
108
- Usage: pglift instance create [OPTIONS] NAME
109
+ Usage: pglift instance create [OPTIONS] [NAME]
109
110
  Try 'pglift instance create --help' for help.
110
111
 
111
112
  Error: Invalid value for '--prometheus-port': port \d+ already in use (re)
@@ -116,7 +117,8 @@ With a port set in postgresql.conf template
116
117
  INFO stopping PostgreSQL 1\d\/main (re)
117
118
  INFO stopping Prometheus postgres_exporter 1\d-main (re)
118
119
  INFO deconfiguring Prometheus postgres_exporter 1\d-main (re)
119
- INFO deleting PostgreSQL data and WAL directories
120
+ INFO deleting PostgreSQL data directory: \$TMPDIR\/srv\/pgsql\/1\d\/main\/data (re)
121
+ INFO deleting PostgreSQL WAL directory: \$TMPDIR\/srv\/pgsql\/1\d\/main\/wal (re)
120
122
  $ rm -rf $PGLIFT_CONFIG_DIR/postgresql
121
123
 
122
124
  With default ports
@@ -131,7 +133,7 @@ With default ports
131
133
  INFO creating instance dumps directory: \$TMPDIR\/srv\/dumps\/1\d-main (re)
132
134
  INFO starting Prometheus postgres_exporter 1\d-main (re)
133
135
  $ pglift instance create other
134
- Usage: pglift instance create [OPTIONS] NAME
136
+ Usage: pglift instance create [OPTIONS] [NAME]
135
137
  Try 'pglift instance create --help' for help.
136
138
 
137
139
  Error: Invalid value for '--port': port 5432 already in use
@@ -142,12 +144,13 @@ With default ports
142
144
  INFO stopping PostgreSQL 1\d\/main (re)
143
145
  INFO stopping Prometheus postgres_exporter 1\d-main (re)
144
146
  INFO deconfiguring Prometheus postgres_exporter 1\d-main (re)
145
- INFO deleting PostgreSQL data and WAL directories
147
+ INFO deleting PostgreSQL data directory: \$TMPDIR\/srv\/pgsql\/1\d\/main\/data (re)
148
+ INFO deleting PostgreSQL WAL directory: \$TMPDIR\/srv\/pgsql\/1\d\/main\/wal (re)
146
149
 
147
150
  (cleanup)
148
- INFO deleting pgBackRest include directory
151
+ INFO deleting pgBackRest include directory: $TMPDIR/etc/pgbackrest/conf.d
149
152
  INFO uninstalling base pgBackRest configuration
150
- INFO deleting pgBackRest log directory
151
- INFO deleting pgBackRest spool directory
152
- INFO deleting PostgreSQL log directory
153
- INFO deleting PostgreSQL socket directory (no-eol)
153
+ INFO deleting pgBackRest log directory: $TMPDIR/log/pgbackrest
154
+ INFO deleting pgBackRest spool directory: $TMPDIR/srv/pgbackrest/spool
155
+ INFO deleting PostgreSQL log directory: $TMPDIR/log/postgresql
156
+ INFO deleting PostgreSQL socket directory: $TMPDIR/run/postgresql (no-eol)
@@ -44,8 +44,8 @@
44
44
  {
45
45
  "before_header": "$TMPDIR/etc/prometheus/postgres_exporter-test.conf",
46
46
  "after_header": "$TMPDIR/etc/prometheus/postgres_exporter-test.conf",
47
- "before": "DATA_SOURCE_NAME=postgresql://:5432/monitoring\nPOSTGRES_EXPORTER_OPTS='--web.listen-address :* --log.level info'", (glob)
48
- "after": "DATA_SOURCE_NAME=postgresql://prometheus@:5432/monitoring\nPOSTGRES_EXPORTER_OPTS='--web.listen-address :* --log.level info'" (glob)
47
+ "before": "DATA_SOURCE_NAME=postgresql://:5432/monitoring\nPOSTGRES_EXPORTER_OPTS='--web.listen-address :* --log.level info'\n", (glob)
48
+ "after": "DATA_SOURCE_NAME=postgresql://prometheus@:5432/monitoring\nPOSTGRES_EXPORTER_OPTS='--web.listen-address :* --log.level info'\n" (glob)
49
49
  },
50
50
  {
51
51
  "before_header": "$TMPDIR/run/prometheus/test.pid deleted",
@@ -100,4 +100,4 @@ Check port conflicts
100
100
  INFO deconfiguring Prometheus postgres_exporter test
101
101
 
102
102
  (cleanup)
103
- INFO deleting PostgreSQL log directory (no-eol)
103
+ INFO deleting PostgreSQL log directory: $TMPDIR/log/postgresql (no-eol)
@@ -60,7 +60,7 @@ Site settings and configuration
60
60
  $ pglift1 site-configure install
61
61
  INFO creating base pgBackRest configuration directory: $TMPDIR/1/etc/pgbackrest
62
62
  INFO installing base pgBackRest configuration
63
- INFO creating pgBackRest include directory
63
+ INFO creating pgBackRest include directory: $TMPDIR/1/etc/pgbackrest/conf.d
64
64
  INFO creating pgBackRest repository backups and archive directory: $TMPDIR/test-standby-pgbackrest/backups
65
65
  INFO creating pgBackRest log directory: $TMPDIR/1/log/pgbackrest
66
66
  INFO creating pgBackRest spool directory: $TMPDIR/1/srv/pgbackrest/spool
@@ -68,7 +68,7 @@ Site settings and configuration
68
68
  $ pglift2 site-configure install
69
69
  INFO creating base pgBackRest configuration directory: $TMPDIR/2/etc/pgbackrest
70
70
  INFO installing base pgBackRest configuration
71
- INFO creating pgBackRest include directory
71
+ INFO creating pgBackRest include directory: $TMPDIR/2/etc/pgbackrest/conf.d
72
72
  INFO creating pgBackRest log directory: $TMPDIR/2/log/pgbackrest
73
73
  INFO creating pgBackRest spool directory: $TMPDIR/2/srv/pgbackrest/spool
74
74
  INFO creating PostgreSQL log directory: $TMPDIR/2/log/postgresql
@@ -143,7 +143,7 @@ Cannot create a standby with --slot option
143
143
  > --pgbackrest-stanza=stnz \
144
144
  > --standby-for="host=hst" \
145
145
  > --slot=slt
146
- Usage: pglift instance create [OPTIONS] NAME
146
+ Usage: pglift instance create [OPTIONS] [NAME]
147
147
  Try 'pglift instance create --help' for help.
148
148
 
149
149
  Error: Invalid value for '--slot': replication slots cannot be set on a standby instance
@@ -184,7 +184,7 @@ Try to create primary instance with same stanza
184
184
  $ pglift1 instance create err \
185
185
  > --port=$PG3PORT --pgbackrest-stanza=app \
186
186
  > --surole-password=s3per --pgbackrest-password=b@ckUp
187
- Usage: pglift instance create [OPTIONS] NAME
187
+ Usage: pglift instance create [OPTIONS] [NAME]
188
188
  Try 'pglift instance create --help' for help.
189
189
 
190
190
  Error: Invalid value for '--pgbackrest-stanza': Stanza 'app' already bound to another instance \(datadir=\$TMPDIR\/1\/srv\/pgsql\/1\d\/pg1\/data\) (re)
@@ -301,7 +301,8 @@ new slot on the new primary
301
301
  INFO dropping instance 1\d\/pg1 (re)
302
302
  WARNING instance 1\d\/pg1 is already stopped (re)
303
303
  INFO deconfiguring pgBackRest stanza 'app'
304
- INFO deleting PostgreSQL data and WAL directories
304
+ INFO deleting PostgreSQL data directory: \$TMPDIR\/1\/srv\/pgsql\/1\d\/pg1\/data (re)
305
+ INFO deleting PostgreSQL WAL directory: \$TMPDIR\/1\/srv\/pgsql\/1\d\/pg1\/wal (re)
305
306
  $ pglift1 instance create pg3 \
306
307
  > --standby-for="host=$RUN_PREFIX2/postgresql port=$PG2PORT user=replication" \
307
308
  > --standby-password=r3pl \
@@ -352,20 +353,22 @@ Restore on (new) primary:
352
353
  INFO dropping instance 1\d\/pg3 (re)
353
354
  INFO stopping PostgreSQL 1\d\/pg3 (re)
354
355
  INFO deconfiguring pgBackRest stanza 'app'
355
- INFO deleting PostgreSQL data and WAL directories
356
+ INFO deleting PostgreSQL data directory: \$TMPDIR\/1\/srv\/pgsql\/1\d\/pg3\/data (re)
357
+ INFO deleting PostgreSQL WAL directory: \$TMPDIR\/1\/srv\/pgsql\/1\d\/pg3\/wal (re)
356
358
  INFO dropping instance 1\d\/pg2 (re)
357
359
  WARNING instance 1\d\/pg2 is already stopped (re)
358
360
  INFO deconfiguring pgBackRest stanza 'app'
359
- INFO deleting PostgreSQL data and WAL directories
360
- INFO deleting pgBackRest include directory
361
+ INFO deleting PostgreSQL data directory: \$TMPDIR\/2\/srv\/pgsql\/1\d\/pg2\/data (re)
362
+ INFO deleting PostgreSQL WAL directory: \$TMPDIR\/2\/srv\/pgsql\/1\d\/pg2\/wal (re)
363
+ INFO deleting pgBackRest include directory: $TMPDIR/1/etc/pgbackrest/conf.d
361
364
  INFO uninstalling base pgBackRest configuration
362
- INFO deleting pgBackRest log directory
363
- INFO deleting pgBackRest spool directory
364
- INFO deleting PostgreSQL log directory
365
- INFO deleting PostgreSQL socket directory
366
- INFO deleting pgBackRest include directory
365
+ INFO deleting pgBackRest log directory: $TMPDIR/1/log/pgbackrest
366
+ INFO deleting pgBackRest spool directory: $TMPDIR/1/srv/pgbackrest/spool
367
+ INFO deleting PostgreSQL log directory: $TMPDIR/1/log/postgresql
368
+ INFO deleting PostgreSQL socket directory: $TMPDIR/1/run/postgresql
369
+ INFO deleting pgBackRest include directory: $TMPDIR/2/etc/pgbackrest/conf.d
367
370
  INFO uninstalling base pgBackRest configuration
368
- INFO deleting pgBackRest log directory
369
- INFO deleting pgBackRest spool directory
370
- INFO deleting PostgreSQL log directory
371
- INFO deleting PostgreSQL socket directory (no-eol)
371
+ INFO deleting pgBackRest log directory: $TMPDIR/2/log/pgbackrest
372
+ INFO deleting pgBackRest spool directory: $TMPDIR/2/srv/pgbackrest/spool
373
+ INFO deleting PostgreSQL log directory: $TMPDIR/2/log/postgresql
374
+ INFO deleting PostgreSQL socket directory: $TMPDIR/2/run/postgresql (no-eol)
@@ -111,11 +111,13 @@ Try to pause/resume WAL replay on primary
111
111
 
112
112
  INFO dropping instance 1\d\/pg1 (re)
113
113
  INFO stopping PostgreSQL 1\d\/pg1 (re)
114
- INFO deleting PostgreSQL data and WAL directories
114
+ INFO deleting PostgreSQL data directory: \$TMPDIR\/primary\/srv\/pgsql\/1\d\/pg1\/data (re)
115
+ INFO deleting PostgreSQL WAL directory: \$TMPDIR\/primary\/srv\/pgsql\/1\d\/pg1\/wal (re)
115
116
  INFO dropping instance 1\d\/pg2 (re)
116
117
  INFO stopping PostgreSQL 1\d\/pg2 (re)
117
- INFO deleting PostgreSQL data and WAL directories
118
- INFO deleting PostgreSQL log directory
119
- INFO deleting PostgreSQL socket directory
120
- INFO deleting PostgreSQL log directory
121
- INFO deleting PostgreSQL socket directory (no-eol)
118
+ INFO deleting PostgreSQL data directory: \$TMPDIR\/secondary\/srv\/pgsql\/1\d\/pg2\/data (re)
119
+ INFO deleting PostgreSQL WAL directory: \$TMPDIR\/secondary\/srv\/pgsql\/1\d\/pg2\/wal (re)
120
+ INFO deleting PostgreSQL log directory: $TMPDIR/primary/log/postgresql
121
+ INFO deleting PostgreSQL socket directory: $TMPDIR/primary/run/postgresql
122
+ INFO deleting PostgreSQL log directory: $TMPDIR/secondary/log/postgresql
123
+ INFO deleting PostgreSQL socket directory: $TMPDIR/secondary/run/postgresql (no-eol)
@@ -41,7 +41,6 @@ Try to create an instance with a non-existing encoding, triggering a failure in
41
41
  INFO initializing PostgreSQL
42
42
  WARNING Command '['*/bin/pg_ctl', 'init', '-D', '$TMPDIR/srv/pgsql/test/data', '-o', '--auth-host=trust --auth-local=trust --encoding=notanencoding --locale=C --username=postgres --waldir=$TMPDIR/srv/pgsql/test/wal']' returned non-zero exit status 1. (glob)
43
43
  WARNING reverting: initializing PostgreSQL
44
- INFO deleting PostgreSQL data and WAL directories
45
44
  Error: Command '['*/bin/pg_ctl', 'init', '-D', '$TMPDIR/srv/pgsql/test/data', '-o', '--auth-host=trust --auth-local=trust --encoding=notanencoding --locale=C --username=postgres --waldir=$TMPDIR/srv/pgsql/test/wal']' returned non-zero exit status 1. (glob)
46
45
  initdb: error: "notanencoding" is not a valid server encoding name
47
46
  pg_ctl: database system initialization failed
@@ -66,9 +66,11 @@ Create an instance with "password" local authentication method, and upgrade it:
66
66
  (cleanup)
67
67
  INFO dropping instance 1\d\/old (re)
68
68
  WARNING instance 1\d\/old is already stopped (re)
69
- INFO deleting PostgreSQL data and WAL directories
69
+ INFO deleting PostgreSQL data directory: \$TMPDIR\/srv\/pgsql\/1\d\/old\/data (re)
70
+ INFO deleting PostgreSQL WAL directory: \$TMPDIR\/srv\/pgsql\/1\d\/old\/wal (re)
70
71
  INFO dropping instance 1\d\/new (re)
71
72
  INFO stopping PostgreSQL 1\d\/new (re)
72
- INFO deleting PostgreSQL data and WAL directories
73
- INFO deleting PostgreSQL log directory
74
- INFO deleting PostgreSQL socket directory (no-eol)
73
+ INFO deleting PostgreSQL data directory: \$TMPDIR\/srv\/pgsql\/1\d\/new\/data (re)
74
+ INFO deleting PostgreSQL WAL directory: \$TMPDIR\/srv\/pgsql\/1\d\/new\/wal (re)
75
+ INFO deleting PostgreSQL log directory: $TMPDIR/log/postgresql
76
+ INFO deleting PostgreSQL socket directory: $TMPDIR/run/postgresql (no-eol)
@@ -76,6 +76,8 @@ def test_as_parameters(runner: CliRunner) -> None:
76
76
  "Options:\n"
77
77
  " --exclude-none\n"
78
78
  " --nickname TEXT Your secret nickname. [required]\n"
79
+ " --gender [female|male|nonbinary|prefer not to say]\n"
80
+ " Gender. [required]\n"
79
81
  " --age AGE Age.\n"
80
82
  " --address-street STREET Street lines. (Can be used multiple times.)\n"
81
83
  " --address-zip-code ZIP_CODE ZIP code.\n"
@@ -102,6 +104,7 @@ def test_as_parameters(runner: CliRunner) -> None:
102
104
  "alice",
103
105
  "friend",
104
106
  "--exclude-none",
107
+ "--gender=female",
105
108
  "--age=42",
106
109
  "--address-street=bd montparnasse",
107
110
  "--address-street=far far away",
@@ -115,7 +118,6 @@ def test_as_parameters(runner: CliRunner) -> None:
115
118
  "--nickname=aaa",
116
119
  "--phone-numbers=12345",
117
120
  ],
118
- input="alc\nalc\n",
119
121
  )
120
122
  assert result.exit_code == 0, click_result_traceback(result)
121
123
  assert json.loads(result.stderr) == {
@@ -127,6 +129,7 @@ def test_as_parameters(runner: CliRunner) -> None:
127
129
  "zip_code": 0,
128
130
  "primary": True,
129
131
  },
132
+ "gender": "female",
130
133
  "age": 42,
131
134
  "birth": {"date": "1981-02-18"},
132
135
  "is_dead": False,
@@ -158,6 +161,7 @@ def test_as_parameters(runner: CliRunner) -> None:
158
161
  [
159
162
  "foo",
160
163
  "friend",
164
+ "--gender=female",
161
165
  "--age=17",
162
166
  "--birth-date=1987-06-05",
163
167
  "--nickname=aaa",
@@ -187,6 +191,8 @@ def test_as_parameters_update() -> None:
187
191
  "\n"
188
192
  "Options:\n"
189
193
  " --nickname TEXT Your secret nickname. [required]\n"
194
+ " --gender [female|male|nonbinary|prefer not to say]\n"
195
+ " Gender. [required]\n"
190
196
  " --age AGE Age.\n"
191
197
  " --address-zip-code ZIP_CODE ZIP code.\n"
192
198
  " --address-town CITY City.\n"
@@ -210,16 +216,24 @@ def test_as_parameters_update() -> None:
210
216
  ["alice", "--age=5", "--birthdate=2042-02-31"],
211
217
  )
212
218
  assert result.exit_code == 2, result.output
213
- assert "Error: No such option: --birthdate" in result.output
219
+ assert "Error: No such option '--birthdate'" in result.output
214
220
 
215
221
  result = runner.invoke(
216
222
  update_person,
217
- ["alice", "other", "--nickname=a", "--age=5", "--birth-date=1987-06-05"],
223
+ [
224
+ "alice",
225
+ "other",
226
+ "--nickname=a",
227
+ "--gender=female",
228
+ "--age=5",
229
+ "--birth-date=1987-06-05",
230
+ ],
218
231
  )
219
232
  assert result.exit_code == 0, result.output
220
233
  assert json.loads(result.output) == {
221
234
  "name": "alice",
222
235
  "nickname": "**********",
236
+ "gender": "female",
223
237
  "relation": "other",
224
238
  "age": 5,
225
239
  "birth": {"date": "1987-06-05"},
@@ -229,7 +243,14 @@ def test_as_parameters_update() -> None:
229
243
 
230
244
  result = runner.invoke(
231
245
  update_person,
232
- ["alice", "friend", "--nickname=a", "--age=abc", "--birth-date=2010-02-03"],
246
+ [
247
+ "alice",
248
+ "friend",
249
+ "--nickname=a",
250
+ "--gender=female",
251
+ "--age=abc",
252
+ "--birth-date=2010-02-03",
253
+ ],
233
254
  )
234
255
  assert result.exit_code == 2
235
256
  assert (
@@ -243,6 +264,7 @@ def test_as_parameters_update() -> None:
243
264
  "bob",
244
265
  "family",
245
266
  "--nickname=b",
267
+ "--gender=male",
246
268
  "--birth-date=1987-06-05",
247
269
  "--address-town=laville",
248
270
  "--address-country=be",
@@ -263,6 +285,7 @@ def test_as_parameters_update() -> None:
263
285
  "marcel",
264
286
  "other",
265
287
  "--nickname=a",
288
+ "--gender=male",
266
289
  "--age=46",
267
290
  "--birth-date=1978-03-09",
268
291
  "--add-pet=snoopy",
@@ -276,6 +299,7 @@ def test_as_parameters_update() -> None:
276
299
  "name": "marcel",
277
300
  "nickname": "**********",
278
301
  "relation": "other",
302
+ "gender": "male",
279
303
  "age": 46,
280
304
  "birth": {"date": "1978-03-09"},
281
305
  "pets": [
@@ -340,6 +364,7 @@ def test_parse_params_as() -> None:
340
364
  "name": "alice",
341
365
  "relation": "other",
342
366
  "nickname": "la malice",
367
+ "gender": "female",
343
368
  "age": 42,
344
369
  "address": address_params,
345
370
  "birth": {"date": "1976-05-04"},
@@ -348,6 +373,7 @@ def test_parse_params_as() -> None:
348
373
  name="alice",
349
374
  nickname="la malice",
350
375
  relation="other",
376
+ gender="female",
351
377
  age=42,
352
378
  address=address,
353
379
  birth=models.BirthInformation(date=date(1976, 5, 4)), # type: ignore[call-arg]
@@ -358,8 +384,60 @@ def test_parse_params_as() -> None:
358
384
  "name": "alice",
359
385
  "relation": "other",
360
386
  "nickname": "la malice",
387
+ "gender": "female",
361
388
  "age": 42,
362
389
  "birth_date": "1976-05-04",
363
390
  }
364
391
  params_nested.update({f"address_{k}": v for k, v in address_params.items()})
365
392
  assert model.parse_params_as(models.Person, params_nested) == person
393
+
394
+
395
+ def test_as_parameters_unrequired_argument(runner: CliRunner) -> None:
396
+ @click.command("add-product")
397
+ @model.as_parameters(models.Product, "create")
398
+ @click.option("--indent", type=int)
399
+ def add_product(product: models.Product, indent: int) -> None:
400
+ """Add a new product."""
401
+ click.echo(
402
+ product.model_dump_json(by_alias=True, indent=indent),
403
+ err=True,
404
+ )
405
+
406
+ result = runner.invoke(add_product, ["--help"])
407
+ assert result.exit_code == 0, click_result_traceback(result)
408
+ assert result.stdout == (
409
+ "Usage: add-product [OPTIONS] [NAME]\n"
410
+ "\n"
411
+ " Add a new product.\n"
412
+ "\n"
413
+ "Options:\n"
414
+ " --price TEXT Price. [required]\n"
415
+ " --description DESCRIPTION Description for the product.\n"
416
+ " --indent INTEGER\n"
417
+ " --help Show this message and exit.\n"
418
+ )
419
+
420
+ result = runner.invoke(
421
+ add_product,
422
+ [
423
+ "--price=19.5",
424
+ "--description=useless stuff",
425
+ ],
426
+ )
427
+ assert result.exit_code == 0, click_result_traceback(result)
428
+ assert json.loads(result.stderr) == {
429
+ "description": "useless stuff",
430
+ "name": "Unnamed product",
431
+ "price": 19.5,
432
+ }
433
+
434
+ result = runner.invoke(
435
+ add_product,
436
+ ["--price=19.5", "--description=useless stuff", "The Product Name"],
437
+ )
438
+ assert result.exit_code == 0, click_result_traceback(result)
439
+ assert json.loads(result.stderr) == {
440
+ "description": "useless stuff",
441
+ "name": "The Product Name",
442
+ "price": 19.5,
443
+ }
File without changes
File without changes
File without changes
File without changes
File without changes