doover-cli 0.5.2__tar.gz → 0.5.4__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.5.2 → doover_cli-0.5.4}/PKG-INFO +1 -1
  2. {doover_cli-0.5.2 → doover_cli-0.5.4}/pyproject.toml +4 -1
  3. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/__init__.py +2 -0
  4. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/apps/apps.py +25 -2
  5. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/config_schema.py +17 -3
  6. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/ui_schema.py +16 -2
  7. {doover_cli-0.5.2 → doover_cli-0.5.4}/tests/test_apps.py +45 -0
  8. {doover_cli-0.5.2 → doover_cli-0.5.4}/uv.lock +0 -1
  9. {doover_cli-0.5.2 → doover_cli-0.5.4}/.github/CONTRIBUTING.md +0 -0
  10. {doover_cli-0.5.2 → doover_cli-0.5.4}/.github/ISSUE_TEMPLATE/bug-report.yaml +0 -0
  11. {doover_cli-0.5.2 → doover_cli-0.5.4}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  12. {doover_cli-0.5.2 → doover_cli-0.5.4}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
  13. {doover_cli-0.5.2 → doover_cli-0.5.4}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  14. {doover_cli-0.5.2 → doover_cli-0.5.4}/.github/workflows/lint-test.yml +0 -0
  15. {doover_cli-0.5.2 → doover_cli-0.5.4}/.github/workflows/release-brew.yml +0 -0
  16. {doover_cli-0.5.2 → doover_cli-0.5.4}/.github/workflows/release-pypi.yml +0 -0
  17. {doover_cli-0.5.2 → doover_cli-0.5.4}/.github/workflows/release.yml +0 -0
  18. {doover_cli-0.5.2 → doover_cli-0.5.4}/.gitignore +0 -0
  19. {doover_cli-0.5.2 → doover_cli-0.5.4}/.pre-commit-config.yaml +0 -0
  20. {doover_cli-0.5.2 → doover_cli-0.5.4}/AGENT.md +0 -0
  21. {doover_cli-0.5.2 → doover_cli-0.5.4}/LICENSE +0 -0
  22. {doover_cli-0.5.2 → doover_cli-0.5.4}/README.md +0 -0
  23. {doover_cli-0.5.2 → doover_cli-0.5.4}/debian/changelog +0 -0
  24. {doover_cli-0.5.2 → doover_cli-0.5.4}/debian/control +0 -0
  25. {doover_cli-0.5.2 → doover_cli-0.5.4}/debian/rules +0 -0
  26. {doover_cli-0.5.2 → doover_cli-0.5.4}/docs/user_stories.md +0 -0
  27. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/agent.py +0 -0
  28. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/api/__init__.py +0 -0
  29. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/api/auth.py +0 -0
  30. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/api/errors.py +0 -0
  31. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/api/session.py +0 -0
  32. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/apps/app_install.py +0 -0
  33. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/apps/device.py +0 -0
  34. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/apps/device_type.py +0 -0
  35. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/apps/tunnel.py +0 -0
  36. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/channel.py +0 -0
  37. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/colours.py +0 -0
  38. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/dda_logs.py +0 -0
  39. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/doover_config.py +0 -0
  40. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/grpc.py +0 -0
  41. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/login.py +0 -0
  42. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/renderer/__init__.py +0 -0
  43. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/renderer/_base.py +0 -0
  44. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/renderer/_basic.py +0 -0
  45. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/renderer/_default.py +0 -0
  46. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/renderer/_json.py +0 -0
  47. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/report.py +0 -0
  48. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/simulator.py +0 -0
  49. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/user.py +0 -0
  50. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/utils/__init__.py +0 -0
  51. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/utils/api.py +0 -0
  52. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/utils/apps.py +0 -0
  53. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/utils/context.py +0 -0
  54. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/utils/crud/__init__.py +0 -0
  55. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/utils/crud/commands.py +0 -0
  56. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/utils/crud/lookup.py +0 -0
  57. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/utils/crud/prompting.py +0 -0
  58. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/utils/crud/schema.py +0 -0
  59. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/utils/crud/values.py +0 -0
  60. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/utils/errors.py +0 -0
  61. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/utils/formatters.py +0 -0
  62. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/utils/misc.py +0 -0
  63. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/utils/parsers.py +0 -0
  64. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/utils/prompt.py +0 -0
  65. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/utils/sentry.py +0 -0
  66. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/utils/shell_commands.py +0 -0
  67. {doover_cli-0.5.2 → doover_cli-0.5.4}/src/doover_cli/utils/state.py +0 -0
  68. {doover_cli-0.5.2 → doover_cli-0.5.4}/tests/__init__.py +0 -0
  69. {doover_cli-0.5.2 → doover_cli-0.5.4}/tests/conftest.py +0 -0
  70. {doover_cli-0.5.2 → doover_cli-0.5.4}/tests/test_agent.py +0 -0
  71. {doover_cli-0.5.2 → doover_cli-0.5.4}/tests/test_app_install.py +0 -0
  72. {doover_cli-0.5.2 → doover_cli-0.5.4}/tests/test_auth_integration.py +0 -0
  73. {doover_cli-0.5.2 → doover_cli-0.5.4}/tests/test_auth_unit.py +0 -0
  74. {doover_cli-0.5.2 → doover_cli-0.5.4}/tests/test_basic.py +0 -0
  75. {doover_cli-0.5.2 → doover_cli-0.5.4}/tests/test_crud_commands.py +0 -0
  76. {doover_cli-0.5.2 → doover_cli-0.5.4}/tests/test_crud_lookup.py +0 -0
  77. {doover_cli-0.5.2 → doover_cli-0.5.4}/tests/test_crud_prompting.py +0 -0
  78. {doover_cli-0.5.2 → doover_cli-0.5.4}/tests/test_crud_schema.py +0 -0
  79. {doover_cli-0.5.2 → doover_cli-0.5.4}/tests/test_crud_values.py +0 -0
  80. {doover_cli-0.5.2 → doover_cli-0.5.4}/tests/test_default_renderer.py +0 -0
  81. {doover_cli-0.5.2 → doover_cli-0.5.4}/tests/test_device.py +0 -0
  82. {doover_cli-0.5.2 → doover_cli-0.5.4}/tests/test_device_cli_integration.py +0 -0
  83. {doover_cli-0.5.2 → doover_cli-0.5.4}/tests/test_device_type.py +0 -0
  84. {doover_cli-0.5.2 → doover_cli-0.5.4}/tests/test_device_type_cli_integration.py +0 -0
  85. {doover_cli-0.5.2 → doover_cli-0.5.4}/tests/test_prompt.py +0 -0
  86. {doover_cli-0.5.2 → doover_cli-0.5.4}/tests/test_sentry.py +0 -0
  87. {doover_cli-0.5.2 → doover_cli-0.5.4}/tests/test_user.py +0 -0
  88. {doover_cli-0.5.2 → doover_cli-0.5.4}/tests/test_utils_apps.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: doover-cli
3
- Version: 0.5.2
3
+ Version: 0.5.4
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.5.2"
3
+ dynamic = ["version"]
4
4
  description = "CLI for Doover"
5
5
  requires-python = ">=3.11"
6
6
  dependencies = [
@@ -26,6 +26,9 @@ dependencies = [
26
26
  requires = ["hatchling"]
27
27
  build-backend = "hatchling.build"
28
28
 
29
+ [tool.hatch.version]
30
+ path = "src/doover_cli/__init__.py"
31
+
29
32
  [project.scripts]
30
33
  doover = "doover_cli:main"
31
34
 
@@ -1,6 +1,8 @@
1
1
  from doover_cli.renderer import Renderer
2
2
  from typing import Annotated, Optional
3
3
 
4
+ __version__ = "0.5.4"
5
+
4
6
  import click
5
7
  import typer
6
8
 
@@ -938,6 +938,14 @@ def publish(
938
938
  help="Upload the widget when publishing. Disable with --no-put-widget.",
939
939
  ),
940
940
  ] = True,
941
+ build_package: Annotated[
942
+ bool,
943
+ typer.Option(
944
+ help="Build package.zip (./build.sh) before uploading a processor. "
945
+ "Use --no-build-package to upload an existing package.zip -- e.g. "
946
+ "when publishing several apps that share a single build.",
947
+ ),
948
+ ] = True,
941
949
  app_name: Annotated[
942
950
  str | None,
943
951
  typer.Option(
@@ -999,6 +1007,16 @@ def publish(
999
1007
 
1000
1008
  client, renderer = get_state()
1001
1009
 
1010
+ # The publish endpoints are organisation-scoped: without the
1011
+ # X-Doover-Organisation header the control plane rejects the request with
1012
+ # 401 "X-Doover-Organisation header missing" for any non-staff account.
1013
+ # Pin the client to the app's organisation (staging-aware, resolved into the
1014
+ # payload above) so every publish sub-call - create/partial, widget upload,
1015
+ # processor source + version release - carries the header.
1016
+ publish_org_id = payload.get("organisation_id")
1017
+ if publish_org_id is not None:
1018
+ client.organisation_id = int(publish_org_id)
1019
+
1002
1020
  rich.print(
1003
1021
  f"Updating application on doover site ({_control_base_url() or 'unknown base URL'})...\n"
1004
1022
  )
@@ -1062,8 +1080,13 @@ def publish(
1062
1080
  rich.print("[green]Widget uploaded.[/green]")
1063
1081
 
1064
1082
  if app_config.type in ("PRO", "REP", "INT"):
1065
- print("\nBuilding package.zip for upload...")
1066
- shell_run("./build.sh", cwd=root_fp)
1083
+ if build_package:
1084
+ print("\nBuilding package.zip for upload...")
1085
+ shell_run("./build.sh", cwd=root_fp)
1086
+ else:
1087
+ print(
1088
+ "\nSkipping package.zip build (--no-build-package); using existing package.zip."
1089
+ )
1067
1090
  print("Uploading package.zip to Doover...")
1068
1091
  processor_response = _publish_processor_package(
1069
1092
  client,
@@ -38,18 +38,25 @@ def export(
38
38
  file_okay=True,
39
39
  ),
40
40
  ] = None,
41
+ app_name: Annotated[
42
+ str | None,
43
+ typer.Option(
44
+ help="Application name in doover_config.json. Required to pick one "
45
+ "non-interactively when the repo defines multiple apps.",
46
+ ),
47
+ ] = None,
41
48
  ):
42
49
  """Export the application configuration to the doover config json file."""
43
50
  if config_fp is None:
44
51
  root_fp = get_app_directory(app_fp)
45
- app_config = get_app_config(root_fp)
52
+ app_config = get_app_config(root_fp, app_name=app_name)
46
53
  call_with_uv(
47
54
  app_config.export_config_command or "export-config",
48
55
  in_shell=True,
49
56
  cwd=app_fp,
50
57
  )
51
58
  else:
52
- config = get_app_config(app_fp)
59
+ config = get_app_config(app_fp, app_name=app_name)
53
60
  call_with_uv(config.src_directory / "app_config.py", in_shell=True)
54
61
 
55
62
  print("Exporting application configuration...")
@@ -74,6 +81,13 @@ def validate(
74
81
  "afterwards, so validation never writes to the working tree.",
75
82
  ),
76
83
  ] = True,
84
+ app_name: Annotated[
85
+ str | None,
86
+ typer.Option(
87
+ help="Application name in doover_config.json. Required to pick one "
88
+ "non-interactively when the repo defines multiple apps.",
89
+ ),
90
+ ] = None,
77
91
  ):
78
92
  """Validate application config is a valid JSON schema."""
79
93
  root_fp = get_app_directory(app_fp)
@@ -83,7 +97,7 @@ def validate(
83
97
  # Validate the schema the Python *currently* generates, without leaving
84
98
  # doover_config.json modified (e.g. when run as a pre-commit hook).
85
99
  with preserve_file(config_file):
86
- ctx.invoke(export, ctx, app_fp=root_fp, validate_=False)
100
+ ctx.invoke(export, ctx, app_fp=root_fp, validate_=False, app_name=app_name)
87
101
  _validate_config_file(config_file)
88
102
  else:
89
103
  _validate_config_file(config_file)
@@ -29,10 +29,17 @@ def export(
29
29
  help="Validate the UI schema before exporting.",
30
30
  ),
31
31
  ] = True,
32
+ app_name: Annotated[
33
+ str | None,
34
+ typer.Option(
35
+ help="Application name in doover_config.json. Required to pick one "
36
+ "non-interactively when the repo defines multiple apps.",
37
+ ),
38
+ ] = None,
32
39
  ):
33
40
  """Export the application UI schema to the doover config json file."""
34
41
  root_fp = get_app_directory(app_fp)
35
- app_config = get_app_config(root_fp)
42
+ app_config = get_app_config(root_fp, app_name=app_name)
36
43
  export_command = app_config.export_ui_command or "export-ui"
37
44
  if export_command == "NO_EXPORT":
38
45
  print("App requested no ui export. Skipping...")
@@ -66,6 +73,13 @@ def validate(
66
73
  "afterwards, so validation never writes to the working tree.",
67
74
  ),
68
75
  ] = True,
