tinybird 4.6.6.dev0__py3-none-any.whl → 4.6.7.dev0__py3-none-any.whl
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.
- tinybird/tb/__cli__.py +2 -2
- tinybird/tb/modules/build.py +1 -46
- tinybird/tb/modules/config.py +0 -3
- tinybird/tb/modules/datafile/playground.py +1 -329
- tinybird/tb/modules/local_common.py +124 -22
- tinybird/tb/modules/watch.py +1 -100
- tinybird/tb_cli_modules/config.py +0 -6
- {tinybird-4.6.6.dev0.dist-info → tinybird-4.6.7.dev0.dist-info}/METADATA +8 -1
- {tinybird-4.6.6.dev0.dist-info → tinybird-4.6.7.dev0.dist-info}/RECORD +12 -12
- {tinybird-4.6.6.dev0.dist-info → tinybird-4.6.7.dev0.dist-info}/WHEEL +0 -0
- {tinybird-4.6.6.dev0.dist-info → tinybird-4.6.7.dev0.dist-info}/entry_points.txt +0 -0
- {tinybird-4.6.6.dev0.dist-info → tinybird-4.6.7.dev0.dist-info}/top_level.txt +0 -0
tinybird/tb/__cli__.py
CHANGED
|
@@ -4,5 +4,5 @@ __description__ = 'Tinybird Command Line Tool'
|
|
|
4
4
|
__url__ = 'https://www.tinybird.co/docs/forward/commands'
|
|
5
5
|
__author__ = 'Tinybird'
|
|
6
6
|
__author_email__ = 'support@tinybird.co'
|
|
7
|
-
__version__ = '4.6.
|
|
8
|
-
__revision__ = '
|
|
7
|
+
__version__ = '4.6.7.dev0'
|
|
8
|
+
__revision__ = 'd3cbd87'
|
tinybird/tb/modules/build.py
CHANGED
|
@@ -1,22 +1,15 @@
|
|
|
1
|
-
from copy import deepcopy
|
|
2
1
|
from functools import partial
|
|
3
2
|
from pathlib import Path
|
|
4
|
-
from typing import Any, Callable, Dict,
|
|
5
|
-
from urllib.parse import urlencode
|
|
3
|
+
from typing import Any, Callable, Dict, Optional
|
|
6
4
|
|
|
7
5
|
import click
|
|
8
6
|
|
|
9
|
-
from tinybird.datafile.exceptions import ParseException
|
|
10
|
-
from tinybird.datafile.parse_datasource import parse_datasource
|
|
11
|
-
from tinybird.datafile.parse_pipe import parse_pipe
|
|
12
7
|
from tinybird.tb.client import TinyB
|
|
13
8
|
from tinybird.tb.config import CLOUD_HOSTS
|
|
14
9
|
from tinybird.tb.modules.build_common import process
|
|
15
10
|
from tinybird.tb.modules.cli import cli, get_current_git_branch
|
|
16
|
-
from tinybird.tb.modules.config import CLIConfig
|
|
17
11
|
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
18
12
|
from tinybird.tb.modules.project import Project
|
|
19
|
-
from tinybird.tb.modules.query_output import print_table_formatted
|
|
20
13
|
from tinybird.tb.modules.watch import watch_project
|
|
21
14
|
|
|
22
15
|
|
|
@@ -182,41 +175,3 @@ def is_endpoint(f: Path) -> bool:
|
|
|
182
175
|
|
|
183
176
|
def is_pipe(f: Path) -> bool:
|
|
184
177
|
return f.suffix == ".pipe" and not is_vendor(f)
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
def check_filenames(filenames: List[str]):
|
|
188
|
-
parser_matrix = {".pipe": parse_pipe, ".datasource": parse_datasource}
|
|
189
|
-
incl_suffix = ".incl"
|
|
190
|
-
|
|
191
|
-
for filename in filenames:
|
|
192
|
-
file_suffix = Path(filename).suffix
|
|
193
|
-
if file_suffix == incl_suffix:
|
|
194
|
-
continue
|
|
195
|
-
|
|
196
|
-
parser = parser_matrix.get(file_suffix)
|
|
197
|
-
if not parser:
|
|
198
|
-
raise ParseException(FeedbackManager.error_unsupported_datafile(extension=file_suffix))
|
|
199
|
-
|
|
200
|
-
parser(filename)
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
def build_and_print_resource(config: CLIConfig, tb_client: TinyB, filename: str):
|
|
204
|
-
resource_path = Path(filename)
|
|
205
|
-
name = resource_path.stem
|
|
206
|
-
playground_name = name if filename.endswith(".pipe") else None
|
|
207
|
-
user_client = deepcopy(tb_client)
|
|
208
|
-
user_client.token = config.get_user_token() or ""
|
|
209
|
-
cli_params = {}
|
|
210
|
-
cli_params["workspace_id"] = config.get("id", None)
|
|
211
|
-
data = user_client._req(f"/v0/playgrounds?{urlencode(cli_params)}")
|
|
212
|
-
playgrounds = data["playgrounds"]
|
|
213
|
-
playground = next((p for p in playgrounds if p["name"] == (f"{playground_name}" + "__tb__playground")), None)
|
|
214
|
-
if not playground:
|
|
215
|
-
return
|
|
216
|
-
playground_id = playground["id"]
|
|
217
|
-
last_node = playground["nodes"][-1]
|
|
218
|
-
if not last_node:
|
|
219
|
-
return
|
|
220
|
-
node_sql = last_node["sql"]
|
|
221
|
-
res = tb_client.query(f"{node_sql} FORMAT JSON", playground=playground_id)
|
|
222
|
-
print_table_formatted(res, name)
|
tinybird/tb/modules/config.py
CHANGED
|
@@ -197,9 +197,6 @@ class CLIConfig:
|
|
|
197
197
|
netloc: str = url_info.netloc.decode() if isinstance(url_info.netloc, bytes) else url_info.netloc
|
|
198
198
|
self["host"] = f"{scheme}://{netloc}"
|
|
199
199
|
|
|
200
|
-
def set_cwd(self, cwd: str) -> None:
|
|
201
|
-
self["cwd"] = cwd
|
|
202
|
-
|
|
203
200
|
def get_host(self, use_defaults_if_needed: bool = False) -> Optional[str]:
|
|
204
201
|
result: Optional[str] = self.get("host", None)
|
|
205
202
|
if result:
|
|
@@ -9,7 +9,6 @@ from pathlib import Path
|
|
|
9
9
|
from typing import Any, Dict, Iterable, List, Optional, Set, Tuple
|
|
10
10
|
|
|
11
11
|
import click
|
|
12
|
-
from toposort import toposort
|
|
13
12
|
|
|
14
13
|
from tinybird.datafile.common import (
|
|
15
14
|
DEFAULT_CRON_PERIOD,
|
|
@@ -27,7 +26,7 @@ from tinybird.datafile.common import (
|
|
|
27
26
|
get_project_filenames,
|
|
28
27
|
pp,
|
|
29
28
|
)
|
|
30
|
-
from tinybird.datafile.exceptions import
|
|
29
|
+
from tinybird.datafile.exceptions import IncludeFileNotFoundException
|
|
31
30
|
from tinybird.datafile.parse_datasource import parse_datasource
|
|
32
31
|
from tinybird.datafile.parse_pipe import parse_pipe
|
|
33
32
|
from tinybird.sql import parse_table_structure, schema_to_sql_columns
|
|
@@ -35,339 +34,12 @@ from tinybird.sql_template import get_used_tables_in_template, render_sql_templa
|
|
|
35
34
|
from tinybird.tb.client import TinyB
|
|
36
35
|
from tinybird.tb.modules.common import get_ca_pem_content
|
|
37
36
|
from tinybird.tb.modules.config import CLIConfig
|
|
38
|
-
from tinybird.tb.modules.datafile.build_datasource import is_datasource
|
|
39
37
|
from tinybird.tb.modules.datafile.build_pipe import (
|
|
40
38
|
get_target_materialized_data_source_name,
|
|
41
|
-
is_endpoint,
|
|
42
39
|
is_endpoint_with_no_dependencies,
|
|
43
|
-
is_materialized,
|
|
44
40
|
new_pipe,
|
|
45
41
|
)
|
|
46
42
|
from tinybird.tb.modules.feedback_manager import FeedbackManager, get_cli_name
|
|
47
|
-
from tinybird.tb.modules.project import Project
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def folder_playground(
|
|
51
|
-
project: Project,
|
|
52
|
-
config: CLIConfig,
|
|
53
|
-
tb_client: TinyB,
|
|
54
|
-
filenames: Optional[List[str]] = None,
|
|
55
|
-
is_internal: bool = False,
|
|
56
|
-
current_ws: Optional[Dict[str, Any]] = None,
|
|
57
|
-
local_ws: Optional[Dict[str, Any]] = None,
|
|
58
|
-
):
|
|
59
|
-
build = True
|
|
60
|
-
dry_run = False
|
|
61
|
-
force = True
|
|
62
|
-
only_changes = True
|
|
63
|
-
debug = False
|
|
64
|
-
run_tests = False
|
|
65
|
-
verbose = False
|
|
66
|
-
raise_on_exists = False
|
|
67
|
-
fork_downstream = True
|
|
68
|
-
fork = False
|
|
69
|
-
release_created = False
|
|
70
|
-
folder = str(project.path)
|
|
71
|
-
datasources: List[Dict[str, Any]] = tb_client.datasources()
|
|
72
|
-
pipes: List[Dict[str, Any]] = tb_client.pipes(dependencies=True)
|
|
73
|
-
build = True
|
|
74
|
-
dry_run = False
|
|
75
|
-
force = True
|
|
76
|
-
only_changes = True
|
|
77
|
-
debug = False
|
|
78
|
-
check = True
|
|
79
|
-
populate = False
|
|
80
|
-
populate_subset = None
|
|
81
|
-
populate_condition = None
|
|
82
|
-
tests_to_run = 0
|
|
83
|
-
override_datasource = False
|
|
84
|
-
skip_confirmation = True
|
|
85
|
-
wait = False
|
|
86
|
-
unlink_on_populate_error = False
|
|
87
|
-
only_response_times = False
|
|
88
|
-
run_tests = False
|
|
89
|
-
verbose = False
|
|
90
|
-
as_standard = False
|
|
91
|
-
raise_on_exists = False
|
|
92
|
-
fork_downstream = True
|
|
93
|
-
fork = False
|
|
94
|
-
release_created = False
|
|
95
|
-
tests_relative_change = 0.01
|
|
96
|
-
tests_sample_by_params = 0
|
|
97
|
-
tests_filter_by = None
|
|
98
|
-
tests_failfast = False
|
|
99
|
-
tests_ignore_order = False
|
|
100
|
-
tests_validate_processed_bytes = False
|
|
101
|
-
tests_check_requests_from_branch = False
|
|
102
|
-
user_token = None
|
|
103
|
-
ignore_sql_errors = False
|
|
104
|
-
is_vendor = False
|
|
105
|
-
current_ws = current_ws or local_ws
|
|
106
|
-
|
|
107
|
-
existing_resources: List[str] = [x["name"] for x in datasources] + [x["name"] for x in pipes]
|
|
108
|
-
remote_resource_names = [get_remote_resource_name_without_version(x) for x in existing_resources]
|
|
109
|
-
|
|
110
|
-
if not filenames:
|
|
111
|
-
filenames = get_project_filenames(folder)
|
|
112
|
-
|
|
113
|
-
# build graph to get new versions for all the files involved in the query
|
|
114
|
-
# dependencies need to be processed always to get the versions
|
|
115
|
-
dependencies_graph = build_graph(
|
|
116
|
-
filenames,
|
|
117
|
-
tb_client,
|
|
118
|
-
dir_path=folder,
|
|
119
|
-
process_dependencies=True,
|
|
120
|
-
skip_connectors=True,
|
|
121
|
-
vendor_paths=[],
|
|
122
|
-
current_ws=current_ws,
|
|
123
|
-
only_changes=only_changes,
|
|
124
|
-
fork_downstream=fork_downstream,
|
|
125
|
-
is_internal=is_internal,
|
|
126
|
-
build=build,
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
if debug:
|
|
130
|
-
pp.pprint(dependencies_graph.to_run)
|
|
131
|
-
|
|
132
|
-
def should_push_file(
|
|
133
|
-
name: str,
|
|
134
|
-
remote_resource_names: List[str],
|
|
135
|
-
force: bool,
|
|
136
|
-
run_tests: bool,
|
|
137
|
-
) -> bool:
|
|
138
|
-
"""
|
|
139
|
-
Function to know if we need to run a file or not
|
|
140
|
-
"""
|
|
141
|
-
if name not in remote_resource_names:
|
|
142
|
-
return True
|
|
143
|
-
# When we need to try to push a file when it doesn't exist and the version is different that the existing one
|
|
144
|
-
resource_full_name = name
|
|
145
|
-
if resource_full_name not in existing_resources:
|
|
146
|
-
return True
|
|
147
|
-
return force or run_tests
|
|
148
|
-
|
|
149
|
-
def push(
|
|
150
|
-
name: str,
|
|
151
|
-
to_run: Dict[str, Dict[str, Any]],
|
|
152
|
-
dry_run: bool,
|
|
153
|
-
fork_downstream: Optional[bool] = False,
|
|
154
|
-
fork: Optional[bool] = False,
|
|
155
|
-
):
|
|
156
|
-
if name in to_run:
|
|
157
|
-
resource = to_run[name]["resource"]
|
|
158
|
-
if resource == "datasources":
|
|
159
|
-
return
|
|
160
|
-
if not dry_run:
|
|
161
|
-
if should_push_file(name, remote_resource_names, force, run_tests):
|
|
162
|
-
click.echo(FeedbackManager.info_processing_new_resource(name=name, version=""))
|
|
163
|
-
try:
|
|
164
|
-
exec_file(
|
|
165
|
-
to_run[name],
|
|
166
|
-
config,
|
|
167
|
-
tb_client,
|
|
168
|
-
force,
|
|
169
|
-
check,
|
|
170
|
-
debug and verbose,
|
|
171
|
-
populate,
|
|
172
|
-
populate_subset,
|
|
173
|
-
populate_condition,
|
|
174
|
-
unlink_on_populate_error,
|
|
175
|
-
wait,
|
|
176
|
-
user_token,
|
|
177
|
-
override_datasource,
|
|
178
|
-
ignore_sql_errors,
|
|
179
|
-
skip_confirmation,
|
|
180
|
-
only_response_times,
|
|
181
|
-
run_tests,
|
|
182
|
-
as_standard,
|
|
183
|
-
tests_to_run,
|
|
184
|
-
tests_relative_change,
|
|
185
|
-
tests_sample_by_params,
|
|
186
|
-
tests_filter_by,
|
|
187
|
-
tests_failfast,
|
|
188
|
-
tests_ignore_order,
|
|
189
|
-
tests_validate_processed_bytes,
|
|
190
|
-
tests_check_requests_from_branch,
|
|
191
|
-
current_ws,
|
|
192
|
-
local_ws,
|
|
193
|
-
fork_downstream,
|
|
194
|
-
fork,
|
|
195
|
-
build,
|
|
196
|
-
is_vendor,
|
|
197
|
-
)
|
|
198
|
-
if not run_tests:
|
|
199
|
-
click.echo(
|
|
200
|
-
FeedbackManager.success_create(
|
|
201
|
-
name=(
|
|
202
|
-
name
|
|
203
|
-
if to_run[name]["version"] is None
|
|
204
|
-
else f"{name}__v{to_run[name]['version']}"
|
|
205
|
-
)
|
|
206
|
-
)
|
|
207
|
-
)
|
|
208
|
-
except Exception as e:
|
|
209
|
-
filename = to_run[name]["filename"]
|
|
210
|
-
exception = FeedbackManager.error_push_file_exception(
|
|
211
|
-
filename=filename,
|
|
212
|
-
error=e,
|
|
213
|
-
)
|
|
214
|
-
raise click.ClickException(exception)
|
|
215
|
-
elif raise_on_exists:
|
|
216
|
-
raise AlreadyExistsException(
|
|
217
|
-
FeedbackManager.warning_name_already_exists(
|
|
218
|
-
name=name if to_run[name]["version"] is None else f"{name}__v{to_run[name]['version']}"
|
|
219
|
-
)
|
|
220
|
-
)
|
|
221
|
-
elif name_matches_existing_resource(resource, name, tb_client):
|
|
222
|
-
if resource == "pipes":
|
|
223
|
-
click.echo(FeedbackManager.error_pipe_cannot_be_pushed(name=name))
|
|
224
|
-
else:
|
|
225
|
-
click.echo(FeedbackManager.error_datasource_cannot_be_pushed(name=name))
|
|
226
|
-
else:
|
|
227
|
-
click.echo(
|
|
228
|
-
FeedbackManager.warning_name_already_exists(
|
|
229
|
-
name=(name if to_run[name]["version"] is None else f"{name}__v{to_run[name]['version']}")
|
|
230
|
-
)
|
|
231
|
-
)
|
|
232
|
-
if dry_run:
|
|
233
|
-
if should_push_file(name, remote_resource_names, force, run_tests):
|
|
234
|
-
extension = "pipe" if resource == "pipes" else "datasource"
|
|
235
|
-
click.echo(FeedbackManager.info_building_resource(name=f"{name}.{extension}", version=""))
|
|
236
|
-
elif name_matches_existing_resource(resource, name, tb_client):
|
|
237
|
-
if resource == "pipes":
|
|
238
|
-
click.echo(FeedbackManager.warning_pipe_cannot_be_pushed(name=name))
|
|
239
|
-
else:
|
|
240
|
-
click.echo(FeedbackManager.warning_datasource_cannot_be_pushed(name=name))
|
|
241
|
-
else:
|
|
242
|
-
click.echo(FeedbackManager.warning_dry_name_already_exists(name=name))
|
|
243
|
-
|
|
244
|
-
def push_files(
|
|
245
|
-
dependency_graph: GraphDependencies,
|
|
246
|
-
dry_run: bool = False,
|
|
247
|
-
):
|
|
248
|
-
endpoints_dep_map = dict()
|
|
249
|
-
processed = set()
|
|
250
|
-
|
|
251
|
-
resources_to_run = dependency_graph.to_run
|
|
252
|
-
|
|
253
|
-
# This will generate the graph from right to left and will fill the gaps of the dependencies
|
|
254
|
-
# If we have a graph like this:
|
|
255
|
-
# A -> B -> C
|
|
256
|
-
# If we only modify A, the normal dependencies graph will only contain a node like _{A => B}
|
|
257
|
-
# But we need a graph that contains A, B and C and the dependencies between them to deploy them in the right order
|
|
258
|
-
dependencies_graph_fork_downstream, resources_to_run_fork_downstream = generate_forkdownstream_graph(
|
|
259
|
-
dependency_graph.all_dep_map,
|
|
260
|
-
dependency_graph.all_resources,
|
|
261
|
-
resources_to_run,
|
|
262
|
-
list(dependency_graph.dep_map.keys()),
|
|
263
|
-
)
|
|
264
|
-
|
|
265
|
-
# First, we will deploy the datasources that need to be deployed.
|
|
266
|
-
# We need to deploy the datasources from left to right as some datasources might have MV that depend on the column types of previous datasources. Ex: `test_change_column_type_landing_datasource` test
|
|
267
|
-
groups = [group for group in toposort(dependencies_graph_fork_downstream)]
|
|
268
|
-
|
|
269
|
-
groups.reverse()
|
|
270
|
-
for group in groups:
|
|
271
|
-
for name in group:
|
|
272
|
-
if name in processed or not is_datasource(resources_to_run_fork_downstream[name]):
|
|
273
|
-
continue
|
|
274
|
-
|
|
275
|
-
# If we are trying to modify a Kafka or CDK datasource, we need to inform the user that the resource needs to be post-released
|
|
276
|
-
kafka_connection_name = (
|
|
277
|
-
resources_to_run_fork_downstream[name].get("params", {}).get("kafka_connection_name")
|
|
278
|
-
)
|
|
279
|
-
service = resources_to_run_fork_downstream[name].get("params", {}).get("import_service")
|
|
280
|
-
if release_created and (kafka_connection_name or service):
|
|
281
|
-
connector = "Kafka" if kafka_connection_name else service
|
|
282
|
-
error_msg = FeedbackManager.error_connector_require_post_release(connector=connector)
|
|
283
|
-
raise click.ClickException(error_msg)
|
|
284
|
-
|
|
285
|
-
push(
|
|
286
|
-
name,
|
|
287
|
-
resources_to_run_fork_downstream,
|
|
288
|
-
dry_run,
|
|
289
|
-
fork_downstream,
|
|
290
|
-
fork,
|
|
291
|
-
)
|
|
292
|
-
processed.add(name)
|
|
293
|
-
|
|
294
|
-
# Now, we will create a map of all the endpoints and there dependencies
|
|
295
|
-
# We are using the forkdownstream graph to get the dependencies of the endpoints as the normal dependencies graph only contains the resources that are going to be deployed
|
|
296
|
-
# But does not include the missing gaps
|
|
297
|
-
# If we have ENDPOINT_A ----> MV_PIPE_B -----> DATASOURCE_B ------> ENDPOINT_C
|
|
298
|
-
# Where endpoint A is being used in the MV_PIPE_B, if we only modify the endpoint A
|
|
299
|
-
# The dependencies graph will only contain the endpoint A and the MV_PIPE_B, but not the DATASOURCE_B and the ENDPOINT_C
|
|
300
|
-
groups = [group for group in toposort(dependencies_graph_fork_downstream)]
|
|
301
|
-
for group in groups:
|
|
302
|
-
for name in group:
|
|
303
|
-
if name in processed or not is_endpoint(resources_to_run_fork_downstream[name]):
|
|
304
|
-
continue
|
|
305
|
-
|
|
306
|
-
endpoints_dep_map[name] = dependencies_graph_fork_downstream[name]
|
|
307
|
-
|
|
308
|
-
# Now that we have the dependencies of the endpoints, we need to check that the resources has not been deployed yet and only care about the endpoints that depend on endpoints
|
|
309
|
-
groups = [group for group in toposort(endpoints_dep_map)]
|
|
310
|
-
|
|
311
|
-
# As we have used the forkdownstream graph to get the dependencies of the endpoints, we have all the dependencies of the endpoints
|
|
312
|
-
# But we need to deploy the endpoints and the dependencies of the endpoints from left to right
|
|
313
|
-
# So we need to reverse the groups
|
|
314
|
-
groups.reverse()
|
|
315
|
-
for group in groups:
|
|
316
|
-
for name in group:
|
|
317
|
-
if name in processed or not is_endpoint(resources_to_run_fork_downstream[name]):
|
|
318
|
-
continue
|
|
319
|
-
|
|
320
|
-
push(
|
|
321
|
-
name,
|
|
322
|
-
resources_to_run_fork_downstream,
|
|
323
|
-
dry_run,
|
|
324
|
-
fork_downstream,
|
|
325
|
-
fork,
|
|
326
|
-
)
|
|
327
|
-
processed.add(name)
|
|
328
|
-
|
|
329
|
-
# Now we should have the endpoints and datasources deployed, we can deploy the rest of the pipes (copy & sinks)
|
|
330
|
-
# We need to rely on the forkdownstream graph as it contains all the modified pipes as well as the dependencies of the pipes
|
|
331
|
-
# In this case, we don't need to generate a new graph as we did for the endpoints as the pipes are not going to be used as dependencies and the datasources are already deployed
|
|
332
|
-
groups = [group for group in toposort(dependencies_graph_fork_downstream)]
|
|
333
|
-
for group in groups:
|
|
334
|
-
for name in group:
|
|
335
|
-
if name in processed or is_materialized(resources_to_run_fork_downstream.get(name)):
|
|
336
|
-
continue
|
|
337
|
-
|
|
338
|
-
push(
|
|
339
|
-
name,
|
|
340
|
-
resources_to_run_fork_downstream,
|
|
341
|
-
dry_run,
|
|
342
|
-
fork_downstream,
|
|
343
|
-
fork,
|
|
344
|
-
)
|
|
345
|
-
processed.add(name)
|
|
346
|
-
|
|
347
|
-
# Finally, we need to deploy the materialized views from right to left.
|
|
348
|
-
# We need to rely on the forkdownstream graph as it contains all the modified materialized views as well as the dependencies of the materialized views
|
|
349
|
-
# In this case, we don't need to generate a new graph as we did for the endpoints as the pipes are not going to be used as dependencies and the datasources are already deployed
|
|
350
|
-
groups = [group for group in toposort(dependencies_graph_fork_downstream)]
|
|
351
|
-
for group in groups:
|
|
352
|
-
for name in group:
|
|
353
|
-
if name in processed or not is_materialized(resources_to_run_fork_downstream.get(name)):
|
|
354
|
-
continue
|
|
355
|
-
|
|
356
|
-
push(
|
|
357
|
-
name,
|
|
358
|
-
resources_to_run_fork_downstream,
|
|
359
|
-
dry_run,
|
|
360
|
-
fork_downstream,
|
|
361
|
-
fork,
|
|
362
|
-
)
|
|
363
|
-
processed.add(name)
|
|
364
|
-
|
|
365
|
-
push_files(dependencies_graph, dry_run)
|
|
366
|
-
|
|
367
|
-
if not dry_run and not run_tests and verbose:
|
|
368
|
-
click.echo(FeedbackManager.info_not_pushing_fixtures())
|
|
369
|
-
|
|
370
|
-
return dependencies_graph.to_run
|
|
371
43
|
|
|
372
44
|
|
|
373
45
|
def name_matches_existing_resource(resource: str, name: str, tb_client: TinyB):
|
|
@@ -2,6 +2,7 @@ import hashlib
|
|
|
2
2
|
import json
|
|
3
3
|
import logging
|
|
4
4
|
import os
|
|
5
|
+
import platform
|
|
5
6
|
import re
|
|
6
7
|
import subprocess
|
|
7
8
|
import threading
|
|
@@ -13,6 +14,7 @@ import boto3
|
|
|
13
14
|
import click
|
|
14
15
|
import requests
|
|
15
16
|
from docker.client import DockerClient
|
|
17
|
+
from docker.errors import ImageNotFound
|
|
16
18
|
from docker.models.containers import Container
|
|
17
19
|
|
|
18
20
|
import docker
|
|
@@ -39,6 +41,11 @@ _PATTERN_MESSAGE = re.compile(r'message="([^"]*)"')
|
|
|
39
41
|
|
|
40
42
|
TB_IMAGE_NAME = "tinybirdco/tinybird-local:latest"
|
|
41
43
|
TB_CONTAINER_NAME = "tinybird-local"
|
|
44
|
+
|
|
45
|
+
# Docker Hub registry hostnames that Docker may prepend to a repository name in
|
|
46
|
+
# RepoDigests (e.g. ``docker.io/tinybirdco/tinybird-local@sha256:…``). They all
|
|
47
|
+
# refer to the same registry, so we strip them before comparing repositories.
|
|
48
|
+
_DOCKER_HUB_REGISTRY_PREFIXES = ("docker.io/", "index.docker.io/", "registry-1.docker.io/")
|
|
42
49
|
TB_LOCAL_PORT = int(os.getenv("TB_LOCAL_PORT", 7181))
|
|
43
50
|
TB_LOCAL_CLICKHOUSE_INTERFACE_PORT = int(os.getenv("TB_LOCAL_CLICKHOUSE_INTERFACE_PORT", 7182))
|
|
44
51
|
TB_LOCAL_HOST = _PATTERN_HTTP_PREFIX.sub("", os.getenv("TB_LOCAL_HOST", "localhost"))
|
|
@@ -270,6 +277,107 @@ def get_local_tokens(silent: bool = False) -> Dict[str, str]:
|
|
|
270
277
|
)
|
|
271
278
|
|
|
272
279
|
|
|
280
|
+
def get_local_image_platform() -> str:
|
|
281
|
+
"""Return the Docker platform for the Tinybird Local image matching the host.
|
|
282
|
+
|
|
283
|
+
Tinybird Local is published as a multi-arch image, so we select the native
|
|
284
|
+
architecture to avoid emulating linux/amd64 on arm64 hosts (e.g. Apple
|
|
285
|
+
Silicon). Defaults to linux/amd64 for unknown architectures.
|
|
286
|
+
|
|
287
|
+
``TB_LOCAL_IMAGE_PLATFORM`` overrides the auto-detected platform. This is an
|
|
288
|
+
escape hatch for running a single-arch image that does not match the host
|
|
289
|
+
(e.g. deliberately running the amd64 image emulated on an arm64 Mac, or when
|
|
290
|
+
a locally built image only provides one architecture).
|
|
291
|
+
"""
|
|
292
|
+
override = os.getenv("TB_LOCAL_IMAGE_PLATFORM")
|
|
293
|
+
if override:
|
|
294
|
+
return override
|
|
295
|
+
machine = platform.machine().lower()
|
|
296
|
+
if machine in ("arm64", "aarch64"):
|
|
297
|
+
return "linux/arm64"
|
|
298
|
+
return "linux/amd64"
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _normalize_repo(name: str) -> str:
|
|
302
|
+
"""Strip the Docker Hub registry host from a repository name so digests
|
|
303
|
+
recorded as ``docker.io/tinybirdco/tinybird-local`` and
|
|
304
|
+
``tinybirdco/tinybird-local`` compare equal."""
|
|
305
|
+
for prefix in _DOCKER_HUB_REGISTRY_PREFIXES:
|
|
306
|
+
if name.startswith(prefix):
|
|
307
|
+
return name[len(prefix) :]
|
|
308
|
+
return name
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def _remote_manifest_digests(docker_client: DockerClient, image_name: str) -> set[str]:
|
|
312
|
+
remote_image = docker_client.images.get_registry_data(image_name)
|
|
313
|
+
remote_digests: set[str] = set()
|
|
314
|
+
if isinstance(remote_image.id, str):
|
|
315
|
+
remote_digests.add(remote_image.id)
|
|
316
|
+
|
|
317
|
+
attrs = remote_image.attrs if isinstance(remote_image.attrs, dict) else {}
|
|
318
|
+
descriptor = attrs.get("Descriptor", {})
|
|
319
|
+
if isinstance(descriptor, dict):
|
|
320
|
+
descriptor_digest = descriptor.get("digest")
|
|
321
|
+
if isinstance(descriptor_digest, str):
|
|
322
|
+
remote_digests.add(descriptor_digest)
|
|
323
|
+
|
|
324
|
+
return remote_digests
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def is_new_local_image_available(docker_client: DockerClient, check_new_version: bool = True) -> tuple[bool, bool]:
|
|
328
|
+
"""Decide whether to pull the Tinybird Local image before starting it.
|
|
329
|
+
|
|
330
|
+
Returns ``(show_prompt, pull_required)``.
|
|
331
|
+
|
|
332
|
+
Two local-only checks always run, even with ``check_new_version=False`` (the
|
|
333
|
+
``--skip-new-version`` / prompt-to-start paths), because they decide whether
|
|
334
|
+
the cached image is *usable* at all:
|
|
335
|
+
- image missing -> pull it;
|
|
336
|
+
- wrong platform cached -> re-pull. An arm64 user who pulled ``:latest`` with
|
|
337
|
+
the old (amd64-forcing) CLI has the tag cached as ``linux/amd64``; we must
|
|
338
|
+
replace it with the native image instead of running it emulated.
|
|
339
|
+
|
|
340
|
+
Only when ``check_new_version`` is set do we make the registry round-trip to
|
|
341
|
+
detect a newer published image. Docker Engine exposes the tag's registry
|
|
342
|
+
digest through ``get_registry_data``; we compare that digest with the
|
|
343
|
+
normalized local ``RepoDigests`` for this image.
|
|
344
|
+
|
|
345
|
+
We fail safe everywhere: if the registry is unreachable or the local image
|
|
346
|
+
cannot be inspected we keep what is there. A locally built image (no matching
|
|
347
|
+
registry digest) will prompt; developers use ``--skip-new-version`` for that.
|
|
348
|
+
"""
|
|
349
|
+
try:
|
|
350
|
+
local_image = docker_client.images.get(TB_IMAGE_NAME)
|
|
351
|
+
except ImageNotFound:
|
|
352
|
+
return False, True # nothing local yet, we must pull
|
|
353
|
+
except Exception:
|
|
354
|
+
return False, False # cannot inspect locally, keep whatever is there
|
|
355
|
+
|
|
356
|
+
local_platform = f"{local_image.attrs.get('Os', '')}/{local_image.attrs.get('Architecture', '')}"
|
|
357
|
+
if local_platform != get_local_image_platform():
|
|
358
|
+
return False, True # wrong platform cached, re-pull the native image
|
|
359
|
+
|
|
360
|
+
if not check_new_version:
|
|
361
|
+
return False, False # cached image is usable; don't touch the registry
|
|
362
|
+
|
|
363
|
+
repo = _PATTERN_TAG_SUFFIX.sub("", TB_IMAGE_NAME)
|
|
364
|
+
local_digests = {
|
|
365
|
+
repo_digest.split("@", 1)[1]
|
|
366
|
+
for repo_digest in (local_image.attrs.get("RepoDigests") or [])
|
|
367
|
+
if "@" in repo_digest and _normalize_repo(repo_digest.split("@", 1)[0]) == repo
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
try:
|
|
371
|
+
remote_digests = _remote_manifest_digests(docker_client, TB_IMAGE_NAME)
|
|
372
|
+
except Exception:
|
|
373
|
+
return False, False # registry unreachable, keep the local image
|
|
374
|
+
|
|
375
|
+
if not remote_digests:
|
|
376
|
+
return False, False # registry metadata unavailable, keep the local image
|
|
377
|
+
|
|
378
|
+
return local_digests.isdisjoint(remote_digests), False
|
|
379
|
+
|
|
380
|
+
|
|
273
381
|
def start_tinybird_local(
|
|
274
382
|
docker_client: DockerClient,
|
|
275
383
|
use_aws_creds: bool,
|
|
@@ -280,29 +388,23 @@ def start_tinybird_local(
|
|
|
280
388
|
watch: bool = False,
|
|
281
389
|
) -> None:
|
|
282
390
|
"""Start the Tinybird container."""
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
local_image_id = local_image.attrs["RepoDigests"][0].split("@")[1]
|
|
290
|
-
remote_image = docker_client.images.get_registry_data(TB_IMAGE_NAME)
|
|
291
|
-
pull_show_prompt = local_image_id != remote_image.id
|
|
292
|
-
except Exception:
|
|
293
|
-
pull_show_prompt = False
|
|
294
|
-
pull_required = True
|
|
391
|
+
# Always honors a forced pull (missing / wrong-platform image) even when the
|
|
392
|
+
# new-version check is skipped, so an arm64 host never keeps running the
|
|
393
|
+
# emulated amd64 image.
|
|
394
|
+
pull_show_prompt, pull_required = is_new_local_image_available(
|
|
395
|
+
docker_client, check_new_version=not skip_new_version
|
|
396
|
+
)
|
|
295
397
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
398
|
+
if pull_show_prompt and click.confirm(
|
|
399
|
+
FeedbackManager.warning(message="△ New version detected, download? [y/N]:"),
|
|
400
|
+
show_default=False,
|
|
401
|
+
prompt_suffix="",
|
|
402
|
+
):
|
|
403
|
+
click.echo(FeedbackManager.info(message="* Downloading latest version of Tinybird Local..."))
|
|
404
|
+
pull_required = True
|
|
303
405
|
|
|
304
|
-
|
|
305
|
-
|
|
406
|
+
if pull_required:
|
|
407
|
+
docker_client.images.pull(TB_IMAGE_NAME, platform=get_local_image_platform())
|
|
306
408
|
|
|
307
409
|
environment = {}
|
|
308
410
|
if use_aws_creds:
|
|
@@ -334,7 +436,7 @@ def start_tinybird_local(
|
|
|
334
436
|
detach=True,
|
|
335
437
|
ports={"7181/tcp": TB_LOCAL_PORT, "7182/tcp": TB_LOCAL_CLICKHOUSE_INTERFACE_PORT},
|
|
336
438
|
remove=False,
|
|
337
|
-
platform=
|
|
439
|
+
platform=get_local_image_platform(),
|
|
338
440
|
environment=environment,
|
|
339
441
|
volumes=volumes,
|
|
340
442
|
)
|
tinybird/tb/modules/watch.py
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import time
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import Any, Callable,
|
|
4
|
+
from typing import Any, Callable, Optional, Union
|
|
5
5
|
|
|
6
6
|
import click
|
|
7
7
|
from watchdog.events import (
|
|
8
8
|
DirDeletedEvent,
|
|
9
|
-
DirMovedEvent,
|
|
10
9
|
FileDeletedEvent,
|
|
11
|
-
FileMovedEvent,
|
|
12
|
-
FileSystemEventHandler,
|
|
13
10
|
PatternMatchingEventHandler,
|
|
14
11
|
)
|
|
15
12
|
from watchdog.observers import Observer
|
|
@@ -148,99 +145,3 @@ def watch_project(
|
|
|
148
145
|
observer.stop()
|
|
149
146
|
|
|
150
147
|
observer.join()
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
class FileChangeHandler(FileSystemEventHandler):
|
|
154
|
-
def __init__(self, filenames: List[str], process: Callable[[List[str]], None], build_ok: bool):
|
|
155
|
-
self.unprocessed_filenames = [os.path.abspath(f) for f in filenames]
|
|
156
|
-
self.process = process
|
|
157
|
-
self.build_ok = build_ok
|
|
158
|
-
|
|
159
|
-
@property
|
|
160
|
-
def filenames(self) -> List[str]:
|
|
161
|
-
return [f for f in self.unprocessed_filenames if os.path.exists(f)]
|
|
162
|
-
|
|
163
|
-
def should_process(self, event: Any) -> Optional[str]:
|
|
164
|
-
if event.is_directory:
|
|
165
|
-
return None
|
|
166
|
-
|
|
167
|
-
def should_process_path(path: str) -> bool:
|
|
168
|
-
if not os.path.exists(path):
|
|
169
|
-
return False
|
|
170
|
-
is_vendor = "vendor/" in path
|
|
171
|
-
if is_vendor:
|
|
172
|
-
return False
|
|
173
|
-
return any(path.endswith(ext) for ext in [".pipe"])
|
|
174
|
-
|
|
175
|
-
if should_process_path(event.src_path):
|
|
176
|
-
return event.src_path
|
|
177
|
-
|
|
178
|
-
if should_process_path(event.dest_path):
|
|
179
|
-
return event.dest_path
|
|
180
|
-
|
|
181
|
-
return None
|
|
182
|
-
|
|
183
|
-
def on_modified(self, event: Any) -> None:
|
|
184
|
-
if path := self.should_process(event):
|
|
185
|
-
filename = path.split("/")[-1]
|
|
186
|
-
click.echo(FeedbackManager.highlight(message=f"\n\n⟲ Changes detected in {filename}\n"))
|
|
187
|
-
try:
|
|
188
|
-
to_process = [path] if self.build_ok else self.filenames
|
|
189
|
-
self.process(to_process)
|
|
190
|
-
self.build_ok = True
|
|
191
|
-
except Exception as e:
|
|
192
|
-
click.echo(FeedbackManager.error_exception(error=e))
|
|
193
|
-
|
|
194
|
-
def on_moved(self, event: Union[DirMovedEvent, FileMovedEvent]) -> None:
|
|
195
|
-
if path := self.should_process(event):
|
|
196
|
-
is_new_file = False
|
|
197
|
-
if path not in self.unprocessed_filenames:
|
|
198
|
-
is_new_file = True
|
|
199
|
-
self.unprocessed_filenames.append(path)
|
|
200
|
-
|
|
201
|
-
filename = path.split("/")[-1]
|
|
202
|
-
if is_new_file:
|
|
203
|
-
click.echo(FeedbackManager.highlight(message=f"\n\n⟲ New file detected: {filename}\n"))
|
|
204
|
-
else:
|
|
205
|
-
click.echo(FeedbackManager.highlight(message=f"\n\n⟲ Changes detected in {filename}\n"))
|
|
206
|
-
try:
|
|
207
|
-
should_rebuild_all = is_new_file or not self.build_ok
|
|
208
|
-
to_process = self.filenames if should_rebuild_all else [path]
|
|
209
|
-
self.process(to_process)
|
|
210
|
-
self.build_ok = True
|
|
211
|
-
except Exception as e:
|
|
212
|
-
click.echo(FeedbackManager.error_exception(error=e))
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
def watch_files(
|
|
216
|
-
filenames: List[str],
|
|
217
|
-
process: Callable,
|
|
218
|
-
project: Project,
|
|
219
|
-
build_ok: bool,
|
|
220
|
-
) -> None:
|
|
221
|
-
# Handle both sync and async process functions
|
|
222
|
-
def process_wrapper(files: List[str]) -> None:
|
|
223
|
-
click.echo(FeedbackManager.highlight(message="» Rebuilding project..."))
|
|
224
|
-
time_start = time.time()
|
|
225
|
-
process(files, watch=True)
|
|
226
|
-
time_end = time.time()
|
|
227
|
-
elapsed_time = time_end - time_start
|
|
228
|
-
click.echo(
|
|
229
|
-
FeedbackManager.success(message="\n✓ ")
|
|
230
|
-
+ FeedbackManager.gray(message=f"Rebuild completed in {elapsed_time:.1f}s")
|
|
231
|
-
)
|
|
232
|
-
|
|
233
|
-
event_handler = FileChangeHandler(filenames, lambda f: process_wrapper(f), build_ok)
|
|
234
|
-
observer = Observer()
|
|
235
|
-
|
|
236
|
-
observer.schedule(event_handler, path=str(project.path), recursive=True)
|
|
237
|
-
|
|
238
|
-
observer.start()
|
|
239
|
-
|
|
240
|
-
try:
|
|
241
|
-
while True:
|
|
242
|
-
time.sleep(1)
|
|
243
|
-
except KeyboardInterrupt:
|
|
244
|
-
observer.stop()
|
|
245
|
-
|
|
246
|
-
observer.join()
|
|
@@ -171,12 +171,6 @@ class CLIConfig:
|
|
|
171
171
|
def set_semver(self, semver: Optional[str]) -> None:
|
|
172
172
|
self["semver"] = semver
|
|
173
173
|
|
|
174
|
-
def get_semver(self) -> Optional[str]:
|
|
175
|
-
try:
|
|
176
|
-
return self["semver"]
|
|
177
|
-
except KeyError:
|
|
178
|
-
return None
|
|
179
|
-
|
|
180
174
|
def set_token_for_host(self, token: Optional[str], host: Optional[str]) -> None:
|
|
181
175
|
"""Sets the token for the specified host.
|
|
182
176
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: tinybird
|
|
3
|
-
Version: 4.6.
|
|
3
|
+
Version: 4.6.7.dev0
|
|
4
4
|
Summary: Tinybird Command Line Tool
|
|
5
5
|
Home-page: https://www.tinybird.co/docs/forward/commands
|
|
6
6
|
Author: Tinybird
|
|
@@ -52,6 +52,13 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
|
|
|
52
52
|
Changelog
|
|
53
53
|
----------
|
|
54
54
|
|
|
55
|
+
4.6.6
|
|
56
|
+
*******
|
|
57
|
+
|
|
58
|
+
- `Fixed` `tb local` now starts the Tinybird Local container using the host's native architecture (``linux/arm64`` on Apple Silicon, ``linux/amd64`` elsewhere) instead of always forcing ``linux/amd64``, so it no longer runs under emulation on arm64 machines.
|
|
59
|
+
- `Added` ``TB_LOCAL_IMAGE_PLATFORM`` environment variable to override the auto-detected image platform (e.g. ``linux/amd64`` to run the amd64 image emulated on an arm64 host, or to run a locally built single-arch image that does not match the host).
|
|
60
|
+
- `Fixed` `tb local start` new-version detection now normalizes Docker Hub image digests and no longer pulls when the registry is unreachable.
|
|
61
|
+
|
|
55
62
|
4.6.5
|
|
56
63
|
*******
|
|
57
64
|
|
|
@@ -19,18 +19,18 @@ tinybird/datafile/parse_datasource.py,sha256=yd58HrUF4yNJXLn6OsvKGpZJpvrcjLGAeJG
|
|
|
19
19
|
tinybird/datafile/parse_pipe.py,sha256=-9bbgVuiWRyDYydrLVflDBt8GstZotMy6dklsrc6MUY,3859
|
|
20
20
|
tinybird/iterating/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
21
|
tinybird/iterating/data_branch_modes.py,sha256=5YuDa-gr8mKwmES8Xro6TRKbQtaOtIw4GbC606Qhu3o,184
|
|
22
|
-
tinybird/tb/__cli__.py,sha256=
|
|
22
|
+
tinybird/tb/__cli__.py,sha256=pl01mee5GgJmds84ZsuJKEbeBrsAlWDiJJtFe0icuBs,245
|
|
23
23
|
tinybird/tb/check_pypi.py,sha256=Gp0HkHHDFMSDL6nxKlOY51z7z1Uv-2LRexNTZSHHGmM,552
|
|
24
24
|
tinybird/tb/cli.py,sha256=IjiGfNIpxSxi1odK1kMj9s8lEhx3sAUgGA263XdmyR0,1119
|
|
25
25
|
tinybird/tb/client.py,sha256=wgXYa_CSKi-ymWnX-j8-CNHCNdQrUWQA6ezT0REMPOs,56830
|
|
26
26
|
tinybird/tb/config.py,sha256=l6NtYvSPZDVOtt1W4G4ExXXDIf0hm6lvRnRoDMbdrsY,5429
|
|
27
27
|
tinybird/tb/modules/branch.py,sha256=U50nj2kZllCkOHQfJWQ-YQ260pvlcssgStMSovmqMiU,10157
|
|
28
|
-
tinybird/tb/modules/build.py,sha256=
|
|
28
|
+
tinybird/tb/modules/build.py,sha256=q7-aVM7q1tApiax_y6vYSCZq4cJ9ATSAm9D-elpNp7I,6543
|
|
29
29
|
tinybird/tb/modules/build_common.py,sha256=o04aeaoyGTnwhR0cEXAgQzc7SJya97YECoEihsi4SyU,24979
|
|
30
30
|
tinybird/tb/modules/cicd.py,sha256=IO4qqsoLRXcubALb7vx_QnRpg3zIIxfaVO9bGomlESY,8267
|
|
31
31
|
tinybird/tb/modules/cli.py,sha256=lJlRVpy1IvI9a3zhwNEiMo7gnUEU-IUBEYj3ZWZ_0u0,43760
|
|
32
32
|
tinybird/tb/modules/common.py,sha256=8S_Z49hY4n-uZ_rdEZTFvzbQVG7aV0PBhZx3OBfgN3g,92987
|
|
33
|
-
tinybird/tb/modules/config.py,sha256=
|
|
33
|
+
tinybird/tb/modules/config.py,sha256=kZsMBf_qrGXPvhx1GW46DXS62ClVIKFFPjDXEZyGeyE,11181
|
|
34
34
|
tinybird/tb/modules/connection.py,sha256=HwHn0YgwqcKEYCLr2OqDzJzAUFd65Wv8MZiSUw_TLII,18452
|
|
35
35
|
tinybird/tb/modules/connection_dynamodb.py,sha256=j1Z0tXrlTZJU1LJ0rEzv3ezo0pS4PJJKfVjUTGrH3nk,9473
|
|
36
36
|
tinybird/tb/modules/connection_kafka.py,sha256=Qc8uUNl5SvUcUGb_aG3AirYKw6673FoX1Sfdlzgz6Y8,33530
|
|
@@ -52,7 +52,7 @@ tinybird/tb/modules/job_common.py,sha256=3rdRH9F9kCRL_dBa5fghB27xgHqnO3oulBeIb1A
|
|
|
52
52
|
tinybird/tb/modules/llm.py,sha256=fPBBCmM3KlCksLlgJkg4joDn6y3H5QjDzE-Pm4YNf7E,1782
|
|
53
53
|
tinybird/tb/modules/llm_utils.py,sha256=qs3HKJ1Lcugoumq1gzOpYRyRerEcSK6Yy_80vWoEbco,3217
|
|
54
54
|
tinybird/tb/modules/local.py,sha256=1Uf1cOjVPYt3kF4cTQ8gmDsNbfTqmxv2SAfiiYYoMr4,14881
|
|
55
|
-
tinybird/tb/modules/local_common.py,sha256=
|
|
55
|
+
tinybird/tb/modules/local_common.py,sha256=Bl47fKrPXLCmslFq1H2fG5KMYuAANlHm-GeQ3vdOGro,38863
|
|
56
56
|
tinybird/tb/modules/local_logs.py,sha256=TTrfEQr3wVoddrQiKslSWJQ8BE4FOlMQZbH1yJ5f56I,7427
|
|
57
57
|
tinybird/tb/modules/login.py,sha256=fxgcsN0fMhjzT_6C8VrwGTxnLxLVuWlZvASFr8I9yUU,1885
|
|
58
58
|
tinybird/tb/modules/login_common.py,sha256=FFMdj2vSiFaAvZUwmtTwg--Q-gqwEo34CaAgItf3M08,21744
|
|
@@ -76,7 +76,7 @@ tinybird/tb/modules/test.py,sha256=KqMMl_nHrl23AJzM1KIDraApGln5vniCMdDzVCzgC0s,1
|
|
|
76
76
|
tinybird/tb/modules/test_common.py,sha256=t5tFg79ng_6d5aXW-RGbcwBq2S22m9sCKAiHagrTaiQ,11041
|
|
77
77
|
tinybird/tb/modules/token.py,sha256=fNUh2PQCMRFO1YYiWydeM9JUGJaYljCWMtU40fjw-Wc,13895
|
|
78
78
|
tinybird/tb/modules/ts_project.py,sha256=XeDN-3gthT7GZYIFn0l-y5RJaofB_2T-I8gq6dL-a-A,7428
|
|
79
|
-
tinybird/tb/modules/watch.py,sha256=
|
|
79
|
+
tinybird/tb/modules/watch.py,sha256=HTZwqlGWDLJPBxBh04J0tAZDP_6BhqLupHBDx4uZDYo,5218
|
|
80
80
|
tinybird/tb/modules/workspace.py,sha256=7shWyyZD9IT32EeP0aEgsZGk0U1ZdT6hWnVfctRoIJk,8939
|
|
81
81
|
tinybird/tb/modules/workspace_members.py,sha256=8oQLTczEh9cIdD3iF-N3SJuqLtKdF-_g-YzeVaqKGP0,9486
|
|
82
82
|
tinybird/tb/modules/datafile/build.py,sha256=WtxPakSjnpQhs2SxyF8Ckm_BdOVOUMlRdhPjVFli2SE,40910
|
|
@@ -90,17 +90,17 @@ tinybird/tb/modules/datafile/format_connection.py,sha256=ieD9Jb6In6PmZqIysZ1tD1J
|
|
|
90
90
|
tinybird/tb/modules/datafile/format_datasource.py,sha256=rqA0Mr_3x5vaXe5vXWs7Fe2XmgVWn4niSlpvLnZh4vg,5325
|
|
91
91
|
tinybird/tb/modules/datafile/format_pipe.py,sha256=9UuUsl5DjV4tJIRr6Whuv-AtIM9Gx7R3396AbKjHaJ4,7724
|
|
92
92
|
tinybird/tb/modules/datafile/pipe_checker.py,sha256=dxsCQoA6ruxg1fvF6sMLFowpjaqws8lUQcM7XyhgZPE,24609
|
|
93
|
-
tinybird/tb/modules/datafile/playground.py,sha256=
|
|
93
|
+
tinybird/tb/modules/datafile/playground.py,sha256=5AUFNi2Oh6VQdaoCbQ4JeZAXKD0xzCOEUkEk5IXOj00,40973
|
|
94
94
|
tinybird/tb/modules/datafile/pull.py,sha256=C-px80jgA6pwPaGi5mcF5HjqpgwC0grso-F9SDDnRT0,6447
|
|
95
95
|
tinybird/tb/modules/tinyunit/tinyunit.py,sha256=ZhKSwk5uIRggxDAPaqYZrcMMI0hLN1qGNIf-eRoJJWc,11055
|
|
96
96
|
tinybird/tb_cli_modules/cicd.py,sha256=i2Mw8AbmEVNBcEPYdio7liy3PGqh1ezVFZ0OmJ9ww5o,13809
|
|
97
97
|
tinybird/tb_cli_modules/common.py,sha256=rp1IAfDhbPSwnoMQyW4ZniYDqNZepo1cBgwL8naAa8U,76711
|
|
98
|
-
tinybird/tb_cli_modules/config.py,sha256=
|
|
98
|
+
tinybird/tb_cli_modules/config.py,sha256=Ey9yqM27C4Irglm5-63RXte8K0bFh8ConKdIoAaQv-k,11188
|
|
99
99
|
tinybird/tb_cli_modules/exceptions.py,sha256=pmucP4kTF4irIt7dXiG-FcnI-o3mvDusPmch1L8RCWk,3367
|
|
100
100
|
tinybird/tb_cli_modules/regions.py,sha256=QjsL5H6Kg-qr0aYVLrvb1STeJ5Sx_sjvbOYO0LrEGMk,166
|
|
101
101
|
tinybird/tb_cli_modules/telemetry.py,sha256=W098H6jmS4kpE7hN3tadaREBTf7oMocel-lkKWN0pU8,10466
|
|
102
|
-
tinybird-4.6.
|
|
103
|
-
tinybird-4.6.
|
|
104
|
-
tinybird-4.6.
|
|
105
|
-
tinybird-4.6.
|
|
106
|
-
tinybird-4.6.
|
|
102
|
+
tinybird-4.6.7.dev0.dist-info/METADATA,sha256=MEgE3XVXz6knf6hn0GSYsYgYQ3DbIUoYXM7KSJPT-0A,14568
|
|
103
|
+
tinybird-4.6.7.dev0.dist-info/WHEEL,sha256=beeZ86-EfXScwlR_HKu4SllMC9wUEj_8Z_4FJ3egI2w,91
|
|
104
|
+
tinybird-4.6.7.dev0.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
|
|
105
|
+
tinybird-4.6.7.dev0.dist-info/top_level.txt,sha256=ZIQJTPCzMqnfDzM_hEGZrJqDSEcKnIK_49T86DGWpyQ,78
|
|
106
|
+
tinybird-4.6.7.dev0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|