tinybird 0.0.1.dev48__tar.gz → 0.0.1.dev50__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 (102) hide show
  1. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/PKG-INFO +1 -1
  2. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/prompts.py +0 -59
  3. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/__cli__.py +2 -2
  4. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/cli.py +0 -1
  5. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/build.py +24 -11
  6. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/cli.py +18 -63
  7. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/create.py +17 -12
  8. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/datafile/build.py +1 -2
  9. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/datafile/common.py +9 -18
  10. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/deployment.py +65 -14
  11. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/feedback_manager.py +1 -3
  12. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/local_common.py +6 -4
  13. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/login.py +1 -1
  14. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/mock.py +2 -2
  15. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/project.py +2 -2
  16. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/shell.py +50 -17
  17. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/test.py +9 -8
  18. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird.egg-info/PKG-INFO +1 -1
  19. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird.egg-info/SOURCES.txt +0 -1
  20. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird.egg-info/top_level.txt +1 -0
  21. tinybird-0.0.1.dev48/tinybird/tb/modules/update.py +0 -182
  22. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/setup.cfg +0 -0
  23. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/__cli__.py +0 -0
  24. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/ch_utils/constants.py +0 -0
  25. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/ch_utils/engine.py +0 -0
  26. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/check_pypi.py +0 -0
  27. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/client.py +0 -0
  28. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/config.py +0 -0
  29. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/connectors.py +0 -0
  30. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/context.py +0 -0
  31. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/datafile.py +0 -0
  32. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/datatypes.py +0 -0
  33. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/feedback_manager.py +0 -0
  34. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/git_settings.py +0 -0
  35. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/sql.py +0 -0
  36. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/sql_template.py +0 -0
  37. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/sql_template_fmt.py +0 -0
  38. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/sql_toolset.py +0 -0
  39. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/syncasync.py +0 -0
  40. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/auth.py +0 -0
  41. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/cicd.py +0 -0
  42. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/common.py +0 -0
  43. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/config.py +0 -0
  44. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/copy.py +0 -0
  45. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/datafile/build_common.py +0 -0
  46. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
  47. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/datafile/build_pipe.py +0 -0
  48. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/datafile/diff.py +0 -0
  49. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/datafile/exceptions.py +0 -0
  50. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/datafile/fixture.py +0 -0
  51. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/datafile/format_common.py +0 -0
  52. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
  53. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/datafile/format_pipe.py +0 -0
  54. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/datafile/parse_datasource.py +0 -0
  55. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/datafile/parse_pipe.py +0 -0
  56. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/datafile/pipe_checker.py +0 -0
  57. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/datafile/pull.py +0 -0
  58. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/datasource.py +0 -0
  59. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/endpoint.py +0 -0
  60. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/exceptions.py +0 -0
  61. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/fmt.py +0 -0
  62. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/job.py +0 -0
  63. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/llm.py +0 -0
  64. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/llm_utils.py +0 -0
  65. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/local.py +0 -0
  66. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/materialization.py +0 -0
  67. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/pipe.py +0 -0
  68. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/regions.py +0 -0
  69. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/table.py +0 -0
  70. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/tag.py +0 -0
  71. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/telemetry.py +0 -0
  72. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/tinyunit/tinyunit.py +0 -0
  73. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/tinyunit/tinyunit_lib.py +0 -0
  74. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/token.py +0 -0
  75. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/watch.py +0 -0
  76. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/workspace.py +0 -0
  77. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb/modules/workspace_members.py +0 -0
  78. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb_cli.py +0 -0
  79. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb_cli_modules/auth.py +0 -0
  80. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb_cli_modules/branch.py +0 -0
  81. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb_cli_modules/cicd.py +0 -0
  82. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb_cli_modules/cli.py +0 -0
  83. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb_cli_modules/common.py +0 -0
  84. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb_cli_modules/config.py +0 -0
  85. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb_cli_modules/connection.py +0 -0
  86. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb_cli_modules/datasource.py +0 -0
  87. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb_cli_modules/exceptions.py +0 -0
  88. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb_cli_modules/fmt.py +0 -0
  89. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb_cli_modules/job.py +0 -0
  90. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb_cli_modules/pipe.py +0 -0
  91. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb_cli_modules/regions.py +0 -0
  92. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb_cli_modules/tag.py +0 -0
  93. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb_cli_modules/telemetry.py +0 -0
  94. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb_cli_modules/test.py +0 -0
  95. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
  96. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
  97. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb_cli_modules/workspace.py +0 -0
  98. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  99. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird/tornado_template.py +0 -0
  100. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird.egg-info/dependency_links.txt +0 -0
  101. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird.egg-info/entry_points.txt +0 -0
  102. {tinybird-0.0.1.dev48 → tinybird-0.0.1.dev50}/tinybird.egg-info/requires.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tinybird
3
- Version: 0.0.1.dev48
3
+ Version: 0.0.1.dev50
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli/introduction.html
6
6
  Author: Tinybird
@@ -651,32 +651,6 @@ DATASOURCE sales_by_hour
651
651
  """
652
652
 
653
653
 
654
- def ask_prompt(existing_resources: str) -> str:
655
- return """
656
- You are a Tinybird expert. You will be given a prompt to ask questions about Tinybird resources.
657
- <existing_resources>{existing_resources}</existing_resources>
658
- {datasource_instructions}
659
- {pipe_instructions}
660
- {sql_instructions}
661
- {datasource_example}
662
- {pipe_example}
663
- {copy_pipe_instructions}
664
- {materialized_pipe_instructions}
665
-
666
- The previous instructions are explanations of how things work in Tinybird. Answer in natural language.
667
-
668
- """.format(
669
- existing_resources=existing_resources,
670
- datasource_instructions=datasource_instructions,
671
- datasource_example=datasource_example,
672
- pipe_instructions=pipe_instructions,
673
- pipe_example=pipe_example,
674
- sql_instructions=sql_instructions,
675
- copy_pipe_instructions=copy_pipe_instructions,
676
- materialized_pipe_instructions=materialized_pipe_instructions,
677
- )
678
-
679
-
680
654
  datasource_instructions = """
681
655
  <datasource_file_instructions>
682
656
  - Content cannot be empty.
@@ -790,39 +764,6 @@ sql_instructions = """
790
764
  )
791
765
 
792
766
 
793
- def update_prompt(existing_resources: str) -> str:
794
- return """
795
- You are a Tinybird expert. You will be given a prompt to update the existing Tinybird resources: datasources and/or pipes.
796
- You will return the resources that need to be updated.
797
- <existing_resources>{existing_resources}</existing_resources>
798
- {datasource_instructions}
799
- {pipe_instructions}
800
- {sql_instructions}
801
- {datasource_example}
802
- {pipe_example}
803
- {copy_pipe_instructions}
804
- {materialized_pipe_instructions}
805
- Use the following format to generate the response and do not wrap it in any other text, including the <response> tag.
806
- <response>
807
- <resource>
808
- <type>[datasource or pipe]</type>
809
- <name>[resource name here]</name>
810
- <content>[resource content here]</content>
811
- </resource>
812
- </response>
813
-
814
- """.format(
815
- existing_resources=existing_resources,
816
- datasource_instructions=datasource_instructions,
817
- pipe_instructions=pipe_instructions,
818
- sql_instructions=sql_instructions,
819
- datasource_example=datasource_example,
820
- pipe_example=pipe_example,
821
- copy_pipe_instructions=copy_pipe_instructions,
822
- materialized_pipe_instructions=materialized_pipe_instructions,
823
- )
824
-
825
-
826
767
  def rules_prompt(source: Optional[str] = None) -> str:
827
768
  base_command = source or "tb"
828
769
  return """
@@ -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.dev48'
8
- __revision__ = '6d638e9'
7
+ __version__ = '0.0.1.dev50'
8
+ __revision__ = 'ce37c39'
@@ -23,7 +23,6 @@ import tinybird.tb.modules.pipe
23
23
  import tinybird.tb.modules.tag
24
24
  import tinybird.tb.modules.test
25
25
  import tinybird.tb.modules.token
26
- import tinybird.tb.modules.update
27
26
  import tinybird.tb.modules.workspace
28
27
  import tinybird.tb.modules.workspace_members
29
28
 
@@ -1,6 +1,7 @@
1
1
  import asyncio
2
2
  import json
3
3
  import logging
4
+ import sys
4
5
  import threading
5
6
  import time
6
7
  from pathlib import Path
@@ -15,7 +16,6 @@ from tinybird.tb.modules.common import push_data
15
16
  from tinybird.tb.modules.datafile.build import folder_build
16
17
  from tinybird.tb.modules.datafile.fixture import build_fixture_name, get_fixture_dir
17
18
  from tinybird.tb.modules.feedback_manager import FeedbackManager
18
- from tinybird.tb.modules.local_common import get_tinybird_local_client
19
19
  from tinybird.tb.modules.project import Project
20
20
  from tinybird.tb.modules.shell import Shell, print_table_formatted
