tinybird-cli 2.0.0.dev1__tar.gz → 2.0.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 (45) hide show
  1. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/PKG-INFO +18 -2
  2. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/__cli__.py +2 -2
  3. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/check_pypi.py +2 -2
  4. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/datafile.py +19 -17
  5. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/feedback_manager.py +1 -1
  6. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/tb_cli_modules/branch.py +12 -21
  7. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/tb_cli_modules/cli.py +13 -17
  8. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/tb_cli_modules/common.py +19 -27
  9. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/tb_cli_modules/datasource.py +18 -28
  10. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/tb_cli_modules/job.py +4 -3
  11. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/tb_cli_modules/pipe.py +6 -20
  12. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/tb_cli_modules/test.py +4 -4
  13. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +2 -2
  14. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/tb_cli_modules/workspace.py +9 -9
  15. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird_cli.egg-info/PKG-INFO +18 -2
  16. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/setup.cfg +0 -0
  17. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/ch_utils/constants.py +0 -0
  18. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/ch_utils/engine.py +0 -0
  19. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/client.py +0 -0
  20. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/config.py +0 -0
  21. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/connector_settings.py +0 -0
  22. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/connectors.py +0 -0
  23. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/context.py +0 -0
  24. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/datatypes.py +0 -0
  25. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/sql.py +0 -0
  26. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/sql_template.py +0 -0
  27. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/sql_template_fmt.py +0 -0
  28. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/sql_toolset.py +0 -0
  29. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/syncasync.py +0 -0
  30. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/tb_cli.py +0 -0
  31. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/tb_cli_modules/auth.py +0 -0
  32. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/tb_cli_modules/cicd.py +0 -0
  33. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/tb_cli_modules/config.py +0 -0
  34. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/tb_cli_modules/connection.py +0 -0
  35. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/tb_cli_modules/exceptions.py +0 -0
  36. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/tb_cli_modules/telemetry.py +0 -0
  37. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
  38. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/tb_cli_modules/token.py +0 -0
  39. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  40. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird/tornado_template.py +0 -0
  41. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird_cli.egg-info/SOURCES.txt +0 -0
  42. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird_cli.egg-info/dependency_links.txt +0 -0
  43. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird_cli.egg-info/entry_points.txt +0 -0
  44. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird_cli.egg-info/requires.txt +0 -0
  45. {tinybird-cli-2.0.0.dev1 → tinybird-cli-2.0.1.dev0}/tinybird_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tinybird-cli
3
- Version: 2.0.0.dev1
3
+ Version: 2.0.1.dev0
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://docs.tinybird.co/cli.html
6
6
  Author: Tinybird
@@ -19,10 +19,26 @@ Changelog
19
19
 
20
20
  ---------
21
21
 
22
+ 2.0.0
23
+ ************
24
+
25
+ Released new version 2.0.0 with all these changes:
26
+
27
+ - `Changed` All commands exit with 1 when there's an exception
28
+ - `Removed` Prefixes are not supported anymore ([context](https://www.tinybird.co/changelog/2023-10-09-versions), and [how to migrate](https://www.tinybird.co/docs/guides/staging-and-production-workspaces.html#migrating-from-prefixes))
29
+ - `Removed` `tb pipe create`
30
+
31
+
32
+ 2.0.0.dev2
33
+ ************
34
+
35
+ - `Changed` All commands exit with 1 when there's an exception
36
+ - `Removed` `tb pipe create`
37
+
22
38
  2.0.0.dev1
23
39
  ************
24
40
 
25
- - `Changed` Prefixes are not supported anymore.
41
+ - `Removed` Prefixes are not supported anymore
26
42
 
27
43
 
28
44
  1.3.0
@@ -4,5 +4,5 @@ __description__ = 'Tinybird Command Line Tool'
4
4
  __url__ = 'https://docs.tinybird.co/cli.html'
5
5
  __author__ = 'Tinybird'
6
6
  __author_email__ = 'support@tinybird.co'
7
- __version__ = '2.0.0.dev1'
8
- __revision__ = '166d72b'
7
+ __version__ = '2.0.1.dev0'
8
+ __revision__ = '40c7bee'
@@ -15,9 +15,9 @@ class CheckPypi:
15
15
  disable_ssl: bool = getenv_bool("TB_DISABLE_SSL_CHECKS", False)
16
16
  response: requests.Response = await requests_get(PYPY_URL, verify=not disable_ssl)
17
17
  if response.status_code != 200:
18
- CLIException(FeedbackManager.error_exception(error=response.content.decode("utf-8")))
18
+ raise CLIException(FeedbackManager.error_exception(error=response.content.decode("utf-8")))
19
19
  version = response.json()["info"]["version"]
20
20
  except Exception as e:
21
- CLIException(FeedbackManager.error_exception(error=str(e)))
21
+ raise CLIException(FeedbackManager.error_exception(error=str(e)))
22
22
 
23
23
  return version
@@ -1045,7 +1045,7 @@ async def process_file(
1045
1045
  ]
1046
1046
 
1047
1047
  if not all(required_params):
1048
- raise Exception(FeedbackManager.error_unknown_kafka_connection(datasource=name))
1048
+ raise click.ClickException(FeedbackManager.error_unknown_kafka_connection(datasource=name))
1049
1049
 
1050
1050
  connector = await tb_client.connection_create_kafka(**connector_params)
1051
1051
  except Exception as e:
@@ -1076,7 +1076,7 @@ async def process_file(
1076
1076
 
1077
1077
  if service and service.lower() == "bigquery":
1078
1078
  if not await tb_client.check_gcp_read_permissions():
1079
- raise Exception(FeedbackManager.error_unknown_bq_connection(datasource=datasource["name"]))
1079
+ raise click.ClickException(FeedbackManager.error_unknown_bq_connection(datasource=datasource["name"]))
1080
1080
 
1081
1081
  # Bigquery doesn't have a datalink, so we can stop here
1082
1082
  return params
@@ -1086,7 +1086,7 @@ async def process_file(
1086
1086
  connector_id: Optional[str] = node.get("import_connector", None)
1087
1087
  connector_name: Optional[str] = node.get("import_connection_name", None)
1088
1088
  if not connector_name and not connector_id:
1089
- raise Exception(FeedbackManager.error_missing_connection_name(datasource=datasource["name"]))
1089
+ raise click.ClickException(FeedbackManager.error_missing_connection_name(datasource=datasource["name"]))
1090
1090
 
1091
1091
  if not connector_id:
1092
1092
  assert isinstance(connector_name, str)
@@ -1107,10 +1107,12 @@ async def process_file(
1107
1107
 
1108
1108
  if service in PREVIEW_CONNECTOR_SERVICES:
1109
1109
  if not params.get("import_bucket_uri", None):
1110
- raise Exception(FeedbackManager.error_missing_bucket_uri(datasource=datasource["name"]))
1110
+ raise click.ClickException(FeedbackManager.error_missing_bucket_uri(datasource=datasource["name"]))
1111
1111
  else:
1112
1112
  if not params.get("import_external_datasource", None):
1113
- raise Exception(FeedbackManager.error_missing_external_datasource(datasource=datasource["name"]))
1113
+ raise click.ClickException(
1114
+ FeedbackManager.error_missing_external_datasource(datasource=datasource["name"])
1115
+ )
1114
1116
 
1115
1117
  return params
1116
1118
 
@@ -1392,7 +1394,7 @@ async def process_file(
1392
1394
  }
1393
1395
  ]
1394
1396
  else:
1395
- raise Exception(FeedbackManager.error_file_extension(filename=filename))
1397
+ raise click.ClickException(FeedbackManager.error_file_extension(filename=filename))
1396
1398
 
1397
1399
 
1398
1400
  def full_path_by_name(
@@ -2024,7 +2026,7 @@ async def check_pipe(
2024
2026
  node["params"]["type"] = PipeNodeTypes.STANDARD
2025
2027
 
2026
2028
  if populate:
2027
- raise Exception(FeedbackManager.error_check_pipes_populate())
2029
+ raise click.ClickException(FeedbackManager.error_check_pipes_populate())
2028
2030
 
2029
2031
  runner = PipeCheckerRunner(pipe["name"], host)
2030
2032
  headers = (
@@ -2053,7 +2055,7 @@ async def check_pipe(
2053
2055
  )
2054
2056
 
2055
2057
  if not r or r.status_code != 200:
2056
- raise Exception(FeedbackManager.error_check_pipes_api(pipe=pipe["name"]))
2058
+ raise click.ClickException(FeedbackManager.error_check_pipes_api(pipe=pipe["name"]))
2057
2059
 
2058
2060
  pipe_requests_to_check: List[Dict[str, str]] = []
2059
2061
  for x in r.json().get("data", []):
@@ -2410,7 +2412,7 @@ async def new_pipe(
2410
2412
  f"/v0/pipes?{urlencode(params)}", method="POST", headers=post_headers, data=json.dumps(body)
2411
2413
  )
2412
2414
  except Exception as e:
2413
- raise Exception(FeedbackManager.error_pushing_pipe(pipe=p["name"], error=str(e)))
2415
+ raise click.ClickException(FeedbackManager.error_pushing_pipe(pipe=p["name"], error=str(e)))
2414
2416
 
2415
2417
  datasource = data.get("datasource", None)
2416
2418
  created_datasource = data.get("created_datasource", None)
@@ -2505,7 +2507,7 @@ async def new_pipe(
2505
2507
  r = await tb_client.create_token(token_name, f"PIPES:{tk['permissions']}:{p['name']}", "P", p["name"])
2506
2508
  token = r["token"] # type: ignore
2507
2509
  except Exception as e:
2508
- raise Exception(FeedbackManager.error_creating_pipe(error=e))
2510
+ raise click.ClickException(FeedbackManager.error_creating_pipe(error=e))
2509
2511
  else:
2510
2512
  click.echo(FeedbackManager.info_create_found_token(token=token_name))
2511
2513
  scopes = [f"PIPES:{tk['permissions']}:{p['name']}"]
@@ -2516,7 +2518,7 @@ async def new_pipe(
2516
2518
  r = await tb_client.alter_tokens(token_name, scopes)
2517
2519
  token = r["token"] # type: ignore
2518
2520
  except Exception as e:
2519
- raise Exception(FeedbackManager.error_creating_pipe(error=e))
2521
+ raise click.ClickException(FeedbackManager.error_creating_pipe(error=e))
2520
2522
  click.echo(FeedbackManager.success_test_endpoint(host=host, pipe=p["name"], token=token))
2521
2523
 
2522
2524
 
@@ -2679,7 +2681,7 @@ async def new_ds(
2679
2681
  )
2680
2682
 
2681
2683
  except Exception as e:
2682
- raise Exception(FeedbackManager.error_creating_datasource(error=str(e)))
2684
+ raise click.ClickException(FeedbackManager.error_creating_datasource(error=str(e)))
2683
2685
  return
2684
2686
 
2685
2687
  if not force:
@@ -2779,7 +2781,7 @@ async def new_ds(
2779
2781
  if current_connector["name"] != ds_params["connection"]:
2780
2782
  param = "connection"
2781
2783
  datafile_param = ImportReplacements.get_datafile_param_for_linker_param(service, param) or param
2782
- raise Exception(FeedbackManager.error_updating_connector_not_supported(param=datafile_param))
2784
+ raise click.ClickException(FeedbackManager.error_updating_connector_not_supported(param=datafile_param))
2783
2785
 
2784
2786
  linkers = current_connector.get("linkers", [])
2785
2787
  linker = next((linker for linker in linkers if linker["datasource_id"] == existing_ds["id"]), None)
@@ -2827,7 +2829,7 @@ async def new_ds(
2827
2829
  await client.datasource_delete(ds_name)
2828
2830
  click.echo(FeedbackManager.success_delete_datasource(datasource=ds_name))
2829
2831
  except Exception:
2830
- raise Exception(FeedbackManager.error_removing_datasource(datasource=ds_name))
2832
+ raise click.ClickException(FeedbackManager.error_removing_datasource(datasource=ds_name))
2831
2833
  return
2832
2834
  else:
2833
2835
  if alter_error_message:
@@ -2942,7 +2944,7 @@ async def exec_file(
2942
2944
  elif r["resource"] == "tokens":
2943
2945
  await new_token(r, tb_client, force)
2944
2946
  else:
2945
- raise Exception(FeedbackManager.error_unknown_resource(resource=r["resource"]))
2947
+ raise click.ClickException(FeedbackManager.error_unknown_resource(resource=r["resource"]))
2946
2948
 
2947
2949
 
2948
2950
  def get_name_version(ds: str) -> Dict[str, Any]:
@@ -3769,7 +3771,7 @@ async def check_fixtures_data(
3769
3771
  else:
3770
3772
  click.echo(FeedbackManager.warning_fixture_not_found(datasource_name=name))
3771
3773
  else:
3772
- raise Exception(FeedbackManager.error_unknown_resource(resource=resource["resource"]))
3774
+ raise click.ClickException(FeedbackManager.error_unknown_resource(resource=resource["resource"]))
3773
3775
 
3774
3776
 
3775
3777
  DATAFILE_NEW_LINE = "\n"
@@ -4187,7 +4189,7 @@ async def folder_pull(
4187
4189
  if verbose:
4188
4190
  click.echo(FeedbackManager.info_skip_already_exists())
4189
4191
  except Exception as e:
4190
- raise Exception(FeedbackManager.error_exception(error=e))
4192
+ raise click.ClickException(FeedbackManager.error_exception(error=e))
4191
4193
  return
4192
4194
 
4193
4195
  try:
@@ -235,7 +235,6 @@ class FeedbackManager:
235
235
  error_missing_datasource_name = error_message(
236
236
  "Missing datasource name for pipe creation after the DATASOURCE label"
237
237
  )
238
- error_no_test_results = error_message("Error: No test results to show")
239
238
  error_running_test = error_message("There was a problem running test file {file} (use -v for more info)")
240
239
  error_processing_blocks = error_message("There was been an error while processing some blocks: {error}")
241
240
  error_switching_to_main = error_message(
@@ -342,6 +341,7 @@ Ready? """
342
341
  "You are going to manually update workspace release commit reference manually, this is just for special ocasions. Do you want to update current commit release reference '{current_commit}' to '{new_commit}'?"
343
342
  )
344
343
 
344
+ warning_no_test_results = warning_message("Warning: No test results to show")
345
345
  warning_using_environment_token = warning_message("** You're using the token defined in $TB_TOKEN.")
346
346
  warning_using_environment_host = warning_message("** You're using the token defined in $TB_HOST.")
347
347
 
@@ -396,8 +396,7 @@ async def create_branch(
396
396
  ctx: Context, env_name: str, last_partition: bool, all: bool, ignore_datasources: List[str], wait: bool
397
397
  ) -> None:
398
398
  if last_partition and all:
399
- click.echo(FeedbackManager.error_exception(error="Use --last-partition or --all but not both"))
400
- return
399
+ raise CLIException(FeedbackManager.error_exception(error="Use --last-partition or --all but not both"))
401
400
  await create_workspace_branch(ctx, env_name, last_partition, all, list(ignore_datasources), wait)
402
401
 
403
402
 
@@ -413,8 +412,7 @@ async def delete_branch(ctx: Context, env_name_or_id: str, yes: bool) -> None:
413
412
  config, _, _ = await get_config_and_hosts(ctx)
414
413
 
415
414
  if env_name_or_id == MAIN_BRANCH:
416
- click.echo(FeedbackManager.error_not_allowed_in_main_branch())
417
- return
415
+ raise CLIException(FeedbackManager.error_not_allowed_in_main_branch())
418
416
 
419
417
  try:
420
418
  workspace_branches = await get_current_workspace_branches(client, config)
@@ -453,7 +451,7 @@ async def delete_branch(ctx: Context, env_name_or_id: str, yes: bool) -> None:
453
451
  if workspace_main:
454
452
  await switch_to_workspace_by_user_workspace_data(ctx, workspace_main)
455
453
  else:
456
- click.echo(FeedbackManager.error_switching_to_main())
454
+ raise CLIException(FeedbackManager.error_switching_to_main())
457
455
 
458
456
 
459
457
  @env.command(
@@ -490,12 +488,10 @@ async def delete_branch(ctx: Context, env_name_or_id: str, yes: bool) -> None:
490
488
  @coro
491
489
  async def data_branch(ctx: Context, last_partition: bool, all: bool, ignore_datasources: List[str], wait: bool) -> None:
492
490
  if last_partition and all:
493
- click.echo(FeedbackManager.error_exception(error="Use --last-partition or --all but not both"))
494
- return
491
+ raise CLIException(FeedbackManager.error_exception(error="Use --last-partition or --all but not both"))
495
492
 
496
493
  if not last_partition and not all:
497
- click.echo(FeedbackManager.error_exception(error="Use --last-partition or --all"))
498
- return
494
+ raise CLIException(FeedbackManager.error_exception(error="Use --last-partition or --all"))
499
495
 
500
496
  obj: Dict[str, Any] = ctx.ensure_object(dict)
501
497
  client = obj["client"]
@@ -506,8 +502,7 @@ async def data_branch(ctx: Context, last_partition: bool, all: bool, ignore_data
506
502
  raise CLIException(FeedbackManager.error_unable_to_identify_main_workspace())
507
503
 
508
504
  if current_main_workspace["id"] == config["id"]:
509
- click.echo(FeedbackManager.error_not_allowed_in_main_branch())
510
- return
505
+ raise CLIException(FeedbackManager.error_not_allowed_in_main_branch())
511
506
 
512
507
  try:
513
508
  response = await client.branch_workspace_data(config["id"], last_partition, all, ignore_datasources)
@@ -597,7 +592,7 @@ async def regression_tests(
597
592
  raise CLIException(FeedbackManager.error_unable_to_identify_main_workspace())
598
593
 
599
594
  if current_main_workspace["id"] == config["id"]:
600
- click.echo(FeedbackManager.error_not_allowed_in_main_branch())
595
+ raise CLIException(FeedbackManager.error_not_allowed_in_main_branch())
601
596
  return
602
597
  try:
603
598
  response = await client.branch_regression_tests_file(
@@ -647,7 +642,7 @@ async def _run_regression(
647
642
  raise CLIException(FeedbackManager.error_unable_to_identify_main_workspace())
648
643
 
649
644
  if current_main_workspace["id"] == config["id"]:
650
- click.echo(FeedbackManager.error_not_allowed_in_main_branch())
645
+ raise CLIException(FeedbackManager.error_not_allowed_in_main_branch())
651
646
  return
652
647
  try:
653
648
  response = await client.branch_regression_tests(
@@ -1025,20 +1020,17 @@ async def datasource_copy_from_main(
1025
1020
  config = ctx.ensure_object(dict)["config"]
1026
1021
 
1027
1022
  if sql and sql_from_main:
1028
- click.echo(FeedbackManager.error_exception(error="Use --sql or --sql-from-main but not both"))
1029
- return
1023
+ raise CLIException(FeedbackManager.error_exception(error="Use --sql or --sql-from-main but not both"))
1030
1024
 
1031
1025
  if not sql and not sql_from_main:
1032
- click.echo(FeedbackManager.error_exception(error="Use --sql or --sql-from-main"))
1033
- return
1026
+ raise CLIException(FeedbackManager.error_exception(error="Use --sql or --sql-from-main"))
1034
1027
 
1035
1028
  current_main_workspace = await get_current_main_workspace(client, config)
1036
1029
  if not current_main_workspace:
1037
1030
  raise CLIException(FeedbackManager.error_unable_to_identify_main_workspace())
1038
1031
 
1039
1032
  if current_main_workspace["id"] == config["id"] and sql_from_main:
1040
- click.echo(FeedbackManager.error_not_allowed_in_main_branch())
1041
- return
1033
+ raise CLIException(FeedbackManager.error_not_allowed_in_main_branch())
1042
1034
 
1043
1035
  response = await client.datasource_query_copy(
1044
1036
  datasource_name, sql if sql else f"SELECT * FROM main.{datasource_name}"
@@ -1088,8 +1080,7 @@ async def deploy_env(ctx: Context, semver: str, wait: bool, verbose: bool, dry_r
1088
1080
  raise CLIException(FeedbackManager.error_unable_to_identify_main_workspace())
1089
1081
 
1090
1082
  if current_main_workspace["id"] == config["id"]:
1091
- click.echo(FeedbackManager.error_not_allowed_in_main_branch())
1092
- return
1083
+ raise CLIException(FeedbackManager.error_not_allowed_in_main_branch())
1093
1084
 
1094
1085
  workspaces: List[Dict[str, Any]] = (await client.user_workspaces_and_branches()).get("workspaces", [])
1095
1086
  current_ws: Dict[str, Any] = next(
@@ -319,8 +319,7 @@ async def init(
319
319
  )
320
320
 
321
321
  if current_ws.get("is_branch"):
322
- click.echo(FeedbackManager.error_not_allowed_in_branch())
323
- return
322
+ raise CLIException(FeedbackManager.error_not_allowed_in_branch())
324
323
 
325
324
  await folder_init(client, folder, generate_datasources, generate_releases=True, force=force)
326
325
 
@@ -328,6 +327,7 @@ async def init(
328
327
  return
329
328
 
330
329
  cli_git_release = None
330
+ error = False
331
331
  try:
332
332
  cli_git_release = CLIGitRelease(path=folder)
333
333
  except CLIGitReleaseException:
@@ -358,6 +358,7 @@ async def init(
358
358
  final_response = FeedbackManager.error_release_already_set(
359
359
  workspace=current_ws["name"], commit=current_ws["release"]["commit"]
360
360
  )
361
+ error = True
361
362
 
362
363
  elif override_commit:
363
364
  if click.confirm(
@@ -425,6 +426,8 @@ async def init(
425
426
  client, current_ws, config, path=cli_git_release.working_dir(), data_project_dir=data_project_dir
426
427
  )
427
428
 
429
+ if error:
430
+ raise CLIException(final_response)
428
431
  click.echo(final_response)
429
432
 
430
433
 
@@ -861,22 +864,19 @@ async def diff(
861
864
  for workspace in response["workspaces"]:
862
865
  if config["id"] == workspace["id"]:
863
866
  if not workspace.get("is_branch"):
864
- click.echo(FeedbackManager.error_not_a_branch())
865
- return
867
+ raise CLIException(FeedbackManager.error_not_a_branch())
866
868
 
867
869
  origin = workspace["main"]
868
870
  workspace = await get_current_main_workspace(client, config)
869
871
 
870
872
  if not workspace:
871
- click.echo(FeedbackManager.error_workspace(workspace=origin))
872
- return
873
+ raise CLIException(FeedbackManager.error_workspace(workspace=origin))
873
874
 
874
875
  ws_client = _get_tb_client(workspace["token"], config["host"])
875
876
  break
876
877
 
877
878
  if not ws_client:
878
- click.echo(FeedbackManager.error_workspace(workspace=origin))
879
- return
879
+ raise CLIException(FeedbackManager.error_workspace(workspace=origin))
880
880
  changed = await diff_command(
881
881
  list(filename) if filename else None, fmt, ws_client, no_color, with_print=not only_resources_changed
882
882
  )
@@ -924,12 +924,10 @@ async def sql(
924
924
  if query:
925
925
  q = query.lower().strip()
926
926
  if q.startswith("insert"):
927
- click.echo(FeedbackManager.error_invalid_query())
928
927
  click.echo(FeedbackManager.info_append_data())
929
- return
928
+ raise CLIException(FeedbackManager.error_invalid_query())
930
929
  if q.startswith("delete"):
931
- click.echo(FeedbackManager.error_invalid_query())
932
- return
930
+ raise CLIException(FeedbackManager.error_invalid_query())
933
931
  res = await client.query(
934
932
  f"SELECT * FROM ({query}) LIMIT {rows_limit} FORMAT {req_format}", pipeline=pipeline
935
933
  )
@@ -979,12 +977,10 @@ async def sql(
979
977
  f"SELECT * FROM ({query}) LIMIT {rows_limit} FORMAT {req_format}", pipeline=pipeline
980
978
  )
981
979
  except Exception as e:
982
- click.echo(FeedbackManager.error_exception(error=str(e)))
983
- return
980
+ raise CLIException(FeedbackManager.error_exception(error=str(e)))
984
981
 
985
982
  if "error" in res:
986
- click.echo(FeedbackManager.error_exception(error=res["error"]))
987
- return
983
+ raise CLIException(FeedbackManager.error_exception(error=res["error"]))
988
984
 
989
985
  if stats:
990
986
  stats_query = f"SELECT * FROM ({query}) LIMIT {rows_limit} FORMAT JSON"
@@ -1292,7 +1288,7 @@ async def materialize(
1292
1288
  await _populate(pipe, node_name, f_pipe)
1293
1289
  click.echo(FeedbackManager.success_created_matview(name=datasource_name))
1294
1290
  except Exception as e:
1295
- click.echo(FeedbackManager.error_exception(error=str(e)))
1291
+ raise CLIException(FeedbackManager.error_exception(error=str(e)))
1296
1292
 
1297
1293
 
1298
1294
  def __patch_click_output():
@@ -427,8 +427,7 @@ fi
427
427
 
428
428
  async def configure_connector(connector):
429
429
  if connector not in SUPPORTED_CONNECTORS:
430
- click.echo(FeedbackManager.error_invalid_connector(connectors=", ".join(SUPPORTED_CONNECTORS)))
431
- return
430
+ raise CLIException(FeedbackManager.error_invalid_connector(connectors=", ".join(SUPPORTED_CONNECTORS)))
432
431
 
433
432
  file_name = f".tinyb_{connector}"
434
433
  config_file = Path(getcwd()) / file_name
@@ -815,8 +814,7 @@ async def get_available_starterkits(ctx: Context) -> List[Dict[str, Any]]:
815
814
  ctx_dict["available_starterkits"] = available_starterkits
816
815
  return available_starterkits
817
816
  except Exception as ex:
818
- click.echo(FeedbackManager.error_exception(error=ex))
819
- return []
817
+ raise CLIException(FeedbackManager.error_exception(error=ex))
820
818
 
821
819
 
822
820
  async def get_starterkit(ctx: Context, name: str) -> Optional[Dict[str, Any]]:
@@ -1095,7 +1093,7 @@ async def print_branch_regression_tests_summary(client, job_id, host, response=N
1095
1093
  for failure in step["run"]["failed"]:
1096
1094
  click.echo(f"❌ FAILED {failure['name']}\n")
1097
1095
  if failed:
1098
- raise click.ClickException(
1096
+ raise CLIException(
1099
1097
  "Check Failures Detail above for more information. If the results are expected, skip asserts or increase thresholds, see 💡 Hints above (note skip asserts flags are applied to all regression tests, so use them when it makes sense).\n\nIf you are using the CI template for GitHub or GitLab you can add skip asserts flags as labels to the MR and they are automatically applied. Find available flags to skip asserts and thresholds here => https://www.tinybird.co/docs/guides/continuous-integration.html#run-the-tests"
1100
1098
  )
1101
1099
 
@@ -1147,8 +1145,7 @@ async def deploy_environment(
1147
1145
  )
1148
1146
  await switch_workspace(ctx, config["name"], only_environments=True)
1149
1147
  except Exception as e:
1150
- click.echo(FeedbackManager.error_exception(error=str(e)))
1151
- await switch_workspace(ctx, config["name"], only_environments=True)
1148
+ raise CLIException(FeedbackManager.error_exception(error=str(e)))
1152
1149
 
1153
1150
 
1154
1151
  class MergeBranchStatus:
@@ -1277,8 +1274,7 @@ async def push_data(
1277
1274
  if connector and sql:
1278
1275
  load_connector_config(ctx, connector, False, check_uninstalled=False)
1279
1276
  if connector not in ctx.obj:
1280
- click.echo(FeedbackManager.error_connector_not_configured(connector=connector))
1281
- return
1277
+ raise CLIException(FeedbackManager.error_connector_not_configured(connector=connector))
1282
1278
  else:
1283
1279
  _connector: Connector = ctx.obj[connector]
1284
1280
  click.echo(FeedbackManager.info_starting_export_process(connector=connector))
@@ -1346,19 +1342,18 @@ async def push_data(
1346
1342
  try:
1347
1343
  datasource = await client.get_datasource(datasource_name)
1348
1344
  except DoesNotExistException:
1349
- click.echo(FeedbackManager.error_datasource_does_not_exist(datasource=datasource_name))
1345
+ raise CLIException(FeedbackManager.error_datasource_does_not_exist(datasource=datasource_name))
1350
1346
  except Exception as e:
1351
- click.echo(FeedbackManager.error_exception(error=str(e)))
1352
- return
1347
+ raise CLIException(FeedbackManager.error_exception(error=str(e)))
1353
1348
 
1354
1349
  total_rows = (datasource.get("statistics", {}) or {}).get("row_count", 0)
1355
1350
  appended_rows = 0
1356
1351
  parser = None
1357
1352
 
1358
1353
  if "error" in res and res["error"]:
1359
- click.echo(FeedbackManager.error_exception(error=res["error"]))
1354
+ raise CLIException(FeedbackManager.error_exception(error=res["error"]))
1360
1355
  if "errors" in res and res["errors"]:
1361
- click.echo(FeedbackManager.error_exception(error=res["errors"]))
1356
+ raise CLIException(FeedbackManager.error_exception(error=res["errors"]))
1362
1357
  if "blocks" in res and res["blocks"]:
1363
1358
  for block in res["blocks"]:
1364
1359
  if "process_return" in block and block["process_return"] is not None:
@@ -1386,10 +1381,9 @@ async def push_data(
1386
1381
  output = await gather_with_concurrency(concurrency, *tasks)
1387
1382
  parser, total_rows, appended_rows = list(output)[-1]
1388
1383
  except OperationCanNotBePerformed as e:
1389
- click.echo(FeedbackManager.error_operation_can_not_be_performed(error=e))
1384
+ raise CLIException(FeedbackManager.error_operation_can_not_be_performed(error=e))
1390
1385
  except Exception as e:
1391
- click.echo(FeedbackManager.error_exception(error=e))
1392
- sys.exit(1)
1386
+ raise CLIException(FeedbackManager.error_exception(error=e))
1393
1387
  else:
1394
1388
  click.echo(FeedbackManager.success_progress_blocks())
1395
1389
  if mode == "append":
@@ -1554,9 +1548,9 @@ async def switch_workspace(ctx, workspace_name_or_id, only_environments=False):
1554
1548
 
1555
1549
  if not workspace:
1556
1550
  if only_environments:
1557
- click.echo(FeedbackManager.error_branch(branch=workspace_name_or_id))
1551
+ raise CLIException(FeedbackManager.error_branch(branch=workspace_name_or_id))
1558
1552
  else:
1559
- click.echo(FeedbackManager.error_workspace(workspace=workspace_name_or_id))
1553
+ raise CLIException(FeedbackManager.error_workspace(workspace=workspace_name_or_id))
1560
1554
  return
1561
1555
 
1562
1556
  client = _get_tb_client(workspace["token"], config["host"])
@@ -1577,8 +1571,7 @@ async def switch_workspace(ctx, workspace_name_or_id, only_environments=False):
1577
1571
  await write_config(config)
1578
1572
  click.echo(FeedbackManager.success_now_using_config(name=config["name"], id=config["id"]))
1579
1573
  except Exception as e:
1580
- click.echo(FeedbackManager.error_exception(error=str(e)))
1581
- return
1574
+ raise CLIException(FeedbackManager.error_exception(error=str(e)))
1582
1575
 
1583
1576
 
1584
1577
  async def switch_to_workspace_by_user_workspace_data(ctx, user_workspace_data: Dict):
@@ -1605,8 +1598,7 @@ async def switch_to_workspace_by_user_workspace_data(ctx, user_workspace_data: D
1605
1598
  await write_config(config)
1606
1599
  click.echo(FeedbackManager.success_now_using_config(name=config["name"], id=config["id"]))
1607
1600
  except Exception as e:
1608
- click.echo(FeedbackManager.error_exception(error=str(e)))
1609
- return
1601
+ raise CLIException(FeedbackManager.error_exception(error=str(e)))
1610
1602
 
1611
1603
 
1612
1604
  async def print_current_workspace(ctx):
@@ -1943,14 +1935,14 @@ async def wait_job(
1943
1935
  # TODO: Simplify this as it's not needed to use two functions for
1944
1936
  result = await wait_job_no_ui(tb_client, job_id, timeout, progressbar_cb)
1945
1937
  if result["status"] != "done":
1946
- click.echo(FeedbackManager.error_while_running_job(error=result["error"]))
1938
+ raise CLIException(FeedbackManager.error_while_running_job(error=result["error"]))
1947
1939
  return result
1948
1940
  except asyncio.TimeoutError:
1949
- raise click.ClickException(FeedbackManager.error_while_running_job(error="Reach timeout, job cancelled"))
1941
+ raise CLIException(FeedbackManager.error_while_running_job(error="Reach timeout, job cancelled"))
1950
1942
  except JobException as e:
1951
- raise click.ClickException(FeedbackManager.error_while_running_job(error=str(e)))
1943
+ raise CLIException(FeedbackManager.error_while_running_job(error=str(e)))
1952
1944
  except Exception as e:
1953
- raise click.ClickException(FeedbackManager.error_getting_job_info(error=str(e), url=job_url))
1945
+ raise CLIException(FeedbackManager.error_getting_job_info(error=str(e), url=job_url))
1954
1946
 
1955
1947
 
1956
1948
  async def wait_job_no_ui(
@@ -113,7 +113,7 @@ async def datasource_ls(ctx: Context, match: Optional[str], format_: str):
113
113
  elif format_ == "json":
114
114
  click.echo(json.dumps({"datasources": table_machine_readable}, indent=2))
115
115
  else:
116
- click.echo(FeedbackManager.error_datasource_ls_type)
116
+ raise CLIDatasourceException(FeedbackManager.error_datasource_ls_type())
117
117
 
118
118
 
119
119
  @datasource.command(name="append")
@@ -163,12 +163,10 @@ async def datasource_append(
163
163
  """
164
164
 
165
165
  if not url and not connector:
166
- click.echo(FeedbackManager.error_missing_url_or_connector(datasource=datasource_name))
167
- return
166
+ raise CLIDatasourceException(FeedbackManager.error_missing_url_or_connector(datasource=datasource_name))
168
167
 
169
168
  if incremental and not connector:
170
- click.echo(FeedbackManager.error_incremental_not_supported())
171
- return
169
+ raise CLIDatasourceException(FeedbackManager.error_incremental_not_supported())
172
170
 
173
171
  if incremental:
174
172
  date = None
@@ -255,8 +253,7 @@ async def datasource_analyze(ctx, url_or_file, connector):
255
253
  if connector:
256
254
  load_connector_config(ctx, connector, False, check_uninstalled=False)
257
255
  if connector not in ctx.obj:
258
- click.echo(FeedbackManager.error_connector_not_configured(connector=connector))
259
- return
256
+ raise CLIDatasourceException(FeedbackManager.error_connector_not_configured(connector=connector))
260
257
  else:
261
258
  _connector = ctx.obj[connector]
262
259
 
@@ -396,9 +393,11 @@ async def datasource_truncate(ctx, datasource_name, yes, cascade):
396
393
  try:
397
394
  await client.datasource_truncate(cascade_ds)
398
395
  except DoesNotExistException:
399
- click.echo(FeedbackManager.error_datasource_does_not_exist(datasource=datasource_name))
396
+ raise CLIDatasourceException(
397
+ FeedbackManager.error_datasource_does_not_exist(datasource=datasource_name)
398
+ )
400
399
  except Exception as e:
401
- click.echo(FeedbackManager.error_exception(error=e))
400
+ raise CLIDatasourceException(FeedbackManager.error_exception(error=e))
402
401
  click.echo(FeedbackManager.success_truncate_datasource(datasource=cascade_ds))
403
402
 
404
403
 
@@ -453,8 +452,7 @@ async def datasource_delete_rows(ctx, datasource_name, sql_condition, yes, wait,
453
452
  try:
454
453
  res = await client._req(f"v0/jobs/{job_id}")
455
454
  except Exception:
456
- click.echo(FeedbackManager.error_job_status(url=job_url))
457
- break
455
+ raise CLIDatasourceException(FeedbackManager.error_job_status(url=job_url))
458
456
  if res["status"] == "done":
459
457
  print("\n")
460
458
  click.echo(
@@ -465,8 +463,7 @@ async def datasource_delete_rows(ctx, datasource_name, sql_condition, yes, wait,
465
463
  break
466
464
  elif res["status"] == "error":
467
465
  print("\n")
468
- click.echo(FeedbackManager.error_exception(error=res["error"]))
469
- break
466
+ raise CLIDatasourceException(FeedbackManager.error_exception(error=res["error"]))
470
467
  await asyncio.sleep(1)
471
468
  i += 1
472
469
  progress_line(i)
@@ -499,8 +496,7 @@ async def generate_datasource(ctx: Context, connector: str, filenames, force: bo
499
496
  if connector:
500
497
  load_connector_config(ctx, connector, False, check_uninstalled=False)
501
498
  if connector not in ctx.ensure_object(dict):
502
- click.echo(FeedbackManager.error_connector_not_configured(connector=connector))
503
- return
499
+ raise CLIDatasourceException(FeedbackManager.error_connector_not_configured(connector=connector))
504
500
  else:
505
501
  _connector = ctx.ensure_object(dict)[connector]
506
502
 
@@ -571,7 +567,7 @@ async def datasource_connect(ctx, connection, datasource_name, topic, group, aut
571
567
  datasource_id = resp["datasource"]["id"]
572
568
  click.echo(FeedbackManager.success_datasource_kafka_connected(id=datasource_id))
573
569
  else:
574
- click.echo(FeedbackManager.error_unknown_connection_service(service=service))
570
+ raise CLIDatasourceException(FeedbackManager.error_unknown_connection_service(service=service))
575
571
 
576
572
 
577
573
  @datasource.command(name="share")
@@ -607,12 +603,10 @@ async def datasource_share(ctx: Context, datasource_name: str, workspace_name_or
607
603
  current_workspace = next((workspace for workspace in workspaces if workspace["id"] == config["id"]), None)
608
604
 
609
605
  if not destination_workspace:
610
- click.echo(FeedbackManager.error_workspace(workspace=workspace_name_or_id))
611
- return
606
+ raise CLIDatasourceException(FeedbackManager.error_workspace(workspace=workspace_name_or_id))
612
607
 
613
608
  if not current_workspace:
614
- click.echo(FeedbackManager.error_not_authenticated())
615
- return
609
+ raise CLIDatasourceException(FeedbackManager.error_not_authenticated())
616
610
 
617
611
  if not user_token:
618
612
  user_token = ask_for_user_token("share a Data Source", ui_host)
@@ -638,8 +632,7 @@ async def datasource_share(ctx: Context, datasource_name: str, workspace_name_or
638
632
  )
639
633
  )
640
634
  except Exception as e:
641
- click.echo(FeedbackManager.error_exception(error=str(e)))
642
- return
635
+ raise CLIDatasourceException(FeedbackManager.error_exception(error=str(e)))
643
636
 
644
637
 
645
638
  @datasource.command(name="unshare")
@@ -675,12 +668,10 @@ async def datasource_unshare(ctx: Context, datasource_name: str, workspace_name_
675
668
  current_workspace = next((workspace for workspace in workspaces if workspace["id"] == config["id"]), None)
676
669
 
677
670
  if not destination_workspace:
678
- click.echo(FeedbackManager.error_workspace(workspace=workspace_name_or_id))
679
- return
671
+ raise CLIDatasourceException(FeedbackManager.error_workspace(workspace=workspace_name_or_id))
680
672
 
681
673
  if not current_workspace:
682
- click.echo(FeedbackManager.error_not_authenticated())
683
- return
674
+ raise CLIDatasourceException(FeedbackManager.error_not_authenticated())
684
675
 
685
676
  if not user_token:
686
677
  user_token = ask_for_user_token("unshare a Data Source", ui_host)
@@ -706,8 +697,7 @@ async def datasource_unshare(ctx: Context, datasource_name: str, workspace_name_
706
697
  )
707
698
  )
708
699
  except Exception as e:
709
- click.echo(FeedbackManager.error_exception(error=str(e)))
710
- return
700
+ raise CLIDatasourceException(FeedbackManager.error_exception(error=str(e)))
711
701
 
712
702
 
713
703
  @datasource.command(name="sync")
@@ -11,6 +11,7 @@ from tinybird.feedback_manager import FeedbackManager
11
11
  from tinybird.client import DoesNotExistException, TinyB
12
12
 
13
13
  from tinybird.tb_cli_modules.cli import cli
14
+ from tinybird.tb_cli_modules.exceptions import CLIException
14
15
  from tinybird.tb_cli_modules.common import coro, echo_safe_humanfriendly_tables_format_smart_table
15
16
 
16
17
 
@@ -72,9 +73,9 @@ async def job_cancel(ctx: Context, job_id: str) -> None:
72
73
  try:
73
74
  result = await client.job_cancel(job_id)
74
75
  except DoesNotExistException:
75
- click.echo(FeedbackManager.error_job_does_not_exist(job_id=job_id))
76
+ raise CLIException(FeedbackManager.error_job_does_not_exist(job_id=job_id))
76
77
  except Exception as e:
77
- click.echo(FeedbackManager.error_exception(error=e))
78
+ raise CLIException(FeedbackManager.error_exception(error=e))
78
79
  else:
79
80
  current_job_status = result["status"]
80
81
  if current_job_status == "cancelling":
@@ -82,5 +83,5 @@ async def job_cancel(ctx: Context, job_id: str) -> None:
82
83
  elif current_job_status == "cancelled":
83
84
  click.echo(FeedbackManager.success_job_cancellation_cancelled(job_id=job_id))
84
85
  else:
85
- click.echo(FeedbackManager.error_job_cancelled_but_status_unknown(job_id=job_id))
86
+ raise CLIException(FeedbackManager.error_job_cancelled_but_status_unknown(job_id=job_id))
86
87
  click.echo("\n")
@@ -66,7 +66,9 @@ SQL >
66
66
  file.write(pipefile)
67
67
  click.echo(FeedbackManager.success_generated_pipe(file=f))
68
68
  else:
69
- click.echo(FeedbackManager.error_exception(error=f"File {f} already exists, use --force to override"))
69
+ raise CLIPipeException(
70
+ FeedbackManager.error_exception(error=f"File {f} already exists, use --force to override")
71
+ )
70
72
 
71
73
 
72
74
  @pipe.command(name="stats")
@@ -122,8 +124,7 @@ async def pipe_stats(ctx: click.Context, pipes: Tuple[str, ...], format_: str):
122
124
  res = await client.query(sql)
123
125
 
124
126
  if res and "error" in res:
125
- click.echo(FeedbackManager.error_exception(error=str(res["error"])))
126
- return
127
+ raise CLIPipeException(FeedbackManager.error_exception(error=str(res["error"])))
127
128
 
128
129
  columns = ["version", "name", "request count", "error count", "avg latency"]
129
130
  table_human_readable: List[Tuple] = []
@@ -204,7 +205,7 @@ async def pipe_ls(ctx: Context, match: str, format_: str):
204
205
  elif format_ == "json":
205
206
  click.echo(json.dumps({"pipes": table_machine_readable}, indent=2))
206
207
  else:
207
- click.echo(FeedbackManager.error_pipe_ls_type)
208
+ raise CLIPipeException(FeedbackManager.error_pipe_ls_type())
208
209
 
209
210
 
210
211
  @pipe.command(name="populate")
@@ -264,20 +265,6 @@ async def pipe_populate(
264
265
  await wait_job(cl, job_id, job_url, "Populating")
265
266
 
266
267
 
267
- @pipe.command(name="new", hidden=True)
268
- @click.argument("pipe_name")
269
- @click.argument("sql")
270
- @click.pass_context
271
- @coro
272
- async def pipe_create(ctx: click.Context, pipe_name: str, sql: str):
273
- """Create a new pipe"""
274
- click.echo(FeedbackManager.warning_deprecated(warning="This command is deprecated."))
275
- client: TinyB = ctx.ensure_object(dict)["client"]
276
- host = ctx.ensure_object(dict)["config"].get("host", DEFAULT_API_HOST)
277
- res = await client.pipe_create(pipe_name, sql)
278
- click.echo(FeedbackManager.success_created_pipe(pipe=pipe_name, node_id=res["nodes"][0]["id"], host=host))
279
-
280
-
281
268
  @pipe.command(name="append")
282
269
  @click.argument("pipe_name_or_uid")
283
270
  @click.argument("sql")
@@ -444,8 +431,7 @@ async def print_pipe(ctx: Context, pipe: str, query: str, format_: str):
444
431
  try:
445
432
  res = await client.pipe_data(pipe, format=req_format, sql=query, params=params)
446
433
  except Exception as e:
447
- click.echo(FeedbackManager.error_exception(error=str(e)))
448
- return
434
+ raise CLIPipeException(FeedbackManager.error_exception(error=str(e)))
449
435
 
450
436
  if not format_:
451
437
  stats = res["statistics"]
@@ -9,6 +9,7 @@ from tinybird.feedback_manager import FeedbackManager
9
9
  from typing import Iterable, List, Tuple
10
10
 
11
11
  from tinybird.tb_cli_modules.cli import cli
12
+ from tinybird.tb_cli_modules.exceptions import CLIException
12
13
  from tinybird.tb_cli_modules.common import coro, create_tb_client
13
14
  from tinybird.tb_cli_modules.tinyunit.tinyunit import (
14
15
  TestSummaryResults,
@@ -37,8 +38,7 @@ async def test_run(ctx: click.Context, file: Tuple[str, ...], verbose: bool, onl
37
38
  try:
38
39
  tb_client = create_tb_client(ctx)
39
40
  except Exception as e:
40
- click.echo(FeedbackManager.error_exception(error=e))
41
- raise e
41
+ raise CLIException(FeedbackManager.error_exception(error=e))
42
42
 
43
43
  file_list: Iterable[str] = file if len(file) > 0 else glob.glob("./tests/**/*.y*ml", recursive=True)
44
44
  file_list = [f for f in file_list if not f.endswith("regression.yaml")]
@@ -47,12 +47,12 @@ async def test_run(ctx: click.Context, file: Tuple[str, ...], verbose: bool, onl
47
47
  test_result = await run_test_file(tb_client, test_file)
48
48
  results.append(TestSummaryResults(filename=test_file, results=test_result))
49
49
  except Exception as e:
50
- click.echo(FeedbackManager.error_running_test(file=test_file))
51
50
  if verbose:
52
51
  click.echo(FeedbackManager.error_exception(error=e))
52
+ raise CLIException(FeedbackManager.error_running_test(file=test_file))
53
53
 
54
54
  if len(results) <= 0:
55
- click.echo(FeedbackManager.error_no_test_results())
55
+ click.echo(FeedbackManager.warning_no_test_results())
56
56
  else:
57
57
  test_run_summary(results, only_fail=only_fail, verbose_level=int(verbose))
58
58
 
@@ -150,7 +150,7 @@ def parse_file(file: str) -> Iterable[TestCase]:
150
150
  properties.get("pipe", None),
151
151
  )
152
152
  except Exception as e:
153
- click.echo(
153
+ raise CLIException(
154
154
  f"""Error: {FeedbackManager.error_exception(error=e)} reading file, check "{file}"->"{definition.get('name')}" """
155
155
  )
156
156
 
@@ -184,7 +184,7 @@ def generate_file(file: str, overwrite: bool = False) -> None:
184
184
  yaml.safe_dump(definitions, f)
185
185
  click.echo(FeedbackManager.success_generated_local_file(file=p))
186
186
  else:
187
- click.echo(FeedbackManager.error_file_already_exists(file=p))
187
+ raise CLIException(FeedbackManager.error_file_already_exists(file=p))
188
188
 
189
189
  return
190
190
 
@@ -26,7 +26,7 @@ from tinybird.tb_cli_modules.common import (
26
26
  echo_safe_humanfriendly_tables_format_smart_table,
27
27
  get_current_main_workspace,
28
28
  )
29
- from tinybird.tb_cli_modules.exceptions import CLIException, CLIWorkspaceException
29
+ from tinybird.tb_cli_modules.exceptions import CLIWorkspaceException
30
30
 
31
31
 
32
32
  @cli.group()
@@ -51,7 +51,7 @@ async def workspace_ls(ctx: Context) -> None:
51
51
 
52
52
  current_main_workspace = await get_current_main_workspace(client, config)
53
53
  if not current_main_workspace:
54
- raise CLIException(FeedbackManager.error_unable_to_identify_main_workspace())
54
+ raise CLIWorkspaceException(FeedbackManager.error_unable_to_identify_main_workspace())
55
55
 
56
56
  columns = ["name", "id", "role", "plan", "current"]
57
57
  table = []
@@ -114,7 +114,7 @@ async def clear_workspace(ctx: Context, yes: bool, dry_run: bool) -> None:
114
114
  for workspace in response["workspaces"]:
115
115
  if config["id"] == workspace["id"]:
116
116
  if workspace.get("is_branch"):
117
- click.echo(FeedbackManager.error_not_allowed_in_branch())
117
+ raise CLIWorkspaceException(FeedbackManager.error_not_allowed_in_branch())
118
118
  return
119
119
  else:
120
120
  click.echo(FeedbackManager.info_current_workspace())
@@ -154,10 +154,12 @@ async def clear_workspace(ctx: Context, yes: bool, dry_run: bool) -> None:
154
154
  except DoesNotExistException:
155
155
  click.echo(FeedbackManager.info_removing_datasource_not_found(datasource=ds_name))
156
156
  except CanNotBeDeletedException as e:
157
- click.echo(FeedbackManager.error_datasource_can_not_be_deleted(datasource=ds_name, error=e))
157
+ raise CLIWorkspaceException(
158
+ FeedbackManager.error_datasource_can_not_be_deleted(datasource=ds_name, error=e)
159
+ )
158
160
  except Exception as e:
159
161
  if "is a Shared Data Source" in str(e):
160
- click.echo(FeedbackManager.error_operation_can_not_be_performed(error=e))
162
+ raise CLIWorkspaceException(FeedbackManager.error_operation_can_not_be_performed(error=e))
161
163
  else:
162
164
  raise CLIWorkspaceException(FeedbackManager.error_exception(error=e))
163
165
  else:
@@ -182,8 +184,7 @@ async def create_workspace(
182
184
  ) -> None:
183
185
  if starter_kit:
184
186
  if not await is_valid_starterkit(ctx, starter_kit):
185
- click.echo(FeedbackManager.error_starterkit_name(starterkit_name=starter_kit))
186
- return
187
+ raise CLIWorkspaceException(FeedbackManager.error_starterkit_name(starterkit_name=starter_kit))
187
188
 
188
189
  if not user_token:
189
190
  _, _, ui_host = await get_config_and_hosts(ctx)
@@ -244,5 +245,4 @@ async def delete_workspace(
244
245
  await client.delete_workspace(workspace_to_delete["id"], confirm_hard_delete)
245
246
  click.echo(FeedbackManager.success_workspace_deleted(workspace_name=workspace_to_delete["name"]))
246
247
  except Exception as e:
247
- click.echo(FeedbackManager.error_exception(error=str(e)))
248
- return
248
+ raise CLIWorkspaceException(FeedbackManager.error_exception(error=str(e)))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tinybird-cli
3
- Version: 2.0.0.dev1
3
+ Version: 2.0.1.dev0
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://docs.tinybird.co/cli.html
6
6
  Author: Tinybird
@@ -19,10 +19,26 @@ Changelog
19
19
 
20
20
  ---------
21
21
 
22
+ 2.0.0
23
+ ************
24
+
25
+ Released new version 2.0.0 with all these changes:
26
+
27
+ - `Changed` All commands exit with 1 when there's an exception
28
+ - `Removed` Prefixes are not supported anymore ([context](https://www.tinybird.co/changelog/2023-10-09-versions), and [how to migrate](https://www.tinybird.co/docs/guides/staging-and-production-workspaces.html#migrating-from-prefixes))
29
+ - `Removed` `tb pipe create`
30
+
31
+
32
+ 2.0.0.dev2
33
+ ************
34
+
35
+ - `Changed` All commands exit with 1 when there's an exception
36
+ - `Removed` `tb pipe create`
37
+
22
38
  2.0.0.dev1
23
39
  ************
24
40
 
25
- - `Changed` Prefixes are not supported anymore.
41
+ - `Removed` Prefixes are not supported anymore
26
42
 
27
43
 
28
44
  1.3.0