tinybird 4.2.3.dev0__tar.gz → 4.3.1.dev0__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 (126) hide show
  1. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/PKG-INFO +6 -1
  2. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/datafile/common.py +2 -1
  3. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/sql.py +2 -1
  4. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/__cli__.py +2 -2
  5. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/deployment_common.py +141 -33
  6. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird.egg-info/PKG-INFO +6 -1
  7. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/setup.cfg +0 -0
  8. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/__cli__.py +0 -0
  9. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/ch_utils/constants.py +0 -0
  10. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/ch_utils/engine.py +0 -0
  11. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/check_pypi.py +0 -0
  12. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/client.py +0 -0
  13. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/config.py +0 -0
  14. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/context.py +0 -0
  15. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/datafile/exceptions.py +0 -0
  16. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/datafile/parse_connection.py +0 -0
  17. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/datafile/parse_datasource.py +0 -0
  18. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/datafile/parse_pipe.py +0 -0
  19. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/datatypes.py +0 -0
  20. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/feedback_manager.py +0 -0
  21. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/git_settings.py +0 -0
  22. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/prompts.py +0 -0
  23. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/service_datasources.py +0 -0
  24. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/sql_template.py +0 -0
  25. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/sql_template_fmt.py +0 -0
  26. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/sql_toolset.py +0 -0
  27. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/syncasync.py +0 -0
  28. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/check_pypi.py +0 -0
  29. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/cli.py +0 -0
  30. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/client.py +0 -0
  31. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/config.py +0 -0
  32. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/branch.py +0 -0
  33. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/build.py +0 -0
  34. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/build_common.py +0 -0
  35. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/cicd.py +0 -0
  36. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/cli.py +0 -0
  37. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/common.py +0 -0
  38. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/config.py +0 -0
  39. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/connection.py +0 -0
  40. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/connection_kafka.py +0 -0
  41. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/connection_s3.py +0 -0
  42. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/copy.py +0 -0
  43. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/create.py +0 -0
  44. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/datafile/build.py +0 -0
  45. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/datafile/build_common.py +0 -0
  46. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
  47. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/datafile/build_pipe.py +0 -0
  48. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/datafile/diff.py +0 -0
  49. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/datafile/fixture.py +0 -0
  50. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/datafile/format_common.py +0 -0
  51. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/datafile/format_connection.py +0 -0
  52. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
  53. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/datafile/format_pipe.py +0 -0
  54. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/datafile/pipe_checker.py +0 -0
  55. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/datafile/playground.py +0 -0
  56. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/datafile/pull.py +0 -0
  57. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/datasource.py +0 -0
  58. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/deployment.py +0 -0
  59. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/deprecations.py +0 -0
  60. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/endpoint.py +0 -0
  61. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/exceptions.py +0 -0
  62. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/feedback_manager.py +0 -0
  63. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/fmt.py +0 -0
  64. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/info.py +0 -0
  65. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/infra.py +0 -0
  66. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/job.py +0 -0
  67. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/job_common.py +0 -0
  68. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/llm.py +0 -0
  69. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/llm_utils.py +0 -0
  70. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/local.py +0 -0
  71. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/local_common.py +0 -0
  72. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/local_logs.py +0 -0
  73. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/login.py +0 -0
  74. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/login_common.py +0 -0
  75. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/logout.py +0 -0
  76. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/logs.py +0 -0
  77. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/materialization.py +0 -0
  78. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/open.py +0 -0
  79. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/pipe.py +0 -0
  80. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/preview.py +0 -0
  81. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/project.py +0 -0
  82. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/project_commands.py +0 -0
  83. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/py_project.py +0 -0
  84. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/query_output.py +0 -0
  85. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/regions.py +0 -0
  86. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/secret.py +0 -0
  87. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/secret_common.py +0 -0
  88. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/sink.py +0 -0
  89. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/table.py +0 -0
  90. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/telemetry.py +0 -0
  91. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/test.py +0 -0
  92. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/test_common.py +0 -0
  93. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/tinyunit/tinyunit.py +0 -0
  94. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/tinyunit/tinyunit_lib.py +0 -0
  95. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/token.py +0 -0
  96. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/ts_project.py +0 -0
  97. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/watch.py +0 -0
  98. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/workspace.py +0 -0
  99. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb/modules/workspace_members.py +0 -0
  100. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb_cli.py +0 -0
  101. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb_cli_modules/auth.py +0 -0
  102. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb_cli_modules/branch.py +0 -0
  103. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb_cli_modules/cicd.py +0 -0
  104. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb_cli_modules/cli.py +0 -0
  105. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb_cli_modules/common.py +0 -0
  106. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb_cli_modules/config.py +0 -0
  107. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb_cli_modules/connection.py +0 -0
  108. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb_cli_modules/datasource.py +0 -0
  109. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb_cli_modules/exceptions.py +0 -0
  110. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb_cli_modules/fmt.py +0 -0
  111. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb_cli_modules/job.py +0 -0
  112. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb_cli_modules/pipe.py +0 -0
  113. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb_cli_modules/regions.py +0 -0
  114. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb_cli_modules/tag.py +0 -0
  115. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb_cli_modules/telemetry.py +0 -0
  116. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb_cli_modules/test.py +0 -0
  117. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
  118. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
  119. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb_cli_modules/workspace.py +0 -0
  120. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  121. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird/tornado_template.py +0 -0
  122. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird.egg-info/SOURCES.txt +0 -0
  123. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird.egg-info/dependency_links.txt +0 -0
  124. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird.egg-info/entry_points.txt +0 -0
  125. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird.egg-info/requires.txt +0 -0
  126. {tinybird-4.2.3.dev0 → tinybird-4.3.1.dev0}/tinybird.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tinybird
