doover-cli 0.4.3__tar.gz → 0.5.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 (88) hide show
  1. {doover_cli-0.4.3 → doover_cli-0.5.1}/PKG-INFO +1 -1
  2. {doover_cli-0.4.3 → doover_cli-0.5.1}/pyproject.toml +1 -1
  3. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/apps/apps.py +42 -28
  4. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/config_schema.py +20 -5
  5. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/ui_schema.py +20 -5
  6. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/utils/apps.py +27 -3
  7. {doover_cli-0.4.3 → doover_cli-0.5.1}/tests/test_apps.py +42 -0
  8. {doover_cli-0.4.3 → doover_cli-0.5.1}/tests/test_utils_apps.py +8 -1
  9. {doover_cli-0.4.3 → doover_cli-0.5.1}/uv.lock +1 -1
  10. {doover_cli-0.4.3 → doover_cli-0.5.1}/.github/CONTRIBUTING.md +0 -0
  11. {doover_cli-0.4.3 → doover_cli-0.5.1}/.github/ISSUE_TEMPLATE/bug-report.yaml +0 -0
  12. {doover_cli-0.4.3 → doover_cli-0.5.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  13. {doover_cli-0.4.3 → doover_cli-0.5.1}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
  14. {doover_cli-0.4.3 → doover_cli-0.5.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  15. {doover_cli-0.4.3 → doover_cli-0.5.1}/.github/workflows/lint-test.yml +0 -0
  16. {doover_cli-0.4.3 → doover_cli-0.5.1}/.github/workflows/release-brew.yml +0 -0
  17. {doover_cli-0.4.3 → doover_cli-0.5.1}/.github/workflows/release-pypi.yml +0 -0
  18. {doover_cli-0.4.3 → doover_cli-0.5.1}/.github/workflows/release.yml +0 -0
  19. {doover_cli-0.4.3 → doover_cli-0.5.1}/.gitignore +0 -0
  20. {doover_cli-0.4.3 → doover_cli-0.5.1}/.pre-commit-config.yaml +0 -0
  21. {doover_cli-0.4.3 → doover_cli-0.5.1}/AGENT.md +0 -0
  22. {doover_cli-0.4.3 → doover_cli-0.5.1}/LICENSE +0 -0
  23. {doover_cli-0.4.3 → doover_cli-0.5.1}/README.md +0 -0
  24. {doover_cli-0.4.3 → doover_cli-0.5.1}/debian/changelog +0 -0
  25. {doover_cli-0.4.3 → doover_cli-0.5.1}/debian/control +0 -0
  26. {doover_cli-0.4.3 → doover_cli-0.5.1}/debian/rules +0 -0
  27. {doover_cli-0.4.3 → doover_cli-0.5.1}/docs/user_stories.md +0 -0
  28. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/__init__.py +0 -0
  29. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/agent.py +0 -0
  30. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/api/__init__.py +0 -0
  31. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/api/auth.py +0 -0
  32. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/api/errors.py +0 -0
  33. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/api/session.py +0 -0
  34. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/apps/app_install.py +0 -0
  35. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/apps/device.py +0 -0
  36. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/apps/device_type.py +0 -0
  37. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/apps/tunnel.py +0 -0
  38. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/channel.py +0 -0
  39. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/colours.py +0 -0
  40. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/dda_logs.py +0 -0
  41. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/doover_config.py +0 -0
  42. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/grpc.py +0 -0
  43. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/login.py +0 -0
  44. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/renderer/__init__.py +0 -0
  45. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/renderer/_base.py +0 -0
  46. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/renderer/_basic.py +0 -0
  47. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/renderer/_default.py +0 -0
  48. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/renderer/_json.py +0 -0
  49. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/report.py +0 -0
  50. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/simulator.py +0 -0
  51. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/user.py +0 -0
  52. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/utils/__init__.py +0 -0
  53. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/utils/api.py +0 -0
  54. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/utils/context.py +0 -0
  55. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/utils/crud/__init__.py +0 -0
  56. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/utils/crud/commands.py +0 -0
  57. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/utils/crud/lookup.py +0 -0
  58. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/utils/crud/prompting.py +0 -0
  59. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/utils/crud/schema.py +0 -0
  60. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/utils/crud/values.py +0 -0
  61. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/utils/errors.py +0 -0
  62. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/utils/formatters.py +0 -0
  63. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/utils/misc.py +0 -0
  64. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/utils/parsers.py +0 -0
  65. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/utils/prompt.py +0 -0
  66. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/utils/sentry.py +0 -0
  67. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/utils/shell_commands.py +0 -0
  68. {doover_cli-0.4.3 → doover_cli-0.5.1}/src/doover_cli/utils/state.py +0 -0
  69. {doover_cli-0.4.3 → doover_cli-0.5.1}/tests/__init__.py +0 -0
  70. {doover_cli-0.4.3 → doover_cli-0.5.1}/tests/conftest.py +0 -0
  71. {doover_cli-0.4.3 → doover_cli-0.5.1}/tests/test_agent.py +0 -0
  72. {doover_cli-0.4.3 → doover_cli-0.5.1}/tests/test_app_install.py +0 -0
  73. {doover_cli-0.4.3 → doover_cli-0.5.1}/tests/test_auth_integration.py +0 -0
  74. {doover_cli-0.4.3 → doover_cli-0.5.1}/tests/test_auth_unit.py +0 -0
  75. {doover_cli-0.4.3 → doover_cli-0.5.1}/tests/test_basic.py +0 -0
  76. {doover_cli-0.4.3 → doover_cli-0.5.1}/tests/test_crud_commands.py +0 -0
  77. {doover_cli-0.4.3 → doover_cli-0.5.1}/tests/test_crud_lookup.py +0 -0
  78. {doover_cli-0.4.3 → doover_cli-0.5.1}/tests/test_crud_prompting.py +0 -0
  79. {doover_cli-0.4.3 → doover_cli-0.5.1}/tests/test_crud_schema.py +0 -0
  80. {doover_cli-0.4.3 → doover_cli-0.5.1}/tests/test_crud_values.py +0 -0
  81. {doover_cli-0.4.3 → doover_cli-0.5.1}/tests/test_default_renderer.py +0 -0
  82. {doover_cli-0.4.3 → doover_cli-0.5.1}/tests/test_device.py +0 -0
  83. {doover_cli-0.4.3 → doover_cli-0.5.1}/tests/test_device_cli_integration.py +0 -0
  84. {doover_cli-0.4.3 → doover_cli-0.5.1}/tests/test_device_type.py +0 -0
  85. {doover_cli-0.4.3 → doover_cli-0.5.1}/tests/test_device_type_cli_integration.py +0 -0
  86. {doover_cli-0.4.3 → doover_cli-0.5.1}/tests/test_prompt.py +0 -0
  87. {doover_cli-0.4.3 → doover_cli-0.5.1}/tests/test_sentry.py +0 -0
  88. {doover_cli-0.4.3 → doover_cli-0.5.1}/tests/test_user.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: doover-cli
3
- Version: 0.4.3
3
+ Version: 0.5.1
4
4
  Summary: CLI for Doover
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.11
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "doover-cli"
3
- version = "0.4.3"
3
+ version = "0.5.1"
4
4
  description = "CLI for Doover"
5
5
  requires-python = ">=3.11"
6
6
  dependencies = [
@@ -31,6 +31,7 @@ from ..utils.apps import (
31
31
  call_with_uv,
32
32
  get_docker_path,
33
33
  get_app_config,
34
+ preserve_file,
34
35
  )
35
36
  from ..utils.crud import (
36
37
  build_update_command,
@@ -961,35 +962,40 @@ def publish(
961
962
  # Resolve the app name early so subsequent get_app_config calls don't re-prompt.
962
963
  app_config = get_app_config(root_fp, app_name=app_name)
963
964
 
964
- if export_config:
965
- try:
966
- ctx.invoke(export_config_command, ctx, app_fp=root_fp, validate_=True)
967
- except jsonschema.exceptions.SchemaError as exc:
968
- summary, remainder = str(exc).split("\n", 1)
969
- rich.print(
970
- f"[red]Failed to export application configuration: {summary}[/red]\n{remainder}\n"
971
- )
972
- typer.confirm("Do you want to continue?", abort=True)
973
- else:
974
- rich.print("[green]Exported application configuration.[/green]")
975
- app_config = get_app_config(root_fp)
976
-
977
- if export_ui and _should_export_ui(app_config):
978
- try:
979
- ctx.invoke(export_ui_command, ctx, app_fp=root_fp, validate_=True)
980
- except Exception as exc:
981
- rich.print(f"[red]Failed to export UI schema: {exc}[/red]\n")
982
- typer.confirm("Do you want to continue?", abort=True)
983
- else:
984
- rich.print("[green]Exported UI schema.[/green]")
965
+ # Generate the schema from the Python into doover_config.json so the upload
966
+ # payload carries the current source, then restore the file -- publishing
967
+ # never leaves generated config_schema/ui_schema in the working tree. The
968
+ # Python is the source of truth; the JSON is regenerated on demand.
969
+ with preserve_file(root_fp / "doover_config.json"):
970
+ if export_config:
971
+ try:
972
+ ctx.invoke(export_config_command, ctx, app_fp=root_fp, validate_=True)
973
+ except jsonschema.exceptions.SchemaError as exc:
974
+ summary, remainder = str(exc).split("\n", 1)
975
+ rich.print(
976
+ f"[red]Failed to export application configuration: {summary}[/red]\n{remainder}\n"
977
+ )
978
+ typer.confirm("Do you want to continue?", abort=True)
979
+ else:
980
+ rich.print("[green]Exported application configuration.[/green]")
981
+ app_config = get_app_config(root_fp)
985
982
 
986
- app_config = get_app_config(root_fp)
987
- resolved_staging = _resolve_staging(staging)
988
- payload = _build_application_payload(
989
- app_config,
990
- staging=resolved_staging,
991
- include_deployment_data=True,
992
- )
983
+ if export_ui and _should_export_ui(app_config):
984
+ try:
985
+ ctx.invoke(export_ui_command, ctx, app_fp=root_fp, validate_=True)
986
+ except Exception as exc:
987
+ rich.print(f"[red]Failed to export UI schema: {exc}[/red]\n")
988
+ typer.confirm("Do you want to continue?", abort=True)
989
+ else:
990
+ rich.print("[green]Exported UI schema.[/green]")
991
+
992
+ app_config = get_app_config(root_fp)
993
+ resolved_staging = _resolve_staging(staging)
994
+ payload = _build_application_payload(
995
+ app_config,
996
+ staging=resolved_staging,
997
+ include_deployment_data=True,
998
+ )
993
999
 
994
1000
  client, renderer = get_state()
995
1001
 
@@ -1111,6 +1117,13 @@ def release(
1111
1117
  ),
1112
1118
  ] = None,
1113
1119
  notes: Annotated[str, typer.Option(help="Optional release notes.")] = "",
1120
+ alpha: Annotated[
1121
+ bool,
1122
+ typer.Option(
1123
+ help="Publish as a prerelease: the version is created but not "
1124
+ "auto-selected as 'Latest' (reachable only by pinning it explicitly)."
1125
+ ),
1126
+ ] = False,
1114
1127
  staging: Annotated[
1115
1128
  bool | None,
1116
1129
  typer.Option(help="Force staging mode (default: inferred from the API URL)."),
@@ -1155,6 +1168,7 @@ def release(
1155
1168
  tag=tag or "",
1156
1169
  commit=resolved_commit or "",
1157
1170
  notes=notes or "",
1171
+ alpha=alpha,
1158
1172
  )
1159
1173
  rich.print("[green]Released a new application version.[/green]")
1160
1174
  renderer.render(version)
@@ -7,7 +7,12 @@ import rich
7
7
  import typer
8
8
  import jsf
9
9
 
10
- from .utils.apps import get_app_directory, call_with_uv, get_app_config
10
+ from .utils.apps import (
11
+ get_app_directory,
12
+ call_with_uv,
13
+ get_app_config,
14
+ preserve_file,
15
+ )
11
16
 
12
17
  app = typer.Typer(no_args_is_help=True)
13
18
 
@@ -63,18 +68,28 @@ def validate(
63
68
  export_: Annotated[
64
69
  bool,
65
70
  typer.Option(
66
- "--export",
67
- help="Export the configuration before validating.",
71
+ "--export/--no-export",
72
+ help="Regenerate the schema from the Python before validating. "
73
+ "The export is transient -- doover_config.json is restored "
74
+ "afterwards, so validation never writes to the working tree.",
68
75
  ),
69
76
  ] = True,
70
77
  ):
71
78
  """Validate application config is a valid JSON schema."""
72
79
  root_fp = get_app_directory(app_fp)
80
+ config_file = root_fp / "doover_config.json"
73
81
 
74
82
  if export_ is True:
75
- ctx.invoke(export, ctx, app_fp=root_fp, validate_=False)
83
+ # Validate the schema the Python *currently* generates, without leaving
84
+ # doover_config.json modified (e.g. when run as a pre-commit hook).
85
+ with preserve_file(config_file):
86
+ ctx.invoke(export, ctx, app_fp=root_fp, validate_=False)
87
+ _validate_config_file(config_file)
88
+ else:
89
+ _validate_config_file(config_file)
76
90
 
77
- config_file = root_fp / "doover_config.json"
91
+
92
+ def _validate_config_file(config_file: Path):
78
93
  if not config_file.exists():
79
94
  raise FileNotFoundError(
80
95
  "doover_config.json not found. Please ensure there is a doover_config.json file in the application directory."
@@ -6,7 +6,12 @@ from typing import Annotated
6
6
  import rich
7
7
  import typer
8
8
 
9
- from .utils.apps import get_app_directory, call_with_uv, get_app_config
9
+ from .utils.apps import (
10
+ get_app_directory,
11
+ call_with_uv,
12
+ get_app_config,
13
+ preserve_file,
14
+ )
10
15
 
11
16
  app = typer.Typer(no_args_is_help=True)
12
17
 
@@ -55,18 +60,28 @@ def validate(
55
60
  export_: Annotated[
56
61
  bool,
57
62
  typer.Option(
58
- "--export",
59
- help="Export the UI schema before validating.",
63
+ "--export/--no-export",
64
+ help="Regenerate the schema from the Python before validating. "
65
+ "The export is transient -- doover_config.json is restored "
66
+ "afterwards, so validation never writes to the working tree.",
60
67
  ),
61
68
  ] = True,
62
69
  ):
63
70
  """Validate application UI schema is valid JSON."""
64
71
  root_fp = get_app_directory(app_fp)
72
+ config_file = root_fp / "doover_config.json"
65
73
 
66
74
  if export_ is True:
67
- ctx.invoke(export, ctx, app_fp=root_fp, validate_=False)
75
+ # Validate the schema the Python *currently* generates, without leaving
76
+ # doover_config.json modified (e.g. when run as a pre-commit hook).
77
+ with preserve_file(config_file):
78
+ ctx.invoke(export, ctx, app_fp=root_fp, validate_=False)
79
+ _validate_ui_file(config_file)
80
+ else:
81
+ _validate_ui_file(config_file)
68
82
 
69
- config_file = root_fp / "doover_config.json"
83
+
84
+ def _validate_ui_file(config_file: Path):
70
85
  if not config_file.exists():
71
86
  raise FileNotFoundError(
72
87
  "doover_config.json not found. Please ensure there is a doover_config.json file in the application directory."
@@ -2,6 +2,7 @@ import os
2
2
  import json
3
3
  import base64
4
4
  import shutil
5
+ import contextlib
5
6
  from pathlib import Path
6
7
  from typing import Any
7
8
 
@@ -19,6 +20,27 @@ def get_id_or_key(data: dict[str, Any], key: str) -> Any:
19
20
  return data.get(key)
20
21
 
21
22
 
23
+ @contextlib.contextmanager
24
+ def preserve_file(path: Path):
25
+ """Snapshot a file (or its absence) and restore it on exit.
26
+
27
+ Lets a command generate schema into ``doover_config.json`` transiently --
28
+ to validate it or build an upload payload from it -- without leaving the
29
+ working tree modified. This keeps generated ``config_schema``/``ui_schema``
30
+ out of git: the Python is the source of truth, the JSON is regenerated on
31
+ demand. Restores on exceptions too, so a failed run never dirties the tree.
32
+ """
33
+ existed = path.exists()
34
+ original = path.read_text() if existed else None
35
+ try:
36
+ yield
37
+ finally:
38
+ if existed:
39
+ path.write_text(original)
40
+ elif path.exists():
41
+ path.unlink()
42
+
43
+
22
44
  class LocalApplication(ControlApplication):
23
45
  _model_name = None
24
46
 
@@ -366,14 +388,16 @@ def get_app_config(root_fp: Path, app_name: str | None = None) -> Any:
366
388
 
367
389
  result = []
368
390
  for k, v in data.items():
369
- if isinstance(v, dict) and "config_schema" in v:
370
- # config_schema bit of a prerequisite for an app config entry.
391
+ if isinstance(v, dict) and "type" in v:
392
+ # `type` (e.g. DEV/PRO) marks an app entry. Don't key off
393
+ # config_schema -- it's generated from the Python and may be
394
+ # absent from the committed file.
371
395
  result.append(LocalApplication.from_config(v, root_fp))
372
396
 
373
397
  if len(result) == 0:
374
398
  print(
375
399
  f"No application configuration found in the `doover_config.json` file at {root_fp}. "
376
- f"Make sure the `type` is set to `application` in the configuration."
400
+ f"Make sure each application entry has a `type` set in the configuration."
377
401
  )
378
402
  raise typer.Exit(1)
379
403
 
@@ -943,6 +943,48 @@ def test_app_publish_processor_builds_package_without_release(monkeypatch, tmp_p
943
943
  assert renderer.render_calls == [{"id": 303}]
944
944
 
945
945
 
946
+ def test_app_release_passes_alpha_flag(monkeypatch, tmp_path):
947
+ captured = {}
948
+ renderer = FakeRenderer()
949
+ app_config = FakeAppConfig(app_id=404)
950
+
951
+ class FakeControlClient:
952
+ @staticmethod
953
+ def create_application_version(application_id, **kwargs):
954
+ captured["application_id"] = application_id
955
+ captured["kwargs"] = kwargs
956
+ return {"id": 1, "number": 1, "alpha": kwargs.get("alpha")}
957
+
958
+ monkeypatch.setattr(
959
+ "doover_cli.apps.apps.get_app_directory", lambda root=None: tmp_path
960
+ )
961
+ monkeypatch.setattr(
962
+ "doover_cli.apps.apps.get_app_config", lambda root_fp, app_name=None: app_config
963
+ )
964
+ monkeypatch.setattr(
965
+ "doover_cli.apps.apps.get_state", lambda: (FakeControlClient(), renderer)
966
+ )
967
+ monkeypatch.setattr(
968
+ "doover_cli.apps.apps._detect_git_commit", lambda root_fp: "deadbeef"
969
+ )
970
+
971
+ result = runner.invoke(
972
+ app, ["app", "release", str(tmp_path), "--digest", "sha256:abc", "--alpha"]
973
+ )
974
+
975
+ assert result.exit_code == 0
976
+ assert captured["application_id"] == 404
977
+ assert captured["kwargs"]["alpha"] is True
978
+
979
+ # ...and defaults to False when the flag is omitted.
980
+ captured.clear()
981
+ result = runner.invoke(
982
+ app, ["app", "release", str(tmp_path), "--digest", "sha256:abc"]
983
+ )
984
+ assert result.exit_code == 0
985
+ assert captured["kwargs"]["alpha"] is False
986
+
987
+
946
988
  def test_publish_processor_package_uses_package_zip(tmp_path):
947
989
  package_fp = tmp_path / "package.zip"
948
990
  package_fp.write_bytes(b"zip-bytes")
@@ -63,10 +63,12 @@ def test_get_app_config_caches_explicit_app_name(monkeypatch, tmp_path):
63
63
  {
64
64
  "vega_level_sensor": {
65
65
  "name": "vega_level_sensor",
66
+ "type": "DEV",
66
67
  "config_schema": {"type": "object"},
67
68
  },
68
69
  "farm_water_dashboard": {
69
70
  "name": "farm_water_dashboard",
71
+ "type": "DEV",
70
72
  "config_schema": {"type": "object"},
71
73
  },
72
74
  }
@@ -85,17 +87,21 @@ def test_get_app_config_caches_explicit_app_name(monkeypatch, tmp_path):
85
87
  assert cached_config.name == "farm_water_dashboard"
86
88
 
87
89
 
88
- def test_get_app_config_rejects_unknown_explicit_app_name(monkeypatch, tmp_path, capsys):
90
+ def test_get_app_config_rejects_unknown_explicit_app_name(
91
+ monkeypatch, tmp_path, capsys
92
+ ):
89
93
  config_path = tmp_path / "doover_config.json"
90
94
  config_path.write_text(
91
95
  json.dumps(
92
96
  {
93
97
  "vega_level_sensor": {
94
98
  "name": "vega_level_sensor",
99
+ "type": "DEV",
95
100
  "config_schema": {"type": "object"},
96
101
  },
97
102
  "farm_water_dashboard": {
98
103
  "name": "farm_water_dashboard",
104
+ "type": "DEV",
99
105
  "config_schema": {"type": "object"},
100
106
  },
101
107
  }
@@ -124,6 +130,7 @@ def test_get_app_config_rejects_unknown_explicit_app_name_with_single_app(
124
130
  {
125
131
  "tracker-app": {
126
132
  "name": "tracker-app",
133
+ "type": "DEV",
127
134
  "config_schema": {"type": "object"},
128
135
  },
129
136
  }
@@ -544,7 +544,7 @@ wheels = [
544
544
 
545
545
  [[package]]
546
546
  name = "doover-cli"
547
- version = "0.4.3"
547
+ version = "0.5.1"
548
548
  source = { editable = "." }
549
549
  dependencies = [
550
550
  { name = "click" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes