tinybird 0.0.1.dev9__tar.gz → 0.0.1.dev11__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.
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/PKG-INFO +1 -1
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/feedback_manager.py +1 -1
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/build.py +126 -44
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/cicd.py +0 -1
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/cli.py +0 -3
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/common.py +35 -13
- tinybird-0.0.1.dev11/tinybird/tb/modules/create.py +176 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/datafile/build.py +13 -4
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/datafile/build_pipe.py +0 -1
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/datafile/common.py +12 -0
- tinybird-0.0.1.dev11/tinybird/tb/modules/datafile/fixture.py +53 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/llm.py +1 -1
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/local.py +44 -17
- tinybird-0.0.1.dev11/tinybird/tb/modules/mock.py +62 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/prompts.py +28 -2
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird.egg-info/PKG-INFO +1 -1
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird.egg-info/SOURCES.txt +1 -0
- tinybird-0.0.1.dev9/tinybird/tb/modules/create.py +0 -214
- tinybird-0.0.1.dev9/tinybird/tb/modules/mock.py +0 -53
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/setup.cfg +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/__cli__.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/ch_utils/constants.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/ch_utils/engine.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/check_pypi.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/client.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/config.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/connectors.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/context.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/datafile.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/datatypes.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/git_settings.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/sql.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/sql_template.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/sql_template_fmt.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/sql_toolset.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/syncasync.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/cli.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/auth.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/branch.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/config.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/connection.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/datafile/build_common.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/datafile/diff.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/datafile/exceptions.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/datafile/format_common.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/datafile/format_pipe.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/datafile/parse_datasource.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/datafile/parse_pipe.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/datafile/pipe_checker.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/datafile/pull.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/datasource.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/exceptions.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/fmt.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/job.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/pipe.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/regions.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/table.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/tag.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/telemetry.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/test.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/tinyunit/tinyunit.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/tinyunit/tinyunit_lib.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/token.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/workspace.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb/modules/workspace_members.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb_cli.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb_cli_modules/auth.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb_cli_modules/branch.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb_cli_modules/cicd.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb_cli_modules/cli.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb_cli_modules/common.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb_cli_modules/config.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb_cli_modules/connection.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb_cli_modules/datasource.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb_cli_modules/exceptions.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb_cli_modules/fmt.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb_cli_modules/job.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb_cli_modules/pipe.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb_cli_modules/regions.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb_cli_modules/tag.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb_cli_modules/telemetry.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb_cli_modules/test.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb_cli_modules/workspace.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tb_cli_modules/workspace_members.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird/tornado_template.py +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird.egg-info/dependency_links.txt +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird.egg-info/entry_points.txt +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird.egg-info/requires.txt +0 -0
- {tinybird-0.0.1.dev9 → tinybird-0.0.1.dev11}/tinybird.egg-info/top_level.txt +0 -0
|
@@ -792,7 +792,7 @@ Ready? """
|
|
|
792
792
|
info_diff_resources_for_git_init = info_message(
|
|
793
793
|
"** Checking diffs between remote Workspace and local. Hint: use 'tb diff' to check if your Data Project and Workspace synced"
|
|
794
794
|
)
|
|
795
|
-
info_cicd_file_generated = info_message("
|
|
795
|
+
info_cicd_file_generated = info_message("✓ {file_path}")
|
|
796
796
|
info_available_git_providers = info_message("** List of available providers:")
|
|
797
797
|
info_git_release_init_without_diffs = info_message("** No diffs detected for '{workspace}'")
|
|
798
798
|
info_deployment_detecting_changes_header = info_message("\n** Detecting changes from last commit ...")
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import cmd
|
|
2
3
|
import os
|
|
3
4
|
import random
|
|
4
5
|
import subprocess
|
|
6
|
+
import sys
|
|
5
7
|
import threading
|
|
6
8
|
import time
|
|
7
9
|
from pathlib import Path
|
|
@@ -16,29 +18,71 @@ import tinybird.context as context
|
|
|
16
18
|
from tinybird.client import TinyB
|
|
17
19
|
from tinybird.config import FeatureFlags
|
|
18
20
|
from tinybird.feedback_manager import FeedbackManager, bcolors
|
|
19
|
-
from tinybird.syncasync import sync_to_async
|
|
20
21
|
from tinybird.tb.modules.cli import cli
|
|
21
|
-
from tinybird.tb.modules.common import
|
|
22
|
-
coro,
|
|
23
|
-
)
|
|
22
|
+
from tinybird.tb.modules.common import coro, push_data
|
|
24
23
|
from tinybird.tb.modules.datafile.build import folder_build
|
|
25
|
-
from tinybird.tb.modules.datafile.common import get_project_filenames, has_internal_datafiles
|
|
24
|
+
from tinybird.tb.modules.datafile.common import get_project_filenames, get_project_fixtures, has_internal_datafiles
|
|
26
25
|
from tinybird.tb.modules.datafile.exceptions import ParseException
|
|
26
|
+
from tinybird.tb.modules.datafile.fixture import build_fixture_name, get_fixture_dir
|
|
27
27
|
from tinybird.tb.modules.datafile.parse_datasource import parse_datasource
|
|
28
28
|
from tinybird.tb.modules.datafile.parse_pipe import parse_pipe
|
|
29
29
|
from tinybird.tb.modules.local import get_tinybird_local_client
|
|
30
30
|
from tinybird.tb.modules.table import format_table
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
class BuildShell(cmd.Cmd):
|
|
34
|
+
prompt = "\n\001\033[1;32m\002TB > \001\033[0m\002"
|
|
35
|
+
|
|
36
|
+
def __init__(self, folder: str):
|
|
37
|
+
super().__init__()
|
|
38
|
+
self.folder = folder
|
|
39
|
+
|
|
40
|
+
def do_exit(self, arg):
|
|
41
|
+
sys.exit(0)
|
|
42
|
+
|
|
43
|
+
def do_quit(self, arg):
|
|
44
|
+
sys.exit(0)
|
|
45
|
+
|
|
46
|
+
def default(self, argline):
|
|
47
|
+
click.echo("")
|
|
48
|
+
if argline.startswith("tb build"):
|
|
49
|
+
click.echo(FeedbackManager.error(message="Build command is already running"))
|
|
50
|
+
else:
|
|
51
|
+
arg_stripped = argline.strip().lower()
|
|
52
|
+
if not arg_stripped:
|
|
53
|
+
return
|
|
54
|
+
if arg_stripped.startswith("tb"):
|
|
55
|
+
extra_args = f" --folder {self.folder}" if arg_stripped.startswith("tb mock") else ""
|
|
56
|
+
subprocess.run(arg_stripped + extra_args, shell=True, text=True)
|
|
57
|
+
elif arg_stripped.startswith("with") or arg_stripped.startswith("select"):
|
|
58
|
+
subprocess.run(f'tb sql "{arg_stripped}"', shell=True, text=True)
|
|
59
|
+
elif arg_stripped.startswith("mock "):
|
|
60
|
+
subprocess.run(f"tb {arg_stripped} --folder {self.folder}", shell=True, text=True)
|
|
61
|
+
else:
|
|
62
|
+
click.echo(FeedbackManager.error(message="Invalid command"))
|
|
63
|
+
|
|
64
|
+
def reprint_prompt(self):
|
|
65
|
+
self.stdout.write(self.prompt)
|
|
66
|
+
self.stdout.flush()
|
|
67
|
+
|
|
68
|
+
|
|
33
69
|
class FileChangeHandler(FileSystemEventHandler):
|
|
34
70
|
def __init__(self, filenames: List[str], process: Callable[[List[str]], None]):
|
|
35
71
|
self.filenames = filenames
|
|
36
72
|
self.process = process
|
|
37
73
|
|
|
38
74
|
def on_modified(self, event: Any) -> None:
|
|
39
|
-
if not event.is_directory and any(event.src_path.endswith(ext) for ext in [".datasource", ".pipe"]):
|
|
75
|
+
if not event.is_directory and any(event.src_path.endswith(ext) for ext in [".datasource", ".pipe", ".ndjson"]):
|
|
40
76
|
filename = event.src_path.split("/")[-1]
|
|
41
|
-
click.echo(FeedbackManager.highlight(message=f"\n⟲ Changes detected in {filename}\n"))
|
|
77
|
+
click.echo(FeedbackManager.highlight(message=f"\n\n⟲ Changes detected in {filename}\n"))
|
|
78
|
+
try:
|
|
79
|
+
self.process([event.src_path])
|
|
80
|
+
except Exception as e:
|
|
81
|
+
click.echo(FeedbackManager.error_exception(error=e))
|
|
82
|
+
|
|
83
|
+
def on_created(self, event: Any) -> None:
|
|
84
|
+
if not event.is_directory and any(event.src_path.endswith(ext) for ext in [".datasource", ".pipe", ".ndjson"]):
|
|
85
|
+
click.echo(FeedbackManager.highlight(message=f"\n\n⟲ Changes detected in {event.src_path}\n"))
|
|
42
86
|
try:
|
|
43
87
|
self.process([event.src_path])
|
|
44
88
|
except Exception as e:
|
|
@@ -48,6 +92,7 @@ class FileChangeHandler(FileSystemEventHandler):
|
|
|
48
92
|
def watch_files(
|
|
49
93
|
filenames: List[str],
|
|
50
94
|
process: Union[Callable[[List[str]], None], Callable[[List[str]], Awaitable[None]]],
|
|
95
|
+
shell: BuildShell,
|
|
51
96
|
) -> None:
|
|
52
97
|
# Handle both sync and async process functions
|
|
53
98
|
async def process_wrapper(files: List[str]) -> None:
|
|
@@ -61,8 +106,9 @@ def watch_files(
|
|
|
61
106
|
elapsed_time = time_end - time_start
|
|
62
107
|
click.echo(
|
|
63
108
|
FeedbackManager.success(message="\n✓ ")
|
|
64
|
-
+ FeedbackManager.gray(message=f"Rebuild completed in {elapsed_time:.1f}s
|
|
109
|
+
+ FeedbackManager.gray(message=f"Rebuild completed in {elapsed_time:.1f}s")
|
|
65
110
|
)
|
|
111
|
+
shell.reprint_prompt()
|
|
66
112
|
|
|
67
113
|
event_handler = FileChangeHandler(filenames, lambda f: asyncio.run(process_wrapper(f)))
|
|
68
114
|
observer = Observer()
|
|
@@ -113,7 +159,9 @@ async def build(
|
|
|
113
159
|
ignore_sql_errors = FeatureFlags.ignore_sql_errors()
|
|
114
160
|
context.disable_template_security_validation.set(True)
|
|
115
161
|
is_internal = has_internal_datafiles(folder)
|
|
116
|
-
|
|
162
|
+
|
|
163
|
+
folder_path = os.path.abspath(folder)
|
|
164
|
+
tb_client = await get_tinybird_local_client(folder_path)
|
|
117
165
|
|
|
118
166
|
def check_filenames(filenames: List[str]):
|
|
119
167
|
parser_matrix = {".pipe": parse_pipe, ".datasource": parse_datasource}
|
|
@@ -134,29 +182,53 @@ async def build(
|
|
|
134
182
|
parser(filename)
|
|
135
183
|
|
|
136
184
|
async def process(filenames: List[str], watch: bool = False, only_pipes: bool = False):
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
185
|
+
datafiles = [f for f in filenames if f.endswith(".datasource") or f.endswith(".pipe")]
|
|
186
|
+
if len(datafiles) > 0:
|
|
187
|
+
check_filenames(filenames=datafiles)
|
|
188
|
+
await folder_build(
|
|
189
|
+
tb_client,
|
|
190
|
+
filenames=datafiles,
|
|
191
|
+
ignore_sql_errors=ignore_sql_errors,
|
|
192
|
+
is_internal=is_internal,
|
|
193
|
+
only_pipes=only_pipes,
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
filename = filenames[0]
|
|
197
|
+
if filename.endswith(".ndjson"):
|
|
198
|
+
fixture_path = Path(filename)
|
|
199
|
+
name = "_".join(fixture_path.stem.split("_")[:-1])
|
|
200
|
+
ds_path = Path(folder) / "datasources" / f"{name}.datasource"
|
|
201
|
+
if ds_path.exists():
|
|
202
|
+
await append_datasource({}, tb_client, name, str(fixture_path), silent=True)
|
|
145
203
|
|
|
146
204
|
if watch:
|
|
147
|
-
filename
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
205
|
+
if filename.endswith(".datasource"):
|
|
206
|
+
ds_path = Path(filename)
|
|
207
|
+
name = build_fixture_name(filename, ds_path.stem, ds_path.read_text())
|
|
208
|
+
fixture_path = get_fixture_dir() / f"{name}.ndjson"
|
|
209
|
+
if fixture_path.exists():
|
|
210
|
+
await append_datasource({}, tb_client, ds_path.stem, str(fixture_path), silent=True)
|
|
211
|
+
if not filename.endswith(".ndjson"):
|
|
212
|
+
await build_and_print_resource(tb_client, filename)
|
|
213
|
+
|
|
214
|
+
datafiles = get_project_filenames(folder)
|
|
215
|
+
fixtures = get_project_fixtures(folder)
|
|
216
|
+
filenames = datafiles + fixtures
|
|
152
217
|
|
|
153
218
|
async def build_once(filenames: List[str]):
|
|
154
219
|
try:
|
|
155
|
-
click.echo("⚡ Building project
|
|
220
|
+
click.echo("⚡ Building project...\n")
|
|
156
221
|
time_start = time.time()
|
|
157
222
|
await process(filenames=filenames, watch=False, only_pipes=skip_datasources)
|
|
158
223
|
time_end = time.time()
|
|
159
224
|
elapsed_time = time_end - time_start
|
|
225
|
+
for filename in filenames:
|
|
226
|
+
if filename.endswith(".datasource"):
|
|
227
|
+
ds_path = Path(filename)
|
|
228
|
+
name = build_fixture_name(filename, ds_path.stem, ds_path.read_text())
|
|
229
|
+
fixture_path = get_fixture_dir() / f"{name}.ndjson"
|
|
230
|
+
if fixture_path.exists():
|
|
231
|
+
await append_datasource({}, tb_client, ds_path.stem, str(fixture_path), silent=True)
|
|
160
232
|
click.echo(FeedbackManager.success(message=f"\n✓ Build completed in {elapsed_time:.1f}s\n"))
|
|
161
233
|
except Exception as e:
|
|
162
234
|
click.echo(FeedbackManager.error(message=str(e)))
|
|
@@ -164,31 +236,21 @@ async def build(
|
|
|
164
236
|
await build_once(filenames)
|
|
165
237
|
|
|
166
238
|
if watch:
|
|
167
|
-
|
|
168
|
-
|
|
239
|
+
shell = BuildShell(folder=folder)
|
|
240
|
+
click.echo(FeedbackManager.highlight(message="◎ Watching for changes..."))
|
|
241
|
+
watcher_thread = threading.Thread(target=watch_files, args=(filenames, process, shell), daemon=True)
|
|
169
242
|
watcher_thread.start()
|
|
243
|
+
shell.cmdloop()
|
|
170
244
|
|
|
171
|
-
# Main CLI loop
|
|
172
|
-
while True:
|
|
173
|
-
user_input = click.prompt("", prompt_suffix="")
|
|
174
|
-
if user_input.lower() == "exit":
|
|
175
|
-
break
|
|
176
|
-
|
|
177
|
-
if "tb build" in user_input:
|
|
178
|
-
click.echo(FeedbackManager.error(message="Build command is already running"))
|
|
179
|
-
else:
|
|
180
|
-
# Process the user command
|
|
181
|
-
await sync_to_async(subprocess.run, thread_sensitive=True)(user_input, shell=True, text=True)
|
|
182
|
-
|
|
183
|
-
click.echo(FeedbackManager.highlight(message="\n◎ Watching for changes...\n"))
|
|
184
245
|
|
|
185
|
-
|
|
186
|
-
async def build_and_print_pipe(tb_client: TinyB, filename: str):
|
|
246
|
+
async def build_and_print_resource(tb_client: TinyB, filename: str):
|
|
187
247
|
rebuild_colors = [bcolors.FAIL, bcolors.OKBLUE, bcolors.WARNING, bcolors.OKGREEN, bcolors.HEADER]
|
|
188
248
|
rebuild_index = random.randint(0, len(rebuild_colors) - 1)
|
|
189
249
|
rebuild_color = rebuild_colors[rebuild_index % len(rebuild_colors)]
|
|
190
|
-
|
|
191
|
-
|
|
250
|
+
resource_path = Path(filename)
|
|
251
|
+
name = resource_path.stem
|
|
252
|
+
pipeline = name if filename.endswith(".pipe") else None
|
|
253
|
+
res = await tb_client.query(f"SELECT * FROM {name} FORMAT JSON", pipeline=pipeline)
|
|
192
254
|
data = []
|
|
193
255
|
limit = 5
|
|
194
256
|
for d in res["data"][:5]:
|
|
@@ -197,7 +259,6 @@ async def build_and_print_pipe(tb_client: TinyB, filename: str):
|
|
|
197
259
|
row_count = res.get("rows", 0)
|
|
198
260
|
stats = res.get("statistics", {})
|
|
199
261
|
elapsed = stats.get("elapsed", 0)
|
|
200
|
-
node_name = "endpoint"
|
|
201
262
|
cols = len(meta)
|
|
202
263
|
try:
|
|
203
264
|
|
|
@@ -207,7 +268,7 @@ async def build_and_print_pipe(tb_client: TinyB, filename: str):
|
|
|
207
268
|
table = format_table(data, meta)
|
|
208
269
|
colored_char = print_message("│", rebuild_color)
|
|
209
270
|
table_with_marker = "\n".join(f"{colored_char} {line}" for line in table.split("\n"))
|
|
210
|
-
click.echo(f"\n{colored_char} {print_message('⚡', rebuild_color)} Running {
|
|
271
|
+
click.echo(f"\n{colored_char} {print_message('⚡', rebuild_color)} Running {name}")
|
|
211
272
|
click.echo(colored_char)
|
|
212
273
|
click.echo(table_with_marker)
|
|
213
274
|
click.echo(colored_char)
|
|
@@ -225,3 +286,24 @@ async def build_and_print_pipe(tb_client: TinyB, filename: str):
|
|
|
225
286
|
click.echo("------------")
|
|
226
287
|
else:
|
|
227
288
|
raise exc
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
async def append_datasource(
|
|
292
|
+
ctx: click.Context,
|
|
293
|
+
tb_client: TinyB,
|
|
294
|
+
datasource_name: str,
|
|
295
|
+
url: str,
|
|
296
|
+
silent: bool = False,
|
|
297
|
+
):
|
|
298
|
+
await push_data(
|
|
299
|
+
ctx,
|
|
300
|
+
tb_client,
|
|
301
|
+
datasource_name,
|
|
302
|
+
url,
|
|
303
|
+
connector=None,
|
|
304
|
+
sql=None,
|
|
305
|
+
mode="append",
|
|
306
|
+
ignore_empty=False,
|
|
307
|
+
concurrency=1,
|
|
308
|
+
silent=silent,
|
|
309
|
+
)
|
|
@@ -240,7 +240,6 @@ class GitLabCICDGenerator(CICDGeneratorBase):
|
|
|
240
240
|
template=GITLAB_CI_YML,
|
|
241
241
|
file_name="tinybird-ci.yml",
|
|
242
242
|
dir_path=".gitlab/tinybird",
|
|
243
|
-
warning_message="Make sure to import the file in your .gitlab-ci.yml file, e.g., `include: '.gitlab/tinybird/*.yml'`.",
|
|
244
243
|
),
|
|
245
244
|
]
|
|
246
245
|
|
|
@@ -158,9 +158,6 @@ async def cli(
|
|
|
158
158
|
|
|
159
159
|
latest_version = await CheckPypi().get_latest_version()
|
|
160
160
|
|
|
161
|
-
if "x.y.z" in CURRENT_VERSION:
|
|
162
|
-
click.echo(FeedbackManager.warning_development_cli())
|
|
163
|
-
|
|
164
161
|
if "x.y.z" not in CURRENT_VERSION and latest_version != CURRENT_VERSION:
|
|
165
162
|
click.echo(FeedbackManager.warning_update_version(latest_version=latest_version))
|
|
166
163
|
click.echo(FeedbackManager.warning_current_version(current_version=CURRENT_VERSION))
|
|
@@ -11,6 +11,7 @@ import os
|
|
|
11
11
|
import re
|
|
12
12
|
import socket
|
|
13
13
|
import sys
|
|
14
|
+
import time
|
|
14
15
|
import uuid
|
|
15
16
|
from contextlib import closing
|
|
16
17
|
from copy import deepcopy
|
|
@@ -166,12 +167,12 @@ def generate_datafile(
|
|
|
166
167
|
data: Optional[bytes],
|
|
167
168
|
force: Optional[bool] = False,
|
|
168
169
|
_format: Optional[str] = "csv",
|
|
169
|
-
|
|
170
|
+
folder: Optional[str] = None,
|
|
170
171
|
):
|
|
171
172
|
p = Path(filename)
|
|
172
173
|
base = Path("datasources")
|
|
173
|
-
if
|
|
174
|
-
base = Path(
|
|
174
|
+
if folder:
|
|
175
|
+
base = Path(folder) / base
|
|
175
176
|
datasource_name = normalize_datasource_name(p.stem)
|
|
176
177
|
if not base.exists():
|
|
177
178
|
base = Path()
|
|
@@ -179,7 +180,7 @@ def generate_datafile(
|
|
|
179
180
|
if not f.exists() or force:
|
|
180
181
|
with open(f"{f}", "w") as ds_file:
|
|
181
182
|
ds_file.write(datafile)
|
|
182
|
-
click.echo(FeedbackManager.info_file_created(file=f))
|
|
183
|
+
click.echo(FeedbackManager.info_file_created(file=str(f)))
|
|
183
184
|
|
|
184
185
|
if data and (base / "fixtures").exists():
|
|
185
186
|
# Generating a fixture for Parquet files is not so trivial, since Parquet format
|
|
@@ -1078,6 +1079,7 @@ async def push_data(
|
|
|
1078
1079
|
replace_options=None,
|
|
1079
1080
|
ignore_empty: bool = False,
|
|
1080
1081
|
concurrency: int = 1,
|
|
1082
|
+
silent: bool = False,
|
|
1081
1083
|
):
|
|
1082
1084
|
if url and type(url) is tuple:
|
|
1083
1085
|
url = url[0]
|
|
@@ -1088,7 +1090,8 @@ async def push_data(
|
|
|
1088
1090
|
raise CLIException(FeedbackManager.error_connector_not_configured(connector=connector))
|
|
1089
1091
|
else:
|
|
1090
1092
|
_connector: "Connector" = ctx.obj[connector]
|
|
1091
|
-
|
|
1093
|
+
if not silent:
|
|
1094
|
+
click.echo(FeedbackManager.info_starting_export_process(connector=connector))
|
|
1092
1095
|
try:
|
|
1093
1096
|
url = _connector.export_to_gcs(sql, datasource_name, mode)
|
|
1094
1097
|
except ConnectorNothingToLoad as e:
|
|
@@ -1116,7 +1119,8 @@ async def push_data(
|
|
|
1116
1119
|
cb.First = True # type: ignore[attr-defined]
|
|
1117
1120
|
cb.prev_done = 0 # type: ignore[attr-defined]
|
|
1118
1121
|
|
|
1119
|
-
|
|
1122
|
+
if not silent:
|
|
1123
|
+
click.echo(FeedbackManager.gray(message=f"\nImporting data to {datasource_name}..."))
|
|
1120
1124
|
|
|
1121
1125
|
if isinstance(url, list):
|
|
1122
1126
|
urls = url
|
|
@@ -1187,15 +1191,14 @@ async def push_data(
|
|
|
1187
1191
|
except Exception as e:
|
|
1188
1192
|
raise CLIException(FeedbackManager.error_exception(error=e))
|
|
1189
1193
|
else:
|
|
1190
|
-
if
|
|
1191
|
-
|
|
1194
|
+
if not silent:
|
|
1195
|
+
if mode == "append" and parser and parser != "clickhouse":
|
|
1196
|
+
click.echo(FeedbackManager.success_appended_rows(appended_rows=appended_rows))
|
|
1192
1197
|
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
else:
|
|
1196
|
-
click.echo(FeedbackManager.highlight(message="» 2.57m rows x 9 cols in 852.04ms"))
|
|
1198
|
+
if mode == "replace":
|
|
1199
|
+
click.echo(FeedbackManager.success_replaced_datasource(datasource=datasource_name))
|
|
1197
1200
|
|
|
1198
|
-
|
|
1201
|
+
click.echo(FeedbackManager.success_progress_blocks())
|
|
1199
1202
|
|
|
1200
1203
|
finally:
|
|
1201
1204
|
try:
|
|
@@ -2108,3 +2111,22 @@ def get_ca_pem_content(ca_pem: Optional[str], filename: Optional[str] = None) ->
|
|
|
2108
2111
|
|
|
2109
2112
|
requests_get = sync_to_async(requests.get, thread_sensitive=False)
|
|
2110
2113
|
requests_delete = sync_to_async(requests.delete, thread_sensitive=False)
|
|
2114
|
+
|
|
2115
|
+
|
|
2116
|
+
def format_data_to_ndjson(data: List[Dict[str, Any]]) -> str:
|
|
2117
|
+
return "\n".join([json.dumps(row) for row in data])
|
|
2118
|
+
|
|
2119
|
+
|
|
2120
|
+
async def send_batch_events(
|
|
2121
|
+
client: TinyB, datasource_name: str, data: List[Dict[str, Any]], batch_size: int = 10
|
|
2122
|
+
) -> None:
|
|
2123
|
+
rows = len(data)
|
|
2124
|
+
time_start = time.time()
|
|
2125
|
+
for i in range(0, rows, batch_size):
|
|
2126
|
+
batch = data[i : i + batch_size]
|
|
2127
|
+
ndjson_data = format_data_to_ndjson(batch)
|
|
2128
|
+
await client.datasource_events(datasource_name, ndjson_data)
|
|
2129
|
+
time_end = time.time()
|
|
2130
|
+
elapsed_time = time_end - time_start
|
|
2131
|
+
cols = len(data[0].keys()) if len(data) > 0 else 0
|
|
2132
|
+
click.echo(FeedbackManager.highlight(message=f"» {rows} rows x {cols} cols in {elapsed_time:.1f}s"))
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
from os import getcwd
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
from click import Context
|
|
9
|
+
|
|
10
|
+
from tinybird.client import TinyB
|
|
11
|
+
from tinybird.feedback_manager import FeedbackManager
|
|
12
|
+
from tinybird.tb.modules.cicd import init_cicd
|
|
13
|
+
from tinybird.tb.modules.cli import cli
|
|
14
|
+
from tinybird.tb.modules.common import _generate_datafile, coro, generate_datafile
|
|
15
|
+
from tinybird.tb.modules.config import CLIConfig
|
|
16
|
+
from tinybird.tb.modules.datafile.fixture import build_fixture_name, persist_fixture
|
|
17
|
+
from tinybird.tb.modules.exceptions import CLIException
|
|
18
|
+
from tinybird.tb.modules.llm import LLM
|
|
19
|
+
from tinybird.tb.modules.local import get_tinybird_local_client
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@cli.command()
|
|
23
|
+
@click.option(
|
|
24
|
+
"--data",
|
|
25
|
+
type=click.Path(exists=True),
|
|
26
|
+
default=None,
|
|
27
|
+
help="Initial data to be used to create the project",
|
|
28
|
+
)
|
|
29
|
+
@click.option(
|
|
30
|
+
"--prompt",
|
|
31
|
+
type=str,
|
|
32
|
+
default=None,
|
|
33
|
+
help="Prompt to be used to create the project",
|
|
34
|
+
)
|
|
35
|
+
@click.option(
|
|
36
|
+
"--folder",
|
|
37
|
+
default=None,
|
|
38
|
+
type=click.Path(exists=True, file_okay=False),
|
|
39
|
+
help="Folder where datafiles will be placed",
|
|
40
|
+
)
|
|
41
|
+
@click.option("--rows", type=int, default=10, help="Number of events to send")
|
|
42
|
+
@click.pass_context
|
|
43
|
+
@coro
|
|
44
|
+
async def create(
|
|
45
|
+
ctx: Context,
|
|
46
|
+
data: Optional[str],
|
|
47
|
+
prompt: Optional[str],
|
|
48
|
+
folder: Optional[str],
|
|
49
|
+
rows: int,
|
|
50
|
+
) -> None:
|
|
51
|
+
"""Initialize a new project."""
|
|
52
|
+
folder = folder or getcwd()
|
|
53
|
+
try:
|
|
54
|
+
tb_client = await get_tinybird_local_client(os.path.abspath(folder))
|
|
55
|
+
click.echo(FeedbackManager.gray(message="Creating new project structure..."))
|
|
56
|
+
await project_create(tb_client, data, prompt, folder)
|
|
57
|
+
click.echo(FeedbackManager.success(message="✓ Scaffolding completed!\n"))
|
|
58
|
+
|
|
59
|
+
click.echo(FeedbackManager.gray(message="\nCreating CI/CD files for GitHub and GitLab..."))
|
|
60
|
+
init_git(folder)
|
|
61
|
+
await init_cicd(data_project_dir=os.path.relpath(folder))
|
|
62
|
+
click.echo(FeedbackManager.success(message="✓ Done!\n"))
|
|
63
|
+
|
|
64
|
+
click.echo(FeedbackManager.gray(message="Building fixtures..."))
|
|
65
|
+
|
|
66
|
+
if data:
|
|
67
|
+
ds_name = os.path.basename(data.split(".")[0])
|
|
68
|
+
data_content = Path(data).read_text()
|
|
69
|
+
datasource_path = Path(folder) / "datasources" / f"{ds_name}.datasource"
|
|
70
|
+
fixture_name = build_fixture_name(datasource_path.absolute(), ds_name, datasource_path.read_text())
|
|
71
|
+
click.echo(FeedbackManager.info(message=f"✓ /fixtures/{ds_name}"))
|
|
72
|
+
persist_fixture(fixture_name, data_content)
|
|
73
|
+
elif prompt:
|
|
74
|
+
datasource_files = [f for f in os.listdir(Path(folder) / "datasources") if f.endswith(".datasource")]
|
|
75
|
+
for datasource_file in datasource_files:
|
|
76
|
+
datasource_path = Path(folder) / "datasources" / datasource_file
|
|
77
|
+
llm_config = CLIConfig.get_llm_config()
|
|
78
|
+
llm = LLM(key=llm_config["api_key"])
|
|
79
|
+
datasource_name = datasource_path.stem
|
|
80
|
+
datasource_content = datasource_path.read_text()
|
|
81
|
+
has_json_path = "`json:" in datasource_content
|
|
82
|
+
if has_json_path:
|
|
83
|
+
sql = await llm.generate_sql_sample_data(tb_client, datasource_name, datasource_content, rows)
|
|
84
|
+
result = await tb_client.query(f"{sql} FORMAT JSON")
|
|
85
|
+
data = result.get("data", [])
|
|
86
|
+
fixture_name = build_fixture_name(datasource_path.absolute(), datasource_name, datasource_content)
|
|
87
|
+
persist_fixture(fixture_name, data)
|
|
88
|
+
click.echo(FeedbackManager.info(message=f"✓ /fixtures/{datasource_name}"))
|
|
89
|
+
|
|
90
|
+
click.echo(FeedbackManager.success(message="✓ Done!\n"))
|
|
91
|
+
|
|
92
|
+
click.echo(FeedbackManager.success(message="✓ Tinybird local is ready!"))
|
|
93
|
+
except Exception as e:
|
|
94
|
+
click.echo(FeedbackManager.error(message=f"Error: {str(e)}"))
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
async def project_create(
|
|
98
|
+
client: TinyB,
|
|
99
|
+
data: Optional[str],
|
|
100
|
+
prompt: Optional[str],
|
|
101
|
+
folder: str,
|
|
102
|
+
):
|
|
103
|
+
project_paths = ["datasources", "endpoints", "materializations", "copies", "sinks", "fixtures"]
|
|
104
|
+
force = True
|
|
105
|
+
for x in project_paths:
|
|
106
|
+
try:
|
|
107
|
+
f = Path(folder) / x
|
|
108
|
+
f.mkdir()
|
|
109
|
+
except FileExistsError:
|
|
110
|
+
pass
|
|
111
|
+
click.echo(FeedbackManager.info_path_created(path=x))
|
|
112
|
+
|
|
113
|
+
if data:
|
|
114
|
+
path = Path(folder) / data
|
|
115
|
+
format = path.suffix.lstrip(".")
|
|
116
|
+
try:
|
|
117
|
+
await _generate_datafile(str(path), client, format=format, force=force)
|
|
118
|
+
except Exception as e:
|
|
119
|
+
click.echo(FeedbackManager.error(message=f"Ersssssror: {str(e)}"))
|
|
120
|
+
name = data.split(".")[0]
|
|
121
|
+
generate_pipe_file(
|
|
122
|
+
f"{name}_endpoint",
|
|
123
|
+
f"""
|
|
124
|
+
NODE endpoint
|
|
125
|
+
SQL >
|
|
126
|
+
SELECT * from {name}
|
|
127
|
+
TYPE ENDPOINT
|
|
128
|
+
""",
|
|
129
|
+
folder,
|
|
130
|
+
)
|
|
131
|
+
elif prompt:
|
|
132
|
+
try:
|
|
133
|
+
llm_config = CLIConfig.get_llm_config()
|
|
134
|
+
llm = LLM(key=llm_config["api_key"])
|
|
135
|
+
result = await llm.create_project(prompt)
|
|
136
|
+
for ds in result.datasources:
|
|
137
|
+
content = ds.content.replace("```", "")
|
|
138
|
+
generate_datafile(
|
|
139
|
+
content, filename=f"{ds.name}.datasource", data=None, _format="ndjson", force=force, folder=folder
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
for pipe in result.pipes:
|
|
143
|
+
content = pipe.content.replace("```", "")
|
|
144
|
+
generate_pipe_file(pipe.name, content, folder)
|
|
145
|
+
except Exception as e:
|
|
146
|
+
click.echo(FeedbackManager.error(message=f"Error: {str(e)}"))
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def init_git(folder: str):
|
|
150
|
+
try:
|
|
151
|
+
path = Path(folder)
|
|
152
|
+
gitignore_file = path / ".gitignore"
|
|
153
|
+
git_folder = path / ".git"
|
|
154
|
+
if not git_folder.exists():
|
|
155
|
+
subprocess.run(["git", "init"], cwd=path, check=True, capture_output=True)
|
|
156
|
+
|
|
157
|
+
if gitignore_file.exists():
|
|
158
|
+
content = gitignore_file.read_text()
|
|
159
|
+
if ".tinyb" not in content:
|
|
160
|
+
gitignore_file.write_text(content + "\n.tinyb\n")
|
|
161
|
+
else:
|
|
162
|
+
gitignore_file.write_text(".tinyb\n")
|
|
163
|
+
|
|
164
|
+
click.echo(FeedbackManager.info_file_created(file=".gitignore"))
|
|
165
|
+
except Exception as e:
|
|
166
|
+
raise CLIException(f"Error initializing Git: {e}")
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def generate_pipe_file(name: str, content: str, folder: str):
|
|
170
|
+
base = Path(folder) / "endpoints"
|
|
171
|
+
if not base.exists():
|
|
172
|
+
base = Path()
|
|
173
|
+
f = base / (f"{name}.pipe")
|
|
174
|
+
with open(f"{f}", "w") as file:
|
|
175
|
+
file.write(content)
|
|
176
|
+
click.echo(FeedbackManager.info_file_created(file=f.relative_to(folder)))
|
|
@@ -32,7 +32,6 @@ from tinybird.tb.modules.datafile.common import (
|
|
|
32
32
|
INTERNAL_TABLES,
|
|
33
33
|
ON_DEMAND,
|
|
34
34
|
PREVIEW_CONNECTOR_SERVICES,
|
|
35
|
-
TB_LOCAL_WORKSPACE_NAME,
|
|
36
35
|
CopyModes,
|
|
37
36
|
CopyParameters,
|
|
38
37
|
DataFileExtensions,
|
|
@@ -58,6 +57,7 @@ async def folder_build(
|
|
|
58
57
|
only_pipes: bool = False,
|
|
59
58
|
is_vendor: bool = False,
|
|
60
59
|
current_ws: Optional[Dict[str, Any]] = None,
|
|
60
|
+
local_ws: Optional[Dict[str, Any]] = None,
|
|
61
61
|
workspaces: Optional[List[Dict[str, Any]]] = None,
|
|
62
62
|
):
|
|
63
63
|
if only_pipes:
|
|
@@ -117,8 +117,9 @@ async def folder_build(
|
|
|
117
117
|
user_client.token = user_token
|
|
118
118
|
|
|
119
119
|
vendor_workspaces = []
|
|
120
|
-
|
|
120
|
+
|
|
121
121
|
if vendor_path.exists() and not is_vendor:
|
|
122
|
+
user_workspaces = await user_client.user_workspaces()
|
|
122
123
|
for x in vendor_path.iterdir():
|
|
123
124
|
if x.is_dir() and x.name not in existing_workspaces:
|
|
124
125
|
if user_token:
|
|
@@ -133,15 +134,23 @@ async def folder_build(
|
|
|
133
134
|
workspace_lib_paths.append((x.name, x))
|
|
134
135
|
|
|
135
136
|
workspaces: List[Dict[str, Any]] = (await user_client.user_workspaces()).get("workspaces", [])
|
|
136
|
-
|
|
137
|
+
|
|
138
|
+
if not is_vendor:
|
|
139
|
+
local_workspace = await tb_client.workspace_info()
|
|
140
|
+
local_ws_id = local_workspace.get("id")
|
|
141
|
+
local_ws = next((ws for ws in workspaces if ws["id"] == local_ws_id), {})
|
|
142
|
+
|
|
137
143
|
current_ws: Dict[str, Any] = current_ws or local_ws
|
|
144
|
+
|
|
138
145
|
for vendor_ws in [ws for ws in workspaces if ws["name"] in [ws["name"] for ws in vendor_workspaces]]:
|
|
139
146
|
ws_client = deepcopy(tb_client)
|
|
140
147
|
ws_client.token = vendor_ws["token"]
|
|
141
148
|
shared_ws_path = Path(folder) / "vendor" / vendor_ws["name"]
|
|
142
149
|
|
|
143
150
|
if shared_ws_path.exists() and not is_vendor:
|
|
144
|
-
await folder_build(
|
|
151
|
+
await folder_build(
|
|
152
|
+
ws_client, folder=shared_ws_path.as_posix(), is_vendor=True, current_ws=vendor_ws, local_ws=local_ws
|
|
153
|
+
)
|
|
145
154
|
|
|
146
155
|
datasources: List[Dict[str, Any]] = await tb_client.datasources()
|
|
147
156
|
pipes: List[Dict[str, Any]] = await tb_client.pipes(dependencies=True)
|
|
@@ -256,7 +256,6 @@ async def new_pipe(
|
|
|
256
256
|
raise click.ClickException(FeedbackManager.error_creating_pipe(error=e))
|
|
257
257
|
else:
|
|
258
258
|
token_name = tk["token_name"]
|
|
259
|
-
click.echo(FeedbackManager.info_create_not_found_token(token=token_name))
|
|
260
259
|
try:
|
|
261
260
|
r = await tb_client.create_token(
|
|
262
261
|
token_name, [f"PIPES:{tk['permissions']}:{p['name']}"], "P", p["name"]
|
|
@@ -662,6 +662,18 @@ def get_project_filenames(folder: str, with_vendor=False) -> List[str]:
|
|
|
662
662
|
]
|
|
663
663
|
if with_vendor:
|
|
664
664
|
folders.append(f"{folder}/vendor/**/**/*.datasource")
|
|
665
|
+
|
|
666
|
+
filenames: List[str] = []
|
|
667
|
+
for x in folders:
|
|
668
|
+
filenames += glob.glob(x)
|
|
669
|
+
return filenames
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
def get_project_fixtures(folder: str) -> List[str]:
|
|
673
|
+
folders: List[str] = [
|
|
674
|
+
f"{folder}/fixtures/*.ndjson",
|
|
675
|
+
f"{folder}/fixtures/*.csv",
|
|
676
|
+
]
|
|
665
677
|
filenames: List[str] = []
|
|
666
678
|
for x in folders:
|
|
667
679
|
filenames += glob.glob(x)
|