tinybird 0.0.1.dev132__tar.gz → 0.0.1.dev134__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.

Potentially problematic release.


This version of tinybird might be problematic. Click here for more details.

Files changed (108) hide show
  1. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/PKG-INFO +1 -1
  2. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/feedback_manager.py +0 -3
  3. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/__cli__.py +2 -2
  4. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/config.py +3 -3
  5. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/build.py +10 -14
  6. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/common.py +27 -4
  7. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/create.py +6 -14
  8. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/datasource.py +0 -7
  9. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/deployment.py +44 -31
  10. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/exceptions.py +51 -1
  11. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/feedback_manager.py +0 -4
  12. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/local.py +10 -4
  13. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/local_common.py +5 -2
  14. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/login.py +72 -67
  15. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/mock.py +7 -10
  16. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/telemetry.py +1 -2
  17. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/test.py +98 -93
  18. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb_cli_modules/datasource.py +0 -7
  19. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird.egg-info/PKG-INFO +1 -1
  20. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/setup.cfg +0 -0
  21. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/__cli__.py +0 -0
  22. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/ch_utils/constants.py +0 -0
  23. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/ch_utils/engine.py +0 -0
  24. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/check_pypi.py +0 -0
  25. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/client.py +0 -0
  26. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/config.py +0 -0
  27. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/connectors.py +0 -0
  28. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/context.py +0 -0
  29. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/datafile.py +0 -0
  30. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/datatypes.py +0 -0
  31. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/git_settings.py +0 -0
  32. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/prompts.py +0 -0
  33. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/sql.py +0 -0
  34. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/sql_template.py +0 -0
  35. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/sql_template_fmt.py +0 -0
  36. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/sql_toolset.py +0 -0
  37. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/syncasync.py +0 -0
  38. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/cli.py +0 -0
  39. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/client.py +0 -0
  40. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/auth.py +0 -0
  41. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/cicd.py +0 -0
  42. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/cli.py +0 -0
  43. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/config.py +0 -0
  44. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/connection.py +0 -0
  45. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/copy.py +0 -0
  46. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/datafile/build.py +0 -0
  47. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/datafile/build_common.py +0 -0
  48. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
  49. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/datafile/build_pipe.py +0 -0
  50. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/datafile/common.py +0 -0
  51. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/datafile/diff.py +0 -0
  52. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/datafile/exceptions.py +0 -0
  53. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/datafile/fixture.py +0 -0
  54. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/datafile/format_common.py +0 -0
  55. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
  56. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/datafile/format_pipe.py +0 -0
  57. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/datafile/parse_datasource.py +0 -0
  58. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/datafile/parse_pipe.py +0 -0
  59. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/datafile/pipe_checker.py +0 -0
  60. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/datafile/playground.py +0 -0
  61. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/datafile/pull.py +0 -0
  62. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/endpoint.py +0 -0
  63. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/infra.py +0 -0
  64. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/job.py +0 -0
  65. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/llm.py +0 -0
  66. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/llm_utils.py +0 -0
  67. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/logout.py +0 -0
  68. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/materialization.py +0 -0
  69. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/open.py +0 -0
  70. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/pipe.py +0 -0
  71. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/project.py +0 -0
  72. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/regions.py +0 -0
  73. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/secret.py +0 -0
  74. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/shell.py +0 -0
  75. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/table.py +0 -0
  76. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/tag.py +0 -0
  77. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/tinyunit/tinyunit.py +0 -0
  78. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/tinyunit/tinyunit_lib.py +0 -0
  79. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/token.py +0 -0
  80. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/watch.py +0 -0
  81. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/workspace.py +0 -0
  82. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb/modules/workspace_members.py +0 -0
  83. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb_cli.py +0 -0
  84. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb_cli_modules/auth.py +0 -0
  85. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb_cli_modules/branch.py +0 -0
  86. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb_cli_modules/cicd.py +0 -0
  87. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb_cli_modules/cli.py +0 -0
  88. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb_cli_modules/common.py +0 -0
  89. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb_cli_modules/config.py +0 -0
  90. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb_cli_modules/connection.py +0 -0
  91. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb_cli_modules/exceptions.py +0 -0
  92. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb_cli_modules/fmt.py +0 -0
  93. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb_cli_modules/job.py +0 -0
  94. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb_cli_modules/pipe.py +0 -0
  95. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb_cli_modules/regions.py +0 -0
  96. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb_cli_modules/tag.py +0 -0
  97. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb_cli_modules/telemetry.py +0 -0
  98. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb_cli_modules/test.py +0 -0
  99. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
  100. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
  101. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb_cli_modules/workspace.py +0 -0
  102. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  103. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird/tornado_template.py +0 -0
  104. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird.egg-info/SOURCES.txt +0 -0
  105. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird.egg-info/dependency_links.txt +0 -0
  106. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird.egg-info/entry_points.txt +0 -0
  107. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird.egg-info/requires.txt +0 -0
  108. {tinybird-0.0.1.dev132 → tinybird-0.0.1.dev134}/tinybird.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tinybird