76
+ app_name: Annotated[
77
+ str | None,
78
+ typer.Option(
79
+ help="Application name in doover_config.json. Required to pick one "
80
+ "non-interactively when the repo defines multiple apps.",
81
+ ),
82
+ ] = None,
69
83
  ):
70
84
  """Validate application UI schema is valid JSON."""
71
85
  root_fp = get_app_directory(app_fp)
@@ -75,7 +89,7 @@ def validate(
75
89
  # Validate the schema the Python *currently* generates, without leaving
76
90
  # doover_config.json modified (e.g. when run as a pre-commit hook).
77
91
  with preserve_file(config_file):
78
- ctx.invoke(export, ctx, app_fp=root_fp, validate_=False)
92
+ ctx.invoke(export, ctx, app_fp=root_fp, validate_=False, app_name=app_name)
79
93
  _validate_ui_file(config_file)
80
94
  else:
81
95
  _validate_ui_file(config_file)
@@ -491,6 +491,51 @@ def test_app_publish_updates_existing_application(monkeypatch, tmp_path):
491
491
  assert renderer.render_calls == [{"id": 101, "published": True}]
492
492
 
493
493
 
494
+ def test_app_publish_pins_client_to_app_organisation(monkeypatch, tmp_path):
495
+ # Regression: publish must set the org context on the client so the
496
+ # X-Doover-Organisation header is sent. Without it the control plane
497
+ # returns 401 "X-Doover-Organisation header missing" for non-staff accounts.
498
+ captured = {}
499
+ renderer = FakeRenderer()
500
+ app_config = FakeAppConfig(app_id=101)
501
+
502
+ class FakeApplicationsClient:
503
+ @staticmethod
504
+ def partial(application_id, body):
505
+ captured["partial"] = (application_id, body)
506
+ return {"id": int(application_id), "published": True}
507
+
508
+ class FakeControlClient:
509
+ organisation_id = None
510
+ applications = FakeApplicationsClient()
511
+
512
+ client = FakeControlClient()
513
+
514
+ monkeypatch.setattr(
515
+ "doover_cli.apps.apps.get_app_directory", lambda root=None: tmp_path
516
+ )
517
+ monkeypatch.setattr(
518
+ "doover_cli.apps.apps.get_app_config", lambda root_fp, app_name=None: app_config
519
+ )
520
+ monkeypatch.setattr(
521
+ "doover_cli.apps.apps.get_state", lambda: (client, renderer)
522
+ )
523
+ monkeypatch.setattr(
524
+ "doover_cli.apps.apps.export_config_command",
525
+ lambda ctx, app_fp, validate_: None,
526
+ )
527
+ monkeypatch.setattr(
528
+ "doover_cli.apps.apps.export_ui_command",
529
+ lambda ctx, app_fp, validate_: None,
530
+ )
531
+
532
+ result = runner.invoke(app, ["app", "publish", str(tmp_path)])
533
+
534
+ assert result.exit_code == 0
535
+ assert client.organisation_id == 17
536
+ assert captured["partial"][0] == "101"
537
+
538
+
494
539
  def test_app_publish_creates_then_updates_when_missing(monkeypatch, tmp_path):
495
540
  captured = {}
496
541
  renderer = FakeRenderer()
@@ -544,7 +544,6 @@ wheels = [
544
544
 
545
545
  [[package]]
546
546
  name = "doover-cli"
547
- version = "0.5.2"
548
547
  source = { editable = "." }
549
548
  dependencies = [
550
549
  { 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