3
- Version: 4.2.3.dev0
3
+ Version: 4.3.1.dev0
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/forward/commands
6
6
  Author: Tinybird
@@ -52,6 +52,11 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
52
52
  Changelog
53
53
  ----------
54
54
 
55
+ 4.3.0
56
+ *******
57
+
58
+ - `Changed` `tb deploy --auto/--no-auto` keeps the same CLI behavior (default `--auto`) while using server-side deployment `auto_promote` under the hood when enabled.
59
+
55
60
  4.2.2
56
61
  *******
57
62
 
@@ -1426,7 +1426,8 @@ def schema_to_sql_columns(schema: List[Dict[str, Any]]) -> List[str]:
1426
1426
  name = x["normalized_name"] if "normalized_name" in x else x["name"]
1427
1427
  if x["nullable"]:
1428
1428
  if (_type := try_to_fix_nullable_in_simple_aggregating_function(x["type"])) is None:
1429
- _type = "Nullable(%s)" % x["type"]
1429
+ # Skip wrapping if Nullable already present, e.g. LowCardinality(Nullable(String))
1430
+ _type = x["type"] if "Nullable(" in x["type"] else "Nullable(%s)" % x["type"]
1430
1431
  else:
1431
1432
  _type = x["type"]
1432
1433
  parts = [col_name(name, backquotes=True), _type]