3
- Version: 0.0.1.dev132
3
+ Version: 0.0.1.dev134
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli/introduction.html
6
6
  Author: Tinybird
@@ -910,9 +910,6 @@ Ready? """
910
910
  success_delete_rows_datasource = success_message(
911
911
  "** Data Source '{datasource}' rows deleted matching condition \"{delete_condition}\""
912
912
  )
913
- success_delete_rows_datasource_no_rows = success_message(
914
- "** Data Source '{datasource}' no rows to delete matching condition \"{delete_condition}\""
915
- )
916
913
  success_dry_run_delete_rows_datasource = success_message(
917
914
  "** [DRY RUN] Data Source '{datasource}' rows '{rows}' matching condition \"{delete_condition}\" to be deleted"
918
915
  )
@@ -4,5 +4,5 @@ __description__ = 'Tinybird Command Line Tool'
4
4
  __url__ = 'https://www.tinybird.co/docs/cli/introduction.html'
5
5
  __author__ = 'Tinybird'
6
6
  __author_email__ = 'support@tinybird.co'
7
- __version__ = '0.0.1.dev132'
8
- __revision__ = '1b7e7c7'
7
+ __version__ = '0.0.1.dev134'
8
+ __revision__ = '921269a'
@@ -42,7 +42,7 @@ CLOUD_HOSTS = {
42
42
  }
43
43
 
44
44
 
45
- async def get_config(
45
+ def get_config(
46
46
  host: str, token: Optional[str], semver: Optional[str] = None, config_file: Optional[str] = None
47
47
  ) -> Dict[str, Any]:
48
48
  if host:
@@ -50,8 +50,8 @@ async def get_config(
50
50
 
51
51
  config = {}
52
52
  try:
53
- async with aiofiles.open(config_file or Path(getcwd()) / ".tinyb") as file:
54
- res = await file.read()
53
+ with open(config_file or Path(getcwd()) / ".tinyb") as file:
54
+ res = file.read()
55
55
  config = json.loads(res)
56
56
  except OSError:
57
57
  pass
@@ -1,7 +1,6 @@
1
1
  import asyncio
2
2
  import json
3
3
  import logging
4
- import sys
5
4
  import threading
6
5
  import time
7
6
  from copy import deepcopy
@@ -16,7 +15,7 @@ import requests
16
15
  import tinybird.context as context
17
16
  from tinybird.tb.client import TinyB
18
17
  from tinybird.tb.modules.cli import cli
19
- from tinybird.tb.modules.common import push_data
18
+ from tinybird.tb.modules.common import push_data, sys_exit
20
19
  from tinybird.tb.modules.config import CLIConfig
21
20
  from tinybird.tb.modules.datafile.build import folder_build
22
21
  from tinybird.tb.modules.datafile.exceptions import ParseException
@@ -99,12 +98,10 @@ def build_project(project: Project, tb_client: TinyB, file_changed: Optional[str
99
98
  except Exception as e:
100
99
  logging.debug(e, exc_info=True)
101
100
  click.echo(FeedbackManager.error(message="Couldn't parse response from server"))
102
- sys.exit(1)
101
+ sys_exit("build_error", str(e))
103
102
 
104
103
  logging.debug(json.dumps(result, indent=2))
105
104
 
106
- output_pre_formatted = False
107
-
108
105
  build_result = result.get("result")
109
106
  if build_result == "success":
110
107
  build = result.get("build")
@@ -174,8 +171,7 @@ def build_project(project: Project, tb_client: TinyB, file_changed: Optional[str
174
171
  build_errors = result.get("errors")
175
172
  full_error_msg = ""
176
173
  for build_error in build_errors:
177
- output_pre_formatted = True
178
- filename_bit = FeedbackManager.build_result_error_title(message=f"{build_error.get('filename', '')}")
174
+ filename_bit = build_error.get("filename", "")
179
175
  error_bit = build_error.get("error") or build_error.get("message") or ""
180
176
  error_msg = ((filename_bit + "\n") if filename_bit else "") + error_bit
181
177
  full_error_msg += error_msg + "\n\n"
@@ -187,11 +183,9 @@ def build_project(project: Project, tb_client: TinyB, file_changed: Optional[str
187
183
  finally:
188
184
  for fd in fds:
189
185
  fd.close()
190
- if error:
191
- if not output_pre_formatted:
192
- raise click.ClickException(error)
193
- click.echo(error)
194
- raise click.ClickException("") # Needed so the caller shows the right message
186
+
187
+ if error:
188
+ raise click.ClickException(error)
195
189
 
196
190
 
197
191
  def append_fixture(
@@ -270,6 +264,7 @@ def process(
270
264
  ) -> None:
271
265
  time_start = time.time()
272
266
  build_failed = False
267
+ build_error: Optional[str] = None
273
268
  if file_changed and (file_changed.endswith(FixtureExtension.NDJSON) or file_changed.endswith(FixtureExtension.CSV)):
274
269
  rebuild_fixture(project, tb_client, file_changed)
275
270
  elif file_changed and file_changed.endswith(".sql"):
@@ -278,7 +273,8 @@ def process(
278
273
  try:
279
274
  build_project(project, tb_client, file_changed, silent)
280
275
  except click.ClickException as e:
281
- click.echo(FeedbackManager.error(message=e))
276
+ click.echo(FeedbackManager.info(message=str(e)))
277
+ build_error = str(e)
282
278
  build_failed = True
283
279
  try:
284
280
  if file_changed and not build_failed:
@@ -294,7 +290,7 @@ def process(
294
290
  if build_failed:
295
291
  click.echo(FeedbackManager.error(message=f"✗ {rebuild_str} failed"))
296
292
  if not watch:
297
- sys.exit(1)
293
+ sys_exit("build_error", build_error or "Unknown error")
298
294
  else:
299
295
  if not silent:
300
296
  click.echo(FeedbackManager.success(message=f"\n✓ {rebuild_str} completed in {elapsed_time:.1f}s"))
@@ -250,6 +250,7 @@ line experience. To opt-out, set TB_CLI_TELEMETRY_OPTOUT to '1' or 'true'."""
250
250
 