21
21
  from tinybird.tb.modules.watch import watch_project
@@ -29,7 +29,7 @@ def build(ctx: click.Context, watch: bool) -> None:
29
29
  Validate and build the project server side.
30
30
  """
31
31
  project: Project = ctx.ensure_object(dict)["project"]
32
- tb_client = asyncio.run(get_tinybird_local_client(str(project.path), build=True))
32
+ tb_client: TinyB = ctx.ensure_object(dict)["client"]
33
33
  click.echo(FeedbackManager.highlight_building_project())
34
34
  time_start = time.time()
35
35
 
@@ -40,15 +40,21 @@ def build(ctx: click.Context, watch: bool) -> None:
40
40
  build_project(project, tb_client, file_changed)
41
41
  try:
42
42
  if file_changed:
43
- asyncio.run(folder_build(project, filenames=[file_changed]))
44
- build_and_print_resource(tb_client, file_changed, diff)
43
+ asyncio.run(folder_build(project, tb_client, filenames=[file_changed]))
44
+ show_data(tb_client, file_changed, diff)
45
45
  except Exception:
46
46
  pass
47
47
 
48
- process()
49
- time_end = time.time()
50
- elapsed_time = time_end - time_start
51
- click.echo(FeedbackManager.success(message=f"\n✓ Build completed in {elapsed_time:.1f}s"))
48
+ try:
49
+ process()
50
+ except click.ClickException as e:
51
+ click.echo(e)
52
+ if not watch:
53
+ sys.exit(1)
54
+ else:
55
+ time_end = time.time()
56
+ elapsed_time = time_end - time_start
57
+ click.echo(FeedbackManager.success(message=f"\n✓ Build completed in {elapsed_time:.1f}s"))
52
58
 
53
59
  if watch:
54
60
  shell = Shell(project=project, tb_client=tb_client)
@@ -97,7 +103,7 @@ def build_project(project: Project, tb_client: TinyB, file_changed: Optional[str
97
103
  except Exception as e:
98
104
  logging.debug(e, exc_info=True)
99
105
  click.echo(FeedbackManager.error(message="Couldn't parse response from server"))
100
- return
106
+ sys.exit(1)
101
107
 
102
108
  logging.debug(json.dumps(result, indent=2))
103
109
 
@@ -137,9 +143,16 @@ def build_project(project: Project, tb_client: TinyB, file_changed: Optional[str
137
143
 
138
144
  if fixture_path.exists():
139
145
  append_fixture(tb_client, ds_name, str(fixture_path))
146
+
140
147
  except Exception:
141
148
  pass
142
149
 
150
+ feedback = result.get("feedback", [])
151
+ for f in feedback:
152
+ click.echo(
153
+ FeedbackManager.warning(message=f"△ {f.get('level')}: {f.get('resource')}: {f.get('message')}")
154
+ )
155
+
143
156
  elif build_result == "failed":
144
157
  build_errors = result.get("errors")
145
158
  error = True
@@ -157,7 +170,7 @@ def build_project(project: Project, tb_client: TinyB, file_changed: Optional[str
157
170
  for fd in fds:
158
171
  fd.close()
159
172
  if error:
160
- raise click.ClickException(FeedbackManager.error_build_failed())
173
+ raise click.ClickException(FeedbackManager.error(message="Error: Build failed"))
161
174
 
162
175
 
163
176
  def append_fixture(
@@ -198,7 +211,7 @@ def rebuild_fixture(project: Project, tb_client: TinyB, fixture: str) -> None:
198
211
  click.echo(FeedbackManager.error_exception(error=e))
199
212
 
200
213
 
201
- def build_and_print_resource(tb_client: TinyB, filename: str, diff: Optional[str] = None):
214
+ def show_data(tb_client: TinyB, filename: str, diff: Optional[str] = None):
202
215
  table_name = diff
203
216
  resource_path = Path(filename)
204
217
  resource_name = resource_path.stem
@@ -7,7 +7,6 @@ import json
7
7
  import logging
8
8
  import os
9
9
  from os import getcwd
10
- from pathlib import Path
11
10
  from typing import Any, Callable, Dict, List, Optional, Tuple, Union
12
11
 
13
12
  import click
@@ -20,7 +19,6 @@ from tinybird.client import (
20
19
  TinyB,
21
20
  )
22
21
  from tinybird.config import get_config
23
- from tinybird.prompts import ask_prompt
24
22
  from tinybird.tb import __cli__
25
23
  from tinybird.tb.modules.common import (
26
24
  CatchAuthExceptions,
@@ -37,7 +35,6 @@ from tinybird.tb.modules.datafile.build import build_graph
37
35
  from tinybird.tb.modules.datafile.diff import diff_command
38
36
  from tinybird.tb.modules.datafile.pull import folder_pull
39
37
  from tinybird.tb.modules.feedback_manager import FeedbackManager
40
- from tinybird.tb.modules.llm import LLM
41
38
  from tinybird.tb.modules.local_common import get_tinybird_local_client
42
39
  from tinybird.tb.modules.project import Project
43
40
 
@@ -124,19 +121,15 @@ async def cli(
124
121
 
125
122
  logging.debug("debug enabled")
126
123
 
127
- skip_client = ctx.invoked_subcommand in ["auth", "check", "login", "local", "build"]
128
- client = await create_ctx_client(config, prod, build, skip_client, project)
124
+ client = await create_ctx_client(ctx, config, prod, build, project)
129
125
 
130
126
  if client:
131
- if not build:
132
- target = config.get("name", "production") if prod else "Tinybird local"
133
- click.echo(FeedbackManager.gray(message=f"Running against {target}\n"))
134
127
  ctx.ensure_object(dict)["client"] = client
135
128
 
136
129
  ctx.ensure_object(dict)["project"] = project
137
130
 
138
131
 
139
- @cli.command()
132
+ @cli.command(hidden=True)
140
133
  @click.option(
141
134
  "--folder", default=None, type=click.Path(exists=True, file_okay=False), help="Folder where files will be placed"
142
135
  )
@@ -358,56 +351,6 @@ async def sql(
358
351
  click.echo(FeedbackManager.info_no_rows())
359
352
 
360
353
 
361
- @cli.command(hidden=True)
362
- @click.argument("prompt")
363
- @click.option("--folder", default=os.getcwd(), help="The folder to use for the project")
364
- @coro
365
- async def ask(prompt: str, folder: str) -> None:
366
- """Ask things about your data project."""
367
- try:
368
- config = CLIConfig.get_project_config(folder)
369
- user_token = config.get_user_token()
370
-
371
- if not user_token:
372
- raise CLIException("This action requires authentication. Run 'tb login' first.")
373
-
374
- datasource_paths = [
375
- Path(folder) / "datasources" / f
376
- for f in os.listdir(Path(folder) / "datasources")
377
- if f.endswith(".datasource")
378
- ]
379
- pipe_folders = ["endpoints", "pipes", "copies", "materializations", "sinks"]
380
- pipe_paths = [
381
- Path(folder) / pipe_folder / f
382
- for pipe_folder in pipe_folders
383
- if (Path(folder) / pipe_folder).exists()
384
- for f in os.listdir(Path(folder) / pipe_folder)
385
- if f.endswith(".pipe")
386
- ]
387
- resources_xml = "\n".join(
388
- [
389
- f"<resource><type>{resource_type}</type><name>{resource_name}</name><content>{resource_content}</content></resource>"
390
- for resource_type, resource_name, resource_content in [
391
- ("datasource", ds.stem, ds.read_text()) for ds in datasource_paths
392
- ]
393
- + [
394
- (
395
- "pipe",
396
- pipe.stem,
397
- pipe.read_text(),
398
- )
399
- for pipe in pipe_paths
400
- ]
401
- ]
402
- )
403
-
404
- client = config.get_client()
405
- llm = LLM(user_token=user_token, host=client.host)
406
- click.echo(llm.ask(system_prompt=ask_prompt(resources_xml), prompt=prompt))
407
- except Exception as e:
408
- raise CLIException(FeedbackManager.error_exception(error=e))
409
-
410
-
411
354
  def __patch_click_output():
412
355
  import re
413
356
 
@@ -445,11 +388,23 @@ def __unpatch_click_output():
445
388
  click.secho = __old_click_secho
446
389
 
447
390
 
448
- async def create_ctx_client(config: Dict[str, Any], prod: bool, build: bool, skip_client: bool, project: Project):
449
- if skip_client:
391
+ async def create_ctx_client(ctx: Context, config: Dict[str, Any], prod: bool, build: bool, project: Project):
392
+ commands_without_ctx_client = ["auth", "check", "login", "local"]
393
+ command = ctx.invoked_subcommand
394
+ if command in commands_without_ctx_client:
450
395
  return None
451
396
 
452
- if prod:
397
+ commands_always_prod = ["pull"]
398
+ commands_always_build = ["build", "test"]
399
+ commands_always_local = ["create", "mock"]
400
+ if (
401
+ (prod or command in commands_always_prod)
402
+ and command not in commands_always_build
403
+ and command not in commands_always_local
404
+ ):
405
+ click.echo(FeedbackManager.gray(message=f"Running against {config.get('name') or 'production'}"))
453
406
  return _get_tb_client(config.get("token", None), config["host"])
454
-
407
+ build = command in commands_always_build or build
408
+ if not build and command not in commands_always_local and command not in commands_always_build:
409
+ click.echo(FeedbackManager.gray(message="Running against Tinybird local\n"))
455
410
  return await get_tinybird_local_client(str(project.path), build=build)
@@ -17,7 +17,6 @@ from tinybird.tb.modules.exceptions import CLIException
17
17
  from tinybird.tb.modules.feedback_manager import FeedbackManager
18
18
  from tinybird.tb.modules.llm import LLM
19
19
  from tinybird.tb.modules.llm_utils import extract_xml, parse_xml
20
- from tinybird.tb.modules.local_common import get_tinybird_local_client
21
20
  from tinybird.tb.modules.project import Project
22
21
 
23
22
 
@@ -41,8 +40,8 @@ from tinybird.tb.modules.project import Project
41
40
  help="Folder where datafiles will be placed",
42
41
  )
43
42
  @click.option("--rows", type=int, default=10, help="Number of events to send")
44
- @click.option("--cursor", default=False, is_flag=True, help="Create .cursorrules file with Tinybird rules")
45
43
  @click.option("--source", type=str, default="tb", help="Source of the command")
44
+ @click.option("--skip", is_flag=True, default=False, help="Skip following up on the generated data")
46
45
  @click.pass_context
47
46
  @coro
48
47
  async def create(
@@ -51,11 +50,12 @@ async def create(
51
50
  prompt: Optional[str],
52
51
  folder: Optional[str],
53
52
  rows: int,
54
- cursor: bool,
55
53
  source: str,
54
+ skip: bool,
56
55
  ) -> None:
57
56
  """Initialize a new project."""
58
57
  project: Project = ctx.ensure_object(dict)["project"]
58
+ local_client: TinyB = ctx.ensure_object(dict)["client"]
59
59
  folder = folder or getcwd()
60
60
  folder_path = Path(folder)
61
61
  if not folder_path.exists():
@@ -78,7 +78,6 @@ async def create(
78
78
  )
79
79
  )
80
80
  return
81
- local_client = await get_tinybird_local_client(folder)
82
81
 
83
82
  if not validate_project_structure(folder):
84
83
  click.echo(FeedbackManager.highlight(message="\n» Creating new project structure..."))
@@ -88,7 +87,7 @@ async def create(
88
87
  result = ""
89
88
  if data or prompt:
90
89
  click.echo(FeedbackManager.highlight(message="\n» Creating resources..."))
91
- result = await create_resources(local_client, tb_client, user_token, data, prompt, folder)
90
+ result = await create_resources(local_client, tb_client, user_token, data, prompt, folder, skip)
92
91
  click.echo(FeedbackManager.success(message="✓ Done!\n"))
93
92
 
94
93
  if not already_has_cicd(folder):
@@ -97,6 +96,11 @@ async def create(
97
96
  await init_cicd(data_project_dir=os.path.relpath(folder))
98
97
  click.echo(FeedbackManager.success(message="✓ Done!\n"))
99
98
 
99
+ if not already_has_cursor_rules(folder):
100
+ click.echo(FeedbackManager.highlight(message="\n» Creating .cursorrules..."))
101
+ create_rules(folder, source, "cursor")
102
+ click.echo(FeedbackManager.success(message="✓ Done!\n"))
103
+
100
104
  if should_generate_fixtures(result):
101
105
  click.echo(FeedbackManager.highlight(message="\n» Generating fixtures..."))
102
106
 
@@ -130,12 +134,6 @@ async def create(
130
134
  if data:
131
135
  persist_fixture(fixture_name, data, folder)
132
136
  click.echo(FeedbackManager.info(message=f"✓ /fixtures/{datasource_name}"))
133
-
134
- if cursor:
135
- click.echo(FeedbackManager.highlight(message="\n» Creating .cursorrules..."))
136
- create_rules(folder, source, "cursor")
137
- click.echo(FeedbackManager.success(message="✓ Done!\n"))
138
-
139
137
  except Exception as e:
140
138
  click.echo(FeedbackManager.error(message=f"Error: {str(e)}"))
141
139
 
@@ -158,6 +156,11 @@ def already_has_cicd(folder: str) -> bool:
158
156
  return any((Path(folder) / path).exists() for path in ci_cd_paths)
159
157
 
160
158
 
159
+ def already_has_cursor_rules(folder: str) -> bool:
160
+ cursor_rules_paths = (".cursorrules", ".windsurfrules")
161
+ return any((Path(folder) / path).exists() for path in cursor_rules_paths)
162
+
163
+
161
164
  def create_project_structure(folder: str):
162
165
  folder_path = Path(folder)
163
166
  for x in PROJECT_PATHS:
@@ -176,6 +179,7 @@ async def create_resources(
176
179
  data: Optional[str],
177
180
  prompt: Optional[str],
178
181
  folder: str,
182
+ skip: bool,
179
183
  ):
180
184
  result = ""
181
185
  folder_path = Path(folder)
@@ -285,7 +289,8 @@ TYPE ENDPOINT
285
289
  content = pipe["content"].replace("```", "")
286
290
  pipe_path = generate_pipe_file(pipe["name"], content, folder)
287
291
  generated_paths.append(pipe_path)
288
-
292
+ if skip:
293
+ break
289
294
  iterations += 1
290
295
 
291
296
  if iterations == 10:
@@ -42,12 +42,12 @@ from tinybird.tb.modules.datafile.exceptions import AlreadyExistsException, Incl
42
42
  from tinybird.tb.modules.datafile.parse_datasource import parse_datasource
43
43
  from tinybird.tb.modules.datafile.parse_pipe import parse_pipe
44
44
  from tinybird.tb.modules.feedback_manager import FeedbackManager
45
- from tinybird.tb.modules.local_common import get_tinybird_local_client
46
45
  from tinybird.tb.modules.project import Project
47
46
 
48
47
 
49
48
  async def folder_build(
50
49
  project: Project,
50
+ tb_client: TinyB,
51
51
  filenames: Optional[List[str]] = None,
52
52
  is_internal: bool = False,
53
53
  is_vendor: bool = False,
@@ -67,7 +67,6 @@ async def folder_build(
67
67
  fork = False
68
68
  release_created = False
69
69
  folder = str(project.path)
70
- tb_client = await get_tinybird_local_client(folder)
71
70
  datasources: List[Dict[str, Any]] = await tb_client.datasources()
72
71
  pipes: List[Dict[str, Any]] = await tb_client.pipes(dependencies=True)
73
72
 
@@ -11,7 +11,7 @@ import shlex
11
11
  import string
12
12
  import textwrap
13
13
  import traceback
14
- from collections import Counter, namedtuple
14
+ from collections import namedtuple
15
15
  from dataclasses import dataclass
16
16
  from enum import Enum
17
17
  from io import StringIO
@@ -207,28 +207,19 @@ class Datafile:
207
207
  # [x] SQL in all nodes
208
208
  # [x] Materialized nodes have target datasource
209
209
  # [x] Only one materialized node
210
- # [ ] Only one node of any specific type
210
+ # [x] Only one node of any specific type
211
211
  # [ ] ...
212
- repeated_node_names = [
213
- name for name, count in filter(lambda x: x[1] > 1, Counter(n["name"] for n in self.nodes).items())
214
- ]
215
- if repeated_node_names:
216
- raise DatafileValidationError(
217
- f"Pipe node names must be unique. These names are repeated: {repeated_node_names}"
218
- )
219
212
  for node in self.nodes:
220
213
  if "sql" not in node:
221
214
  raise DatafileValidationError(f"SQL missing for node {repr(node['name'])}")
222
- materialized_nodes_count = 0
215
+ non_standard_nodes_count = 0
223
216
  for node in self.nodes:
224
- if node.get("type", "").lower() == "materialized":
225
- materialized_nodes_count += 1
226
- if materialized_nodes_count > 1:
227
- raise DatafileValidationError("Multiple materialized nodes in pipe. There can only be one")
228
- if "datasource" not in node:
229
- raise DatafileValidationError(
230
- f"Materialized node {repr(node['name'])} missing target datasource"
231
- )
217
+ if node.get("type", "").lower() not in {PipeNodeTypes.STANDARD, ""}:
218
+ non_standard_nodes_count += 1
219
+ if non_standard_nodes_count > 1:
220
+ raise DatafileValidationError("Multiple non-standard nodes in pipe. There can only be one")
221
+ if node.get("type", "").lower() == PipeNodeTypes.MATERIALIZED and "datasource" not in node:
222
+ raise DatafileValidationError(f"Materialized node {repr(node['name'])} missing target datasource")
232
223
  elif self.kind == DatafileKind.datasource:
233
224
  # TODO(eclbg):
234
225
  # [x] Just one node
@@ -1,5 +1,6 @@
1
1
  import json
2
2
  import logging
3
+ import sys
3
4
  import time
4
5
  from datetime import datetime
5
6
  from pathlib import Path
@@ -14,7 +15,7 @@ from tinybird.tb.modules.feedback_manager import FeedbackManager
14
15
  from tinybird.tb.modules.project import Project
15
16
 
16
17
 
17
- def promote_deployment(host: Optional[str], headers: dict) -> None:
18
+ def promote_deployment(host: Optional[str], headers: dict, wait: bool) -> None:
18
19
  TINYBIRD_API_URL = f"{host}/v1/deployments"
19
20
  r = requests.get(TINYBIRD_API_URL, headers=headers)
20
21
  result = r.json()
@@ -23,11 +24,11 @@ def promote_deployment(host: Optional[str], headers: dict) -> None:
23
24
  deployments = result.get("deployments")
24
25
  if not deployments:
25
26
  click.echo(FeedbackManager.error(message="No deployments found"))
26
- return
27
+ sys.exit(1)
27
28
 
28
29
  if len(deployments) < 2:
29
30
  click.echo(FeedbackManager.error(message="Only one deployment found"))
30
- return
31
+ sys.exit(1)
31
32
 
32
33
  last_deployment, candidate_deployment = deployments[0], deployments[1]
33
34
 
@@ -36,7 +37,7 @@ def promote_deployment(host: Optional[str], headers: dict) -> None:
36
37
  deploy_errors = candidate_deployment.get("errors", [])
37
38
  for deploy_error in deploy_errors:
38
39
  click.echo(FeedbackManager.error(message=f"* {deploy_error}"))
39
- return
40
+ sys.exit(1)
40
41
 
41
42
  if candidate_deployment.get("live"):
42
43
  click.echo(FeedbackManager.error(message="Candidate deployment is already live"))
@@ -55,10 +56,23 @@ def promote_deployment(host: Optional[str], headers: dict) -> None:
55
56
  result = r.json()
56
57
  logging.debug(json.dumps(result, indent=2))
57
58
 
59
+ if wait:
60
+ while True:
61
+ TINYBIRD_API_URL = f"{host}/v1/deployments/{last_deployment.get('id')}"
62
+ r = requests.get(TINYBIRD_API_URL, headers=headers)
63
+ result = r.json()
64
+ logging.debug(json.dumps(result, indent=2))
65
+
66
+ last_deployment = result.get("deployment")
67
+ if last_deployment.get("status") == "deleted":
68
+ break
69
+
70
+ time.sleep(5)
71
+
58
72
  click.echo(FeedbackManager.success(message="Deployment promoted successfully"))
59
73
 
60
74
 
61
- def rollback_deployment(host: Optional[str], headers: dict) -> None:
75
+ def rollback_deployment(host: Optional[str], headers: dict, wait: bool) -> None:
62
76
  TINYBIRD_API_URL = f"{host}/v1/deployments"
63
77
  r = requests.get(TINYBIRD_API_URL, headers=headers)
64
78
  result = r.json()
@@ -99,6 +113,18 @@ def rollback_deployment(host: Optional[str], headers: dict) -> None:
99
113
  result = r.json()
100
114
  logging.debug(json.dumps(result, indent=2))
101
115
 
116
+ if wait:
117
+ while True:
118
+ TINYBIRD_API_URL = f"{host}/v1/deployments/{current_deployment.get('id')}"
119
+ r = requests.get(TINYBIRD_API_URL, headers=headers)
120
+ result = r.json()
121
+ logging.debug(json.dumps(result, indent=2))
122
+
123
+ current_deployment = result.get("deployment")
124
+ if current_deployment.get("status") == "deleted":
125
+ break
126
+ time.sleep(5)
127
+
102
128
  click.echo(FeedbackManager.success(message="Deployment rolled back successfully"))
103
129
 
104
130
 
@@ -165,7 +191,13 @@ def deployment_ls(ctx: click.Context) -> None:
165
191
 
166
192
  @deployment_group.command(name="promote")
167
193
  @click.pass_context
168
- def deployment_promote(ctx: click.Context) -> None:
194
+ @click.option(
195
+ "--wait/--no-wait",
196
+ is_flag=True,
197
+ default=False,
198
+ help="Wait for deploy to finish. Disabled by default.",
199
+ )
200
+ def deployment_promote(ctx: click.Context, wait: bool) -> None:
169
201
  """
