arcsecond 4.0.0__tar.gz → 4.2.1__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 (121) hide show
  1. {arcsecond-4.0.0 → arcsecond-4.2.1}/PKG-INFO +3 -2
  2. {arcsecond-4.0.0 → arcsecond-4.2.1}/README.md +2 -1
  3. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/cli.py +8 -0
  4. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/docgen.py +48 -2
  5. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/hosting/__init__.py +7 -0
  6. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/hosting/backups.py +34 -26
  7. arcsecond-4.2.1/arcsecond/hosting/check.py +764 -0
  8. arcsecond-4.2.1/arcsecond/hosting/composedoc.py +411 -0
  9. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/hosting/lifecycle.py +1 -1
  10. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/hosting/local.py +44 -4
  11. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/hosting/stack.py +1 -2
  12. arcsecond-4.2.1/arcsecond/hosting/token.py +184 -0
  13. {arcsecond-4.0.0 → arcsecond-4.2.1}/pyproject.toml +1 -1
  14. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/test_docgen.py +13 -0
  15. arcsecond-4.2.1/tests/test_hosting_backups_install_dir.py +89 -0
  16. arcsecond-4.2.1/tests/test_hosting_check.py +447 -0
  17. arcsecond-4.2.1/tests/test_hosting_composedoc.py +110 -0
  18. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/test_hosting_local.py +3 -1
  19. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/test_hosting_stack.py +1 -1
  20. arcsecond-4.2.1/tests/test_hosting_token.py +163 -0
  21. {arcsecond-4.0.0 → arcsecond-4.2.1}/uv.lock +1 -1
  22. {arcsecond-4.0.0 → arcsecond-4.2.1}/.docker/Dockerfile_postgres +0 -0
  23. {arcsecond-4.0.0 → arcsecond-4.2.1}/.docker/Dockerfile_redis +0 -0
  24. {arcsecond-4.0.0 → arcsecond-4.2.1}/.flake8 +0 -0
  25. {arcsecond-4.0.0 → arcsecond-4.2.1}/.gitattributes +0 -0
  26. {arcsecond-4.0.0 → arcsecond-4.2.1}/.github/dependabot.yml +0 -0
  27. {arcsecond-4.0.0 → arcsecond-4.2.1}/.github/workflows/pythonpublish.yml +0 -0
  28. {arcsecond-4.0.0 → arcsecond-4.2.1}/.github/workflows/tests.yml +0 -0
  29. {arcsecond-4.0.0 → arcsecond-4.2.1}/.gitignore +0 -0
  30. {arcsecond-4.0.0 → arcsecond-4.2.1}/LICENSE +0 -0
  31. {arcsecond-4.0.0 → arcsecond-4.2.1}/Makefile +0 -0
  32. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/__init__.py +0 -0
  33. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/__version__.py +0 -0
  34. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/alpaca/__init__.py +0 -0
  35. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/alpaca/commands.py +0 -0
  36. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/alpaca/dome_probe.py +0 -0
  37. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/alpaca/probe.py +0 -0
  38. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/alpaca/telescope_probe.py +0 -0
  39. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/api/__init__.py +0 -0
  40. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/api/config.py +0 -0
  41. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/api/constants.py +0 -0
  42. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/api/endpoint.py +0 -0
  43. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/api/main.py +0 -0
  44. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/api/resources.py +0 -0
  45. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/cloud/__init__.py +0 -0
  46. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/cloud/auth.py +0 -0
  47. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/cloud/resources.py +0 -0
  48. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/cloud/uploader/__init__.py +0 -0
  49. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/cloud/uploader/constants.py +0 -0
  50. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/cloud/uploader/context.py +0 -0
  51. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/cloud/uploader/datafiles/__init__.py +0 -0
  52. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/cloud/uploader/datafiles/context.py +0 -0
  53. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/cloud/uploader/datafiles/errors.py +0 -0
  54. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/cloud/uploader/datafiles/uploader.py +0 -0
  55. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/cloud/uploader/datafiles/utils.py +0 -0
  56. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/cloud/uploader/errors.py +0 -0
  57. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/cloud/uploader/logger.py +0 -0
  58. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/cloud/uploader/uploader.py +0 -0
  59. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/cloud/uploader/utils.py +0 -0
  60. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/cloud/uploader/walker.py +0 -0
  61. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/cloud/uploads.py +0 -0
  62. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/errors.py +0 -0
  63. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/hosting/database.py +0 -0
  64. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/hosting/docker/__init__.py +0 -0
  65. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/hosting/docker/docker-compose.yml +0 -0
  66. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/hosting/utils.py +0 -0
  67. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/imagesources/__init__.py +0 -0
  68. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/imagesources/autostart.py +0 -0
  69. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/imagesources/commands.py +0 -0
  70. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/imagesources/detection.py +0 -0
  71. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/imagesources/proxy.py +0 -0
  72. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/imagesources/registry.py +0 -0
  73. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/imagesources/runtime.py +0 -0
  74. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/imagesources/sources/__init__.py +0 -0
  75. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/imagesources/sources/base.py +0 -0
  76. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/imagesources/sources/filewatch.py +0 -0
  77. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/imagesources/sources/mdns.py +0 -0
  78. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/imagesources/sources/network.py +0 -0
  79. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/imagesources/sources/opencv.py +0 -0
  80. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/imagesources/store.py +0 -0
  81. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/options.py +0 -0
  82. {arcsecond-4.0.0 → arcsecond-4.2.1}/arcsecond/targets.py +0 -0
  83. {arcsecond-4.0.0 → arcsecond-4.2.1}/examples/example_upload_files.py +0 -0
  84. {arcsecond-4.0.0 → arcsecond-4.2.1}/requirements.txt +0 -0
  85. {arcsecond-4.0.0 → arcsecond-4.2.1}/setup.cfg +0 -0
  86. {arcsecond-4.0.0 → arcsecond-4.2.1}/setup.py +0 -0
  87. {arcsecond-4.0.0 → arcsecond-4.2.1}/sonar-project.properties +0 -0
  88. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/__init__.py +0 -0
  89. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/api/__init__.py +0 -0
  90. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/api/test_api.py +0 -0
  91. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/api/test_api_endpoint.py +0 -0
  92. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/api/test_config.py +0 -0
  93. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/api/test_config_pointer.py +0 -0
  94. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/api/test_targets.py +0 -0
  95. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/cloud/__init__.py +0 -0
  96. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/cloud/uploader/__init__.py +0 -0
  97. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/cloud/uploader/datafiles/__init__.py +0 -0
  98. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/cloud/uploader/datafiles/test_uploader_errors.py +0 -0
  99. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/cloud/uploader/datafiles/test_uploader_full_process.py +0 -0
  100. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/cloud/uploader/datafiles/test_uploader_init.py +0 -0
  101. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/cloud/uploader/datafiles/test_uploader_prepare.py +0 -0
  102. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/cloud/uploader/datafiles/test_uploader_upload.py +0 -0
  103. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/conftest.py +0 -0
  104. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/fixtures/file1.fits +0 -0
  105. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/test_alpaca_probe.py +0 -0
  106. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/test_cli.py +0 -0
  107. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/test_cli_api_commands.py +0 -0
  108. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/test_hosting_backups_restore.py +0 -0
  109. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/test_hosting_database.py +0 -0
  110. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/test_hosting_lifecycle.py +0 -0
  111. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/test_hosting_utils_secret_key.py +0 -0
  112. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/test_imagesources_autostart.py +0 -0
  113. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/test_imagesources_commands.py +0 -0
  114. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/test_imagesources_detection.py +0 -0
  115. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/test_imagesources_mdns.py +0 -0
  116. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/test_imagesources_network.py +0 -0
  117. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/test_imagesources_proxy.py +0 -0
  118. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/test_imagesources_store.py +0 -0
  119. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/test_imagesources_webcam.py +0 -0
  120. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/test_targets_planning.py +0 -0
  121. {arcsecond-4.0.0 → arcsecond-4.2.1}/tests/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: arcsecond