@@ -257,7 +257,8 @@ def schema_to_sql_columns(schema: List[Dict[str, Any]], skip_jsonpaths: bool = F
257
257
  name = x["normalized_name"] if "normalized_name" in x else x["name"]
258
258
  if x["nullable"]:
259
259
  if (_type := try_to_fix_nullable_in_simple_aggregating_function(x["type"])) is None:
260
- _type = "Nullable(%s)" % x["type"]
260
+ # Skip wrapping if Nullable already present, e.g. LowCardinality(Nullable(String))
261
+ _type = x["type"] if "Nullable(" in x["type"] else "Nullable(%s)" % x["type"]
261
262
  else:
262
263
  _type = x["type"]
263
264
  parts = [col_name(name, backquotes=True), _type]
@@ -4,5 +4,5 @@ __description__ = 'Tinybird Command Line Tool'
4
4
  __url__ = 'https://www.tinybird.co/docs/forward/commands'
5
5
  __author__ = 'Tinybird'
6
6
  __author_email__ = 'support@tinybird.co'
7
- __version__ = '4.2.3.dev0'
8
- __revision__ = 'e34a5be'
7
+ __version__ = '4.3.1.dev0'
8
+ __revision__ = 'ff0d937'
@@ -87,6 +87,41 @@ def api_post(
87
87
  return {}
88
88
 
89
89
 
90
+ def _is_first_deployment_with_seed_live(host: Optional[str], headers: dict) -> bool:
91
+ """Best-effort check for first real deployment when seed deployment (id=0) is still live."""
92
+ try:
93
+ response = requests.get(f"{host}/v1/deployments", headers=headers)
94
+ if response.status_code >= 300:
95
+ return False
96
+
97
+ result = response.json()
98
+ logging.debug(json.dumps(result, indent=2))
99
+ deployments = result.get("deployments") or []
100
+ if not deployments:
101
+ return False
102
+
103
+ live_deployment = next((deployment for deployment in deployments if deployment.get("live")), deployments[0])
104
+ return str(live_deployment.get("id")) == "0"
105
+ except Exception:
106
+ # Hint calculation must never break deployment flow.
107
+ return False
108
+
109
+
110
+ def _get_deployment_job(client: TinyB, deployment_id: Optional[Union[str, int]]) -> Optional[Dict[str, Any]]:
111
+ if deployment_id is None:
112
+ return None
113
+
114
+ try:
115
+ deployment_id = str(deployment_id)
116
+ jobs = client.jobs()
117
+ return next(
118
+ (job for job in jobs if job.get("kind") == "deployment" and str(job.get("deployment_id")) == deployment_id),
119
+ None,
120
+ )
121
+ except Exception:
122
+ return None
123
+
124
+
90
125
  # TODO(eclbg): This logic should be in the server, and there should be a dedicated endpoint for promoting a deployment
91
126
  # potato
92
127
  def promote_deployment(host: Optional[str], headers: dict, wait: bool, ingest_hint: Optional[bool] = True) -> None:
@@ -243,7 +278,7 @@ def create_deployment(
243
278
  ".connection": "text/plain",
244
279
  }
245
280
 
246
- TINYBIRD_API_URL = f"{client.host}/v1/deploy"
281
+ TINYBIRD_API_DEPLOY_ENDPOINT_URL = f"{client.host}/v1/deploy"
247
282
  TINYBIRD_API_KEY = client.token
248
283
 
249
284
  if project.has_deeper_level():
@@ -273,24 +308,33 @@ def create_deployment(
273
308
  (MULTIPART_BOUNDARY_DATA_PROJECT_VENDORED, (relative_path, fd.read().decode("utf-8"), content_type))
274
309
  )
275
310
 
276
- deployment = None
311
+ deployment_job: Optional[Dict[str, Any]] = None
312
+ deployment_request_sent = False
313
+ deployment = {}
277
314
  try:
278
315
  HEADERS = {"Authorization": f"Bearer {TINYBIRD_API_KEY}"}
279
316
  params = {}
280
317
  if check:
281
318
  click.echo(FeedbackManager.highlight(message="\n» Validating deployment...\n"))
282
319
  params["check"] = "true"
320
+ elif auto:
321
+ params["auto_promote"] = "true"
283
322
  if allow_destructive_operations:
284
323
  params["allow_destructive_operations"] = "true"
285
324
 
286
- result = api_post(TINYBIRD_API_URL, headers=HEADERS, files=files, params=params)
325
+ deployment_request_sent = True
326
+ result = api_post(TINYBIRD_API_DEPLOY_ENDPOINT_URL, headers=HEADERS, files=files, params=params)
287
327
 
288
328
  print_changes(result, project, output)
289
329
 
330
+ deployment = result.get("deployment", {})
331
+ if not deployment:
332
+ click.echo(FeedbackManager.error_exception(error="Failed to parse response from API"))
333
+ sys_exit("deployment_error", "Failed to parse deployment from API response")
334
+
290
335
  if output == "json" and check:
291
- echo_json(result.get("deployment", {}), 8)
336
+ echo_json(deployment, indent=8)
292
337
 
293
- deployment = result.get("deployment", {})
294
338
  feedback = deployment.get("feedback", [])
295
339
  for f in feedback:
296
340
  if f.get("level", "").upper() == "ERROR":
@@ -333,22 +377,13 @@ def create_deployment(
333
377
  f"Deployment is not valid: {str(deployment.get('errors') + deployment.get('feedback', []))}",
334
378
  )
335
379
 
336
- status = result.get("result")
337
380
  host = get_display_cloud_host(client.host)
338
381
  if status in ["success", "failed"]:
339
382
  click.echo(
340
383
  FeedbackManager.gray(message="Deployment URL: ")
341
384
  + f"{bcolors.UNDERLINE}{host}/{config.get('name')}/deployments/{deployment.get('id')}{bcolors.ENDC}"
342
385
  )
343
- jobs = client.jobs()
344
- deployment_job = next(
345
- (
346
- job
347
- for job in jobs
348
- if job.get("kind") == "deployment" and job.get("deployment_id") == deployment.get("id")
349
- ),
350
- None,
351
- )
386
+ deployment_job = _get_deployment_job(client, deployment.get("id"))
352
387
  if deployment_job:
353
388
  echo_job_url(
354
389
  token=client.token,
@@ -358,10 +393,15 @@ def create_deployment(
358
393
  )
359
394
 
360
395
  if status == "success":
396
+ autopromote_frag = (
397
+ " It will be auto-promoted when ready." if auto else " It won't be auto-promoted when ready."
398
+ )
361
399
  if wait:
362
- click.echo(FeedbackManager.info(message="\n* Deployment submitted"))
400
+ message = "\n* Deployment submitted." + autopromote_frag
401
+ click.echo(FeedbackManager.info(message=message))
363
402
  else:
364
- click.echo(FeedbackManager.success(message="\n✓ Deployment submitted successfully"))
403
+ message = "\n✓ Deployment submitted successfully." + autopromote_frag
404
+ click.echo(FeedbackManager.success(message=message))
365
405
  elif status == "no_changes":
366
406
  click.echo(FeedbackManager.warning(message="△ Not deploying. No changes."))
367
407
  sys.exit(0)
@@ -378,13 +418,30 @@ def create_deployment(
378
418
 
379
419
  if not deployment and not check:
380
420
  sys_exit("deployment_error", "Deployment failed")
381
-
382
- if deployment and wait and not check:
383
- click.echo(FeedbackManager.highlight(message="» Waiting for deployment to be ready..."))
421
+ except KeyboardInterrupt:
422
+ if deployment_request_sent and not check:
423
+ click.echo(
424
+ FeedbackManager.warning(
425
+ message=f"\nDeployment request might have reached the server. Check with `tb --{env} deployment ls`. You can cancel an ongoing deployment with `tb --{env} deployment discard`."
426
+ )
427
+ )
428
+ raise click.Abort()
429
+
430
+ if not wait:
431
+ if output == "json" and deployment:
432
+ echo_json(deployment, 8)
433
+ sys.exit(0)
434
+
435
+ click.echo(FeedbackManager.highlight(message="» Waiting for deployment to be ready..."))
436
+ waiting_auto_promote = False
437
+ is_first_deployment = str(deployment.get("id")) == "1"
438
+ times_seen_failed = 0
439
+ poll_interval = 5
440
+ try:
384
441
  while True:
385
442
  url = f"{client.host}/v1/deployments/{deployment.get('id')}"
386
443
  res = api_fetch(url, HEADERS)
387
- deployment = res.get("deployment")
444
+ deployment = res.get("deployment", {})
388
445
  if not deployment:
389
446
  click.echo(FeedbackManager.error(message="Error parsing deployment from response"))
390
447
  sys_exit("deployment_error", "Error parsing deployment from response")
@@ -395,10 +452,28 @@ def create_deployment(
395
452
  feedback = deployment.get("feedback")
396
453
 
397
454
  if status == "failed":
398
- # Just wait until we poll again and see deleting or deleted to report errors
399
- pass
455
+ times_seen_failed += 1
456
+ # See if it's stuck in failed, otherwise just wait until we see deleting or deleted
457
+ if times_seen_failed > 60: # 5s * 60 = 5mins
458
+ message = (
459
+ "Deployment failed to create and didn't start deleting automatically after 5 minutes. "
460
+ "You might need to delete it manually in the UI."
461
+ )
462
+ click.echo(FeedbackManager.warning(message=message))
463
+ sys_exit("deployment_error", "Deployment failed and wasn't deleted automatically")
464
+ time.sleep(poll_interval)
465
+ continue
400
466
 
401
467
  if status == "data_ready":
468
+ if auto and not deployment.get("live"):
469
+ if not waiting_auto_promote:
470
+ click.echo(FeedbackManager.info(message="✓ Deployment is ready"))
471
+ click.echo(FeedbackManager.highlight(message="» Waiting for deployment to be promoted..."))
472
+ if not is_first_deployment:
473
+ is_first_deployment = _is_first_deployment_with_seed_live(client.host, HEADERS)
474
+ waiting_auto_promote = True
475
+ time.sleep(poll_interval)
476
+ continue
402
477
  break
403
478
 
404
479
  if status in ["deleting", "deleted"]:
@@ -413,20 +488,53 @@ def create_deployment(
413
488
  for error in errors:
414
489
  click.echo(FeedbackManager.error(message=f"* {error}"))
415
490
  sys_exit(
416
- "deployment_error", f"Deployment deleted after failure. Errors: {str(errors + (feedback or []))}"
491
+ "deployment_error",
492
+ f"Deployment deleted after failure. Errors: {str(errors + (feedback or []))}",
417
493
  )
418
494
 
419
- time.sleep(5)
495
+ time.sleep(poll_interval)
420
496
 
421
- click.echo(FeedbackManager.info(message="✓ Deployment is ready"))
497
+ except KeyboardInterrupt:
498
+ should_cancel = True
499
+ try:
500
+ prompt = "\nYour deployment is in progress. Do you want to cancel the deployment? If not, it will continue even after exiting this command"
501
+ should_cancel = click.confirm(FeedbackManager.warning(message=prompt), default=True, show_default=True)
502
+ except (KeyboardInterrupt, click.Abort):
503
+ # A second Ctrl+C is interpreted as confirming cancellation.
504
+ pass
505
+
506
+ if not should_cancel:
507
+ click.echo(FeedbackManager.info(message="Deployment not canceled. Exiting"))
508
+ sys.exit(0)
509
+
510
+ click.echo(FeedbackManager.warning(message="Canceling deployment"))
511
+ cancel_url = f"{client.host}/v1/deployments/{deployment.get('id')}"
512
+ r = requests.delete(cancel_url, headers=HEADERS)
513
+ result = r.json()
514
+ logging.debug(json.dumps(result, indent=2))
515
+ if result.get("error"):
516
+ click.echo(FeedbackManager.error(message="Failed to cancel deployment: " + result.get("error")))
517
+ sys_exit("deployment_error", result.get("error", "Unknown error"))
518
+
519
+ click.echo(FeedbackManager.success(message="Deployment canceled. It will be deleted evenutally."))
422
520
 
423
- if auto:
424
- promote_deployment(client.host, HEADERS, wait=wait, ingest_hint=ingest_hint)
425
- # Fetch the final deployment state after promotion for JSON output
426
- if output == "json":
427
- url = f"{client.host}/v1/deployments/{deployment.get('id')}"
428
- res = api_fetch(url, HEADERS)
429
- deployment = res.get("deployment")
521
+ raise click.Abort()
522
+
523
+ # We get here when wait is True, after we've finished polling
524
+ if auto:
525
+ if not waiting_auto_promote:
526
+ click.echo(FeedbackManager.info(message="✓ Deployment is ready"))
527
+ click.echo(FeedbackManager.highlight(message="» Waiting for deployment to be promoted..."))
528
+ click.echo(FeedbackManager.info(message="✓ Deployment promoted"))
529
+ click.echo(FeedbackManager.success(message=f"✓ Deployment #{deployment.get('id')} is live!"))
530
+ if ingest_hint and len(deployment.get("new_data_connector_ids", [])) == 0 and is_first_deployment:
531
+ click.echo(
532
+ FeedbackManager.info(
533
+ message="Need help ingesting your data? Learn how at https://www.tinybird.co/docs/forward/get-data-in"
534
+ )
535
+ )
536
+ else:
537
+ click.echo(FeedbackManager.info(message="✓ Deployment is ready"))
430
538
 
431
539
  # Output JSON at the appropriate time based on the execution path
432
540
  if output == "json" and deployment:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tinybird
3
- Version: 4.2.3.dev0
3
+ Version: 4.3.1.dev0
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/forward/commands
6
6
  Author: Tinybird
@@ -52,6 +52,11 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
52
52
  Changelog
53
53
  ----------
54
54
 
55
+ 4.3.0
56
+ *******
57
+
58
+ - `Changed` `tb deploy --auto/--no-auto` keeps the same CLI behavior (default `--auto`) while using server-side deployment `auto_promote` under the hood when enabled.
59
+
55
60
  4.2.2
56
61
  *******
57
62
 
File without changes