170
202
  Promote last deploy to ready and remove old one.
171
203
  """
@@ -174,12 +206,18 @@ def deployment_promote(ctx: click.Context) -> None:
174
206
  TINYBIRD_API_KEY = client.token
175
207
  HEADERS = {"Authorization": f"Bearer {TINYBIRD_API_KEY}"}
176
208
 
177
- promote_deployment(client.host, HEADERS)
209
+ promote_deployment(client.host, HEADERS, wait=wait)
178
210
 
179
211
 
180
212
  @deployment_group.command(name="rollback")
181
213
  @click.pass_context
182
- def deployment_rollback(ctx: click.Context) -> None:
214
+ @click.option(
215
+ "--wait/--no-wait",
216
+ is_flag=True,
217
+ default=False,
218
+ help="Wait for deploy to finish. Disabled by default.",
219
+ )
220
+ def deployment_rollback(ctx: click.Context, wait: bool) -> None:
183
221
  """
184
222
  Rollback to the previous deployment.
185
223
  """
@@ -188,7 +226,7 @@ def deployment_rollback(ctx: click.Context) -> None:
188
226
  TINYBIRD_API_KEY = client.token
189
227
  HEADERS = {"Authorization": f"Bearer {TINYBIRD_API_KEY}"}
190
228
 
191
- rollback_deployment(client.host, HEADERS)
229
+ rollback_deployment(client.host, HEADERS, wait=wait)
192
230
 
193
231
 
194
232
  @cli.command(name="deploy", hidden=True)
@@ -248,6 +286,12 @@ def create_deployment(ctx: click.Context, wait: bool, auto: bool) -> None:
248
286
  if deploy_result == "success":
249
287
  click.echo(FeedbackManager.success(message="Deployment submitted successfully"))
250
288
  deployment = result.get("deployment")
289
+ feedback = deployment.get("feedback", [])
290
+ for f in feedback:
291
+ click.echo(
292
+ FeedbackManager.warning(message=f"△ {f.get('level')}: {f.get('resource')}: {f.get('message')}")
293
+ )
294
+
251
295
  elif deploy_result == "failed":
252
296
  click.echo(FeedbackManager.error(message="Deployment failed"))
253
297
  deploy_errors = result.get("errors")
@@ -264,9 +308,11 @@ def create_deployment(ctx: click.Context, wait: bool, auto: bool) -> None:
264
308
  for fd in fds:
265
309
  fd.close()
266
310
 
311
+ if not deployment:
312
+ sys.exit(1)
313
+
267
314
  if deployment and wait:
268
- while deployment.get("status") != "data_ready":
269
- time.sleep(5)
315
+ while True:
270
316
  TINYBIRD_API_URL = f"{client.host}/v1/deployments/{deployment.get('id')}"
271
317
  r = requests.get(TINYBIRD_API_URL, headers=HEADERS)
272
318
  result = r.json()
@@ -281,10 +327,15 @@ def create_deployment(ctx: click.Context, wait: bool, auto: bool) -> None:
281
327
 
282
328
  if auto:
283
329
  click.echo(FeedbackManager.error(message="Rolling back deployment"))
284
- rollback_deployment(client.host, HEADERS)
285
- return
330
+ rollback_deployment(client.host, HEADERS, wait=wait)
331
+ sys.exit(1)
332
+
333
+ if deployment.get("status") == "data_ready":
334
+ break
335
+
336
+ time.sleep(5)
286
337
 
287
338
  click.echo(FeedbackManager.success(message="Deployment is ready"))
288
339
 
289
340
  if auto:
290
- promote_deployment(client.host, HEADERS)
341
+ promote_deployment(client.host, HEADERS, wait=wait)
@@ -73,9 +73,7 @@ class FeedbackManager:
73
73
  error_exception = error_exception("{error}")
74
74
  simple_error_exception = simple_error_message("{error}")
75
75
  error_exception_trace = error_message("{error}\n** Trace:\n{trace}")
76
- error_notoken = error_message(
77
- "No auth token provided. Run 'tb auth' to configure them or re-run the command passing the --token param (example: tb --token <the_token> datasource ls)."
78
- )
76
+ error_notoken = error_message("This action requires authentication. Run 'tb login' first")
79
77
  error_auth_config = error_message("{config_file} does not exist")
80
78
  error_file_config = error_message("{config_file} can't be written, check write permissions on this folder")
81
79
  error_load_file_config = error_message("{config_file} can't be loaded, remove it and run the command again")