3
- Version: 4.0.0
3
+ Version: 4.2.1
4
4
  Summary: CLI for arcsecond.io
5
5
  Project-URL: Homepage, https://github.com/arcsecond-io/cli
6
6
  Project-URL: Issues, https://github.com/arcsecond-io/cli/issues
@@ -56,7 +56,7 @@ The Command-line interface (CLI) for Arcsecond. It can be used as Python
56
56
  module too. The CLI makes it easy to login/register and access Arcsecond resources,
57
57
  public and private ones.
58
58
 
59
- [Read the docs](https://docs.arcsecond.io/cli)
59
+ [Read the docs](https://docs.arcsecond.io/reference/cli)
60
60
 
61
61
  ## Running Arcsecond.local
62
62
 
@@ -65,6 +65,7 @@ The CLI owns the whole lifecycle of a self-hosted installation; there is no
65
65
 
66
66
  ```bash
67
67
  $ arcsecond setup --lan-host 192.168.1.42 # writes .env and docker-compose.yml
68
+ $ arcsecond token set # the access token Arcsecond gave you, once per machine (setup asks too)
68
69
  $ arcsecond start # first run downloads the images
69
70
  $ arcsecond status
70
71
  $ arcsecond logs backend -f
@@ -7,7 +7,7 @@ The Command-line interface (CLI) for Arcsecond. It can be used as Python
7
7
  module too. The CLI makes it easy to login/register and access Arcsecond resources,
8
8
  public and private ones.
9
9
 
10
- [Read the docs](https://docs.arcsecond.io/cli)
10
+ [Read the docs](https://docs.arcsecond.io/reference/cli)
11
11
 
12
12
  ## Running Arcsecond.local
13
13
 
@@ -16,6 +16,7 @@ The CLI owns the whole lifecycle of a self-hosted installation; there is no
16
16
 
17
17
  ```bash
18
18
  $ arcsecond setup --lan-host 192.168.1.42 # writes .env and docker-compose.yml
19
+ $ arcsecond token set # the access token Arcsecond gave you, once per machine (setup asks too)
19
20
  $ arcsecond start # first run downloads the images
20
21
  $ arcsecond status
21
22
  $ arcsecond logs backend -f
@@ -12,6 +12,7 @@ from arcsecond.cloud import (
12
12
  from arcsecond.docgen import docs
13
13
  from arcsecond.hosting import (
14
14
  backups,
15
+ check_cmd,
15
16
  db,
16
17
  logs,
17
18
  restart,
@@ -19,6 +20,7 @@ from arcsecond.hosting import (
19
20
  start,
20
21
  status,
21
22
  stop,
23
+ token_group,
22
24
  update,
23
25
  )
24
26
  from arcsecond.imagesources import commands as imagesources
@@ -72,6 +74,12 @@ main.add_command(status)
72
74
  main.add_command(logs)
73
75
  main.add_command(update)
74
76
 
77
+ # Is it reachable from the rest of the observatory, and if not, why not.
78
+ main.add_command(check_cmd)
79
+
80
+ # The access token Arcsecond gave the observatory, once per machine.
81
+ main.add_command(token_group)
82
+
75
83
  # Browse and restore Arcsecond.local DB backups.
76
84
  main.add_command(backups)
77
85
 
@@ -1,6 +1,6 @@
1
1
  """The command reference, generated from the command tree itself.
2
2
 
3
- `arcsecond docs commands --out DIR` writes one Markdown page per top-level
3
+ `arcsecond docs commands --out docs/reference/commands` writes one Markdown page per top-level
4
4
  command — usage, help, arguments, options, subcommands — plus an index and a
5
5
  machine-readable `commands.json`, every page carrying the documentation
6
6
  site's frontmatter with `source: generated`.
@@ -22,6 +22,7 @@ committed copy honest.
22
22
  """
23
23
 
24
24
  import html
25
+ import inspect
25
26
  import json
26
27
  import textwrap
27
28
  from pathlib import Path
@@ -54,8 +55,13 @@ def _render_help(text: Optional[str]) -> List[str]:
54
55
  Click marks a paragraph it must not rewrap with a backspace character —
55
56
  that is how command examples are written in a docstring — and such a
56
57
  paragraph is exactly what belongs in a code fence here."""
58
+ # Dedented the way click dedents it for --help. Python 3.13 strips a
59
+ # docstring's indentation at compile time and 3.12 does not, so without
60
+ # this the same source generated two different pages depending on the
61
+ # interpreter that ran it.
62
+ text = inspect.cleandoc(text or "")
57
63
  lines: List[str] = []
58
- for paragraph in (text or "").strip().split("\n\n"):
64
+ for paragraph in text.split("\n\n"):
59
65
  if not paragraph.strip():
60
66
  continue
61
67
  if paragraph.lstrip().startswith("\b"):
@@ -293,3 +299,43 @@ def docs_commands(ctx, out, check):
293
299
  )
294
300
  written = write(files, Path(out))
295
301
  click.echo(f"Wrote {len(written)} files to {out}")
302
+
303
+
304
+ @docs.command(
305
+ name="compose",
306
+ help="Write the services and environment reference from the packaged compose template.",
307
+ )
308
+ @click.option(
309
+ "--out",
310
+ "out",
311
+ type=click.Path(file_okay=False),
312
+ default=None,
313
+ help="Folder to write into.",
314
+ )
315
+ @click.option(
316
+ "--check",
317
+ "check",
318
+ type=click.Path(file_okay=False, exists=True),
319
+ default=None,
320
+ help="Compare against this folder instead of writing; exit 1 if it is out of date.",
321
+ )
322
+ def docs_compose(out, check):
323
+ from arcsecond.hosting import composedoc
324
+
325
+ files = composedoc.generate(__version__.__version__)
326
+ if check:
327
+ changed = differences(files, Path(check))
328
+ if changed:
329
+ click.echo("The services/environment reference is out of date:")
330
+ for name in changed:
331
+ click.echo(f" {name}")
332
+ click.echo("\nRegenerate it: arcsecond docs compose --out " + check)
333
+ raise SystemExit(1)
334
+ click.echo(f"The services/environment reference in {check} is up to date.")
335
+ return
336
+ if not out:
337
+ raise click.UsageError(
338
+ "Give --out FOLDER to write, or --check FOLDER to verify."
339
+ )
340
+ written = write(files, Path(out))
341
+ click.echo(f"Wrote {len(written)} files to {out}")
@@ -1,13 +1,20 @@
1
1
  from .backups import backups
2
+
3
+ # check_cmd, not check: `check` is also this package's module of that name,
4
+ # and re-exporting the command under it would shadow the module for tests.
5
+ from .check import check_cmd
2
6
  from .database import db
3
7
  from .lifecycle import logs, restart, start, status, stop, update
4
8
  from .local import setup
9
+ from .token import token_group
5
10
 
6
11
  # Re-exported: the command groups `arcsecond.cli` mounts.
7
12
  __all__ = [
8
13
  "backups",
14
+ "check_cmd",
9
15
  "db",
10
16
  "setup",
17
+ "token_group",
11
18
  "start",
12
19
  "stop",
13
20
  "restart",
@@ -10,9 +10,13 @@ from pathlib import Path
10
10
 
11
11
  import click
12
12
 
13
+ from arcsecond.errors import ArcsecondError
13
14
  from arcsecond.options import basic_options
14
15
 
15
- from .utils import _container_running, _read_env_value
16
+ from . import stack
17
+ from .lifecycle import dir_option
18
+ from .utils import _container_running
19
+ from .utils import _read_env_value as _read_env_value_from
16
20
 
17
21
  DB_CONTAINER = "arcsecond-db"
18
22
  API_CONTAINER = "arcsecond-api"
@@ -44,8 +48,16 @@ STATUS_BROKEN = "broken"
44
48
  GZIP_MAGIC = b"\x1f\x8b"
45
49
 
46
50
 
47
- def _cwd_env_path():
48
- return Path.cwd() / ".env"
51
+ # The installation the current command works on, resolved once by
52
+ # _ensure_install_dir: --dir, else the current folder, else the folder
53
+ # `arcsecond setup` last ran in (see stack.resolve_install_dir).
54
+ _install = None
55
+
56
+
57
+ def _read_env_value(key):
58
+ """A value from the installation's .env — the resolved one, not whatever
59
+ happens to be in the current folder."""
60
+ return _read_env_value_from(key, _install.env_path if _install else None)
49
61
 
50
62
 
51
63
  def _backups_dir():
@@ -55,18 +67,13 @@ def _backups_dir():
55
67
  return Path(shared) / "db_backups"
56
68
 
57
69
 
58
- def _print_wrong_dir_hint():
59
- click.echo(
60
- "Could not find an Arcsecond.local installation in the current directory.\n"
61
- "Run `arcsecond backups ...` from the directory that contains your "
62
- "docker-compose.yml and .env files (the one you used for `arcsecond setup`)."
63
- )
64
-
65
-
66
- def _ensure_install_dir():
70
+ def _ensure_install_dir(directory=None):
67
71
  """Return the backups dir, or None after printing a clear error."""
68
- if not _cwd_env_path().exists():
69
- _print_wrong_dir_hint()
72
+ global _install
73
+ try:
74
+ _install = stack.resolve_install_dir(directory)
75
+ except ArcsecondError as e:
76
+ click.echo(str(e))
70
77
  return None
71
78
  backups_dir = _backups_dir()
72
79
  if backups_dir is None:
@@ -78,9 +85,8 @@ def _ensure_install_dir():
78
85
  if not backups_dir.exists():
79
86
  click.echo(
80
87
  f"No backups directory found at: {backups_dir.resolve()}\n"
81
- "Make sure you run this command from the Arcsecond.local working directory "
82
- "(the one with docker-compose.yml and .env), and that the backend has been "
83
- "started at least once — the boot-time backup is the first to appear."
88
+ "Make sure the backend has been started at least once — the boot-time "
89
+ "backup is the first to appear."
84
90
  )
85
91
  return None
86
92
  return backups_dir
@@ -569,9 +575,10 @@ def _print_destination_section(items):
569
575
  @backups.command(
570
576
  name="list", help="List available DB backups with compatibility status."
571
577
  )
578
+ @dir_option
572
579
  @basic_options
573
- def list_cmd():
574
- backups_dir = _ensure_install_dir()
580
+ def list_cmd(directory):
581
+ backups_dir = _ensure_install_dir(directory)
575
582
  if backups_dir is None:
576
583
  sys.exit(1)
577
584
 
@@ -624,9 +631,10 @@ def list_cmd():
624
631
 
625
632
  @backups.command(name="inspect", help="Show detailed info about a backup.")
626
633
  @click.argument("ref", required=True)
634
+ @dir_option
627
635
  @basic_options
628
- def inspect_cmd(ref):
629
- backups_dir = _ensure_install_dir()
636
+ def inspect_cmd(directory, ref):
637
+ backups_dir = _ensure_install_dir(directory)
630
638
  if backups_dir is None:
631
639
  sys.exit(1)
632
640
 
@@ -1085,9 +1093,8 @@ def _stop_services(dry_run):
1085
1093
 
1086
1094
  def _start_services(dry_run):
1087
1095
  """Bring the stack back. Compose restarts dependents; by name if it is absent."""
1088
- compose_file = Path.cwd() / "docker-compose.yml"
1089
- if compose_file.exists():
1090
- _run(["docker", "compose", "up", "-d"], dry_run)
1096
+ if _install is not None and _install.compose_path.exists():
1097
+ _run(stack.compose_command(_install, "up", "-d", "--remove-orphans"), dry_run)
1091
1098
  else:
1092
1099
  for name in reversed(SERVICES_TO_STOP):
1093
1100
  _run(["docker", "start", name], dry_run, capture_output=True, text=True)
@@ -1149,9 +1156,10 @@ def _perform_restore(path, backups_dir, dry_run, no_safety_backup):
1149
1156
  @click.option(
1150
1157
  "--no-safety-backup", is_flag=True, help="Skip the pre-restore safety snapshot."
1151
1158
  )
1159
+ @dir_option
1152
1160
  @basic_options
1153
- def restore_cmd(ref, force, dry_run, no_safety_backup):
1154
- backups_dir = _ensure_install_dir()
1161
+ def restore_cmd(directory, ref, force, dry_run, no_safety_backup):
1162
+ backups_dir = _ensure_install_dir(directory)
1155
1163
  if backups_dir is None:
1156
1164
  sys.exit(1)
1157
1165