251
251
  def __call__(self, *args, **kwargs) -> None:
252
252
  error_msg: Optional[str] = None
253
+ silent_error_msg: Optional[str] = None
253
254
  error_event: str = "error"
254
255
 
255
256
  exit_code: int = 0
@@ -265,14 +266,19 @@ line experience. To opt-out, set TB_CLI_TELEMETRY_OPTOUT to '1' or 'true'."""
265
266
  error_event = "auth_error"
266
267
  exit_code = 1
267
268
  except SystemExit as ex:
268
- exit_code = int(ex.code) if ex.code else 0
269
+ if isinstance(ex.code, str):
270
+ exit_code = 1
271
+ error_event, silent_error_msg = get_error_event(ex.code)
272
+ else:
273
+ exit_code = ex.code or 0
269
274
  except Exception as ex:
270
275
  error_msg = str(ex)
271
276
  exit_code = 1
272
277
 
273
- if error_msg:
274
- click.echo(error_msg)
275
- add_telemetry_event(error_event, error=error_msg)
278
+ if error_msg or silent_error_msg:
279
+ if error_msg:
280
+ click.echo(error_msg)
281
+ add_telemetry_event(error_event, error=error_msg or silent_error_msg)
276
282
  flush_telemetry(wait=True)
277
283
 
278
284
  sys.exit(exit_code)
@@ -2232,3 +2238,20 @@ async def ask_for_organization(
2232
2238
  else:
2233
2239
  return None, None
2234
2240
  return organization_id, organization_name
2241
+
2242
+
2243
+ event_error_separator = "__error__"
2244
+
2245
+
2246
+ def sys_exit(event: str, msg: str) -> None:
2247
+ sys.exit(f"{event}{event_error_separator}{msg}")
2248
+
2249
+
2250
+ def get_error_event(error: str) -> Tuple[str, str]:
2251
+ try:
2252
+ error_event = error.split(event_error_separator)[0]
2253
+ silent_error_msg = error.split(event_error_separator)[1]
2254
+ except Exception:
2255
+ error_event = "error"
2256
+ silent_error_msg = "Unknown error"
2257
+ return error_event, silent_error_msg
@@ -12,7 +12,7 @@ from tinybird.tb.modules.cli import cli
12
12
  from tinybird.tb.modules.common import _generate_datafile, coro, generate_datafile
13
13
  from tinybird.tb.modules.config import CLIConfig
14
14
  from tinybird.tb.modules.datafile.fixture import persist_fixture
15
- from tinybird.tb.modules.exceptions import CLIException
15
+ from tinybird.tb.modules.exceptions import CLICreateException
16
16
  from tinybird.tb.modules.feedback_manager import FeedbackManager
17
17
  from tinybird.tb.modules.llm import LLM
18
18
  from tinybird.tb.modules.llm_utils import extract_xml, parse_xml
@@ -70,17 +70,9 @@ async def create(
70
70
  user_token: Optional[str] = None
71
71
  created_something = False
72
72
  if prompt:
73
- try:
74
- user_token = ctx_config.get("user_token")
75
- if not user_token:
76
- raise CLIException("No user token found")
77
- except Exception as e:
78
- click.echo(
79
- FeedbackManager.error(
80
- message=f"This action requires authentication. Run 'tb login' first. Error: {e}"
81
- )
82
- )
83
- return
73
+ user_token = ctx_config.get("user_token")
74
+ if not user_token:
75
+ raise Exception("This action requires authentication. Run 'tb login' first.")
84
76
 
85
77
  if not validate_project_structure(folder):
86
78
  click.echo(FeedbackManager.highlight(message="\n» Creating new project structure..."))
@@ -164,7 +156,7 @@ async def create(
164
156
  if not created_something:
165
157
  click.echo(FeedbackManager.warning(message="△ No resources created\n"))
166
158
  except Exception as e:
167
- click.echo(FeedbackManager.error(message=f"Error: {str(e)}"))
159
+ raise CLICreateException(FeedbackManager.error(message=str(e)))
168
160
 
169
161
 
170
162
  PROJECT_PATHS = ("datasources", "endpoints", "materializations", "copies", "pipes", "fixtures", "tests", "connections")
@@ -328,7 +320,7 @@ def init_git(folder: str):
328
320
 
329
321
  click.echo(FeedbackManager.info_file_created(file=".gitignore"))
330
322
  except Exception as e:
331
- raise CLIException(f"Error initializing Git: {e}")
323
+ raise Exception(f"Error initializing Git: {e}")
332
324
 
333
325
 
334
326
  def generate_pipe_file(name: str, content: str, folder: str) -> Path:
@@ -322,13 +322,6 @@ async def datasource_delete_rows(ctx, datasource_name, sql_condition, yes, wait,
322
322
  )
323
323
  )
324
324
  return
325
- if res is None: # 204,205
326
- click.echo(
327
- FeedbackManager.success_delete_rows_datasource_no_rows(
328
- datasource=datasource_name, delete_condition=sql_condition
329
- )
330
- )
331
- return
332
325
  job_id = res["job_id"]
333
326
  job_url = res["job_url"]
334
327
  click.echo(FeedbackManager.info_datasource_delete_rows_job_url(url=job_url))
@@ -10,7 +10,11 @@ import click
10
10
  import requests
11
11
 
12
12
  from tinybird.tb.modules.cli import cli
13
- from tinybird.tb.modules.common import echo_safe_humanfriendly_tables_format_smart_table, get_display_cloud_host
13
+ from tinybird.tb.modules.common import (
14
+ echo_safe_humanfriendly_tables_format_smart_table,
15
+ get_display_cloud_host,
16
+ sys_exit,
17
+ )
14
18
  from tinybird.tb.modules.feedback_manager import FeedbackManager
15
19
  from tinybird.tb.modules.project import Project
16
20
 
@@ -27,10 +31,12 @@ def api_fetch(url: str, headers: dict) -> dict:
27
31
  error = result.get("error")
28
32
  logging.debug(json.dumps(result, indent=2))
29
33
  click.echo(FeedbackManager.error(message=f"Error: {error}"))
30
- sys.exit(1)
34
+ sys_exit("deployment_error", error)
31
35
  except Exception:
32
- click.echo(FeedbackManager.error(message="Error parsing response from API"))
33
- sys.exit(1)
36
+ message = "Error parsing response from API"
37
+ click.echo(FeedbackManager.error(message=message))
38
+ sys_exit("deployment_error", message)
39
+ return {}
34
40
 
35
41
 
36
42
  def api_post(
@@ -50,11 +56,13 @@ def api_post(
50
56
  error = result.get("error")
51
57
  if error:
52
58
  click.echo(FeedbackManager.error(message=f"Error: {error}"))
53
- sys.exit(1)
59
+ sys_exit("deployment_error", error)
54
60
  return result
55
61
  except Exception:
56
- click.echo(FeedbackManager.error(message="Error parsing response from API"))
57
- sys.exit(1)
62
+ message = "Error parsing response from API"
63
+ click.echo(FeedbackManager.error(message=message))
64
+ sys_exit("deployment_error", message)
65
+ return {}
58
66
 
59
67
 
60
68
  # TODO(eclbg): This logic should be in the server, and there should be a dedicated endpoint for promoting a deployment
@@ -65,12 +73,16 @@ def promote_deployment(host: Optional[str], headers: dict, wait: bool) -> None:
65
73
 
66
74
  deployments = result.get("deployments")
67
75
  if not deployments:
68
- click.echo(FeedbackManager.error(message="No deployments found"))
69
- sys.exit(1)
76
+ message = "No deployments found"
77
+ click.echo(FeedbackManager.error(message=message))
78
+ sys_exit("deployment_error", message)
79
+ return
70
80
 
71
81
  if len(deployments) < 2:
72
- click.echo(FeedbackManager.error(message="Only one deployment found"))
73
- sys.exit(1)
82
+ message = "Only one deployment found"
83
+ click.echo(FeedbackManager.error(message=message))
84
+ sys_exit("deployment_error", message)
85
+ return
74
86
 
75
87
  last_deployment, candidate_deployment = deployments[0], deployments[1]
76
88
 
@@ -79,17 +91,16 @@ def promote_deployment(host: Optional[str], headers: dict, wait: bool) -> None:
79
91
  deploy_errors = candidate_deployment.get("errors", [])
80
92
  for deploy_error in deploy_errors:
81
93
  click.echo(FeedbackManager.error(message=f"* {deploy_error}"))
82
- sys.exit(1)
94
+ sys_exit("deployment_error", "Current deployment is not ready: " + str(deploy_errors))
95
+ return
83
96
 
84
97
  if candidate_deployment.get("live"):
85
98
  click.echo(FeedbackManager.error(message="Candidate deployment is already live"))
86
99
  else:
87
- click.echo(FeedbackManager.success(message="Setting candidate deployment as live"))
88
-
89
100
  TINYBIRD_API_URL = f"{host}/v1/deployments/{candidate_deployment.get('id')}/set-live"
90
101
  result = api_post(TINYBIRD_API_URL, headers=headers)
91
102
 
92
- click.echo(FeedbackManager.success(message="Removing old deployment"))
103
+ click.echo(FeedbackManager.highlight(message="» Removing old deployment"))
93
104
 
94
105
  TINYBIRD_API_URL = f"{host}/v1/deployments/{last_deployment.get('id')}"
95
106
  r = requests.delete(TINYBIRD_API_URL, headers=headers)
@@ -97,9 +108,10 @@ def promote_deployment(host: Optional[str], headers: dict, wait: bool) -> None:
97
108
  logging.debug(json.dumps(result, indent=2))
98
109
  if result.get("error"):
99
110
  click.echo(FeedbackManager.error(message=result.get("error")))
100
- sys.exit(1)
111
+ sys_exit("deployment_error", result.get("error", "Unknown error"))
112
+ click.echo(FeedbackManager.info(message="✓ Old deployment removed"))
101
113
 
102
- click.echo(FeedbackManager.success(message="Deployment promotion successfully started"))
114
+ click.echo(FeedbackManager.highlight(message="» Waiting for deployment to be promoted..."))
103
115
 
104
116
  if wait:
105
117
  while True:
@@ -108,7 +120,7 @@ def promote_deployment(host: Optional[str], headers: dict, wait: bool) -> None:
108
120
 
109
121
  last_deployment = result.get("deployment")
110
122
  if last_deployment.get("status") == "deleted":
111
- click.echo(FeedbackManager.success(message="Deployment promoted successfully"))
123
+ click.echo(FeedbackManager.success(message="Deployment is live!"))
112
124
  break
113
125
 
114
126
  time.sleep(5)
@@ -154,7 +166,7 @@ def discard_deployment(host: Optional[str], headers: dict, wait: bool) -> None:
154
166
  logging.debug(json.dumps(result, indent=2))
155
167
  if result.get("error"):
156
168
  click.echo(FeedbackManager.error(message=result.get("error")))
157
- sys.exit(1)
169
+ sys_exit("deployment_error", result.get("error", "Unknown error"))
158
170
 
159
171
  click.echo(FeedbackManager.success(message="Discard process successfully started"))
160
172
 
@@ -404,7 +416,7 @@ def create_deployment(
404
416
  sys.exit(0)
405
417
 
406
418
  click.echo(FeedbackManager.error(message="\n✗ Deployment is not valid"))
407
- sys.exit(1)
419
+ sys_exit("deployment_error", "Deployment is not valid: " + str(deployment.get("errors")))
408
420
 
409
421
  status = result.get("result")
410
422
  if status == "success":
@@ -415,15 +427,15 @@ def create_deployment(
415
427
  )
416
428
 
417
429
  if wait:
418
- click.echo(FeedbackManager.info(message="\n Deployment submitted successfully"))
430
+ click.echo(FeedbackManager.info(message="\n* Deployment submitted"))
419
431
  else:
420
432
  click.echo(FeedbackManager.success(message="\n✓ Deployment submitted successfully"))
421
433
  elif status == "no_changes":
422
- click.echo(FeedbackManager.highlight(message="Not deploying. No changes."))
434
+ click.echo(FeedbackManager.warning(message="Not deploying. No changes."))
423
435
  sys.exit(0)
424
436
  elif status == "failed":
425
437
  click.echo(FeedbackManager.error(message="Deployment failed"))
426
- sys.exit(1)
438
+ sys_exit("deployment_error", "Deployment failed" + str(deployment.get("errors")))
427
439
  else:
428
440
  click.echo(FeedbackManager.error(message=f"Unknown deployment result {status}"))
429
441
  except Exception as e:
@@ -433,16 +445,17 @@ def create_deployment(
433
445
  fd.close()
434
446
 
435
447
  if not deployment and not check:
436
- sys.exit(1)
448
+ sys_exit("deployment_error", "Deployment failed")
437
449
 
438
450
  if deployment and wait and not check:
451
+ click.echo(FeedbackManager.highlight(message="» Waiting for deployment to be ready..."))
439
452
  while True:
440
453
  url = f"{client.host}/v1/deployments/{deployment.get('id')}"
441
454
  res = api_fetch(url, HEADERS)
442
455
  deployment = res.get("deployment")
443
456
  if not deployment:
444
457
  click.echo(FeedbackManager.error(message="Error parsing deployment from response"))
445
- sys.exit(1)
458
+ sys_exit("deployment_error", "Error parsing deployment from response")
446
459
  if deployment.get("status") == "failed":
447
460
  click.echo(FeedbackManager.error(message="Deployment failed"))
448
461
  deploy_errors = deployment.get("errors")
@@ -452,14 +465,14 @@ def create_deployment(
452
465
  if auto:
453
466
  click.echo(FeedbackManager.error(message="Rolling back deployment"))
454
467
  discard_deployment(client.host, HEADERS, wait=wait)
455
- sys.exit(1)
468
+ sys_exit("deployment_error", "Deployment failed: " + str(deployment.get("errors")))
456
469
 
457
470
  if deployment.get("status") == "data_ready":
458
471
  break
459
472
 
460
473
  time.sleep(5)
461
474
 
462
- click.echo(FeedbackManager.success(message="Deployment is ready"))
475
+ click.echo(FeedbackManager.info(message="Deployment is ready"))
463
476
 
464
477
  if auto:
465
478
  promote_deployment(client.host, HEADERS, wait=wait)
@@ -513,12 +526,12 @@ def print_changes(result: dict, project: Project) -> None:
513
526
  tokens.append((change_type, token_name, "\n".join(added_perms), "\n".join(removed_perms)))
514
527
 
515
528
  if resources:
516
- click.echo(FeedbackManager.highlight(message="\n» Changes to be deployed...\n"))
529
+ click.echo(FeedbackManager.info(message="\n* Changes to be deployed:"))
517
530
  echo_safe_humanfriendly_tables_format_smart_table(resources, column_names=resources_columns)
518
531
  else:
519
- click.echo(FeedbackManager.highlight(message="\n» No changes to be deployed\n"))
532
+ click.echo(FeedbackManager.info(message="\n* No changes to be deployed"))
520
533
  if tokens:
521
- click.echo(FeedbackManager.highlight(message="\n» Changes in tokens to be deployed...\n"))
534
+ click.echo(FeedbackManager.info(message="\n* Changes in tokens to be deployed:"))
522
535
  echo_safe_humanfriendly_tables_format_smart_table(tokens, column_names=tokens_columns)
523
536
  else:
524
- click.echo(FeedbackManager.highlight(message="\n» No changes in tokens to be deployed\n"))
537
+ click.echo(FeedbackManager.info(message="* No changes in tokens to be deployed"))
@@ -15,7 +15,8 @@ class CLIException(click.exceptions.ClickException):
15
15
 
16
16
  def __init__(self, message: str, telemetry_event: Optional[str] = None, **kw_telemetry_event_data: Any) -> None:
17
17
  telemetry_event = telemetry_event or "error"
18
- data: Dict[str, Any] = {"error": message}
18
+ message_without_color = message.replace("\033[91m", "").replace("\033[0m", "")
19
+ data: Dict[str, Any] = {"error": message_without_color}
19
20
  data.update(kw_telemetry_event_data)
20
21
  add_telemetry_event(telemetry_event, **data)
21
22
  super().__init__(message)
@@ -89,3 +90,52 @@ class CLITokenException(CLIException):
89
90
 
90
91
  def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
91
92
  super().__init__(message, "token_error", **kw_telemetry_event_data)
93
+
94
+
95
+ class CLIBuildException(CLIException):
96
+ """Exceptions generated by the build commands"""
97
+
98
+ def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
99
+ super().__init__(message, "build_error", **kw_telemetry_event_data)
100
+
101
+
102
+ class CLIDeploymentException(CLIException):
103
+ """Exceptions generated by the deployment commands"""
104
+
105
+ def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
106
+ super().__init__(message, "deployment_error", **kw_telemetry_event_data)
107
+
108
+
109
+ class CLITestException(CLIException):
110
+ """Exceptions generated by the test commands"""
111
+
112
+ def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
113
+ super().__init__(message, "test_error", **kw_telemetry_event_data)
114
+
115
+
116
+ class CLICreateException(CLIException):
117
+ """Exceptions generated by the test commands"""
118
+
119
+ def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
120
+ super().__init__(message, "create_error", **kw_telemetry_event_data)
121
+
122
+
123
+ class CLIMockException(CLIException):
124
+ """Exceptions generated by the mock commands"""
125
+
126
+ def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
127
+ super().__init__(message, "mock_error", **kw_telemetry_event_data)
128
+
129
+
130
+ class CLILoginException(CLIException):
131
+ """Exceptions generated by the login commands"""
132
+
133
+ def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
134
+ super().__init__(message, "login_error", **kw_telemetry_event_data)
135
+
136
+
137
+ class CLILocalException(CLIException):
138
+ """Exceptions generated by the local commands"""
139
+
140
+ def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
141
+ super().__init__(message, "local_error", **kw_telemetry_event_data)
@@ -686,7 +686,6 @@ Please enter the ARN of the role you just created"""
686
686
  warning_confirm_delete_rows_datasource = prompt_message(
687
687
  "Do you want to delete {datasource}'s rows matching condition \"{delete_condition}\"? Once deleted, they can't be recovered"
688
688
  )
689
-
690
689
  warning_confirm_delete_pipe = prompt_message('Do you want to remove the pipe "{pipe}"?')
691
690
  warning_confirm_copy_pipe = prompt_message('Do you want to run a copy job from the pipe "{pipe}"?')
692
691
  warning_confirm_sink_job = prompt_message('Do you want to run a sink job from the pipe "{pipe}"?')
@@ -1005,9 +1004,6 @@ Please enter the ARN of the role you just created"""
1005
1004
  success_delete_rows_datasource = success_message(
1006
1005
  "** Data Source '{datasource}' rows deleted matching condition \"{delete_condition}\""
1007
1006
  )
1008
- success_delete_rows_datasource_no_rows = success_message(
1009
- "** Data Source '{datasource}' no rows to delete matching condition \"{delete_condition}\""
1010
- )
1011
1007
  success_dry_run_delete_rows_datasource = success_message(
1012
1008
  "** [DRY RUN] Data Source '{datasource}' rows '{rows}' matching condition \"{delete_condition}\" to be deleted"
1013
1009
  )
@@ -11,7 +11,7 @@ from docker.client import DockerClient
11
11
  from docker.models.containers import Container
12
12
  from tinybird.tb.modules.cli import cli
13
13
  from tinybird.tb.modules.common import coro
14
- from tinybird.tb.modules.exceptions import CLIException
14
+ from tinybird.tb.modules.exceptions import CLIException, CLILocalException
15
15
  from tinybird.tb.modules.feedback_manager import FeedbackManager
16
16
  from tinybird.tb.modules.local_common import TB_CONTAINER_NAME, TB_IMAGE_NAME, TB_LOCAL_PORT
17
17
 
@@ -71,7 +71,11 @@ def start_tinybird_local(
71
71
  if health == "healthy":
72
72
  break
73
73
  if health == "unhealthy":
74
- raise CLIException("Tinybird Local is unhealthy. Try running `tb local restart` in a few seconds.")
74
+ raise CLILocalException(
75
+ FeedbackManager.error(
76
+ message="Tinybird Local is unhealthy. Try running `tb local restart` in a few seconds."
77
+ )
78
+ )
75
79
 
76
80
  time.sleep(5)
77
81
 
@@ -125,8 +129,10 @@ def get_docker_client() -> DockerClient:
125
129
  client.ping()
126
130
  return client
127
131
  except Exception:
128
- raise CLIException(
129
- "No container runtime is running. Make sure a Docker-compatible runtime is installed and running."
132
+ raise CLILocalException(
133
+ FeedbackManager.error(
134
+ message="No container runtime is running. Make sure a Docker-compatible runtime is installed and running."
135
+ )
130
136
  )
131
137
 
132
138
 
@@ -7,7 +7,8 @@ import requests
7
7
 
8
8
  from tinybird.tb.client import AuthNoTokenException, TinyB
9
9
  from tinybird.tb.modules.config import CLIConfig
10
- from tinybird.tb.modules.exceptions import CLIException
10
+ from tinybird.tb.modules.exceptions import CLILocalException
11
+ from tinybird.tb.modules.feedback_manager import FeedbackManager
11
12
 
12
13
  TB_IMAGE_NAME = "tinybirdco/tinybird-local:latest"
13
14
  TB_CONTAINER_NAME = "tinybird-local"
@@ -34,7 +35,9 @@ async def get_tinybird_local_config(config_obj: Dict[str, Any], build: bool = Fa
34
35
  # ruff: noqa: ASYNC210
35
36
  tokens = requests.get(f"{TB_LOCAL_HOST}/tokens").json()
36
37
  except Exception:
37
- raise CLIException("Tinybird local is not running. Please run `tb local start` first.")
38
+ raise CLILocalException(
39
+ FeedbackManager.error(message="Tinybird local is not running. Please run `tb local start` first.")
40
+ )
38
41
 
39
42
  user_token = tokens["user_token"]
40
43
  admin_token = tokens["admin_token"]