tinybird 0.0.1.dev56__tar.gz → 0.0.1.dev58__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.dev56 → tinybird-0.0.1.dev58}/PKG-INFO +1 -1
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/__cli__.py +2 -2
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/build.py +22 -20
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/create.py +3 -9
- tinybird-0.0.1.dev58/tinybird/tb/modules/datafile/fixture.py +30 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/deployment.py +59 -7
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/login.py +1 -1
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/mock.py +3 -4
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/project.py +6 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/token.py +2 -1
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/watch.py +4 -116
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird.egg-info/PKG-INFO +1 -1
- tinybird-0.0.1.dev56/tinybird/tb/modules/datafile/fixture.py +0 -57
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/setup.cfg +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/__cli__.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/ch_utils/constants.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/ch_utils/engine.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/check_pypi.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/client.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/config.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/connectors.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/context.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/datafile.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/datatypes.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/feedback_manager.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/git_settings.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/prompts.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/sql.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/sql_template.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/sql_template_fmt.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/sql_toolset.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/syncasync.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/cli.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/auth.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/cicd.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/cli.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/common.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/config.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/copy.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/datafile/build.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/datafile/build_common.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/datafile/build_pipe.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/datafile/common.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/datafile/diff.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/datafile/exceptions.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/datafile/format_common.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/datafile/format_pipe.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/datafile/parse_datasource.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/datafile/parse_pipe.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/datafile/pipe_checker.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/datafile/pull.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/datasource.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/endpoint.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/exceptions.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/feedback_manager.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/fmt.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/job.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/llm.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/llm_utils.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/local.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/local_common.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/materialization.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/pipe.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/regions.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/shell.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/table.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/tag.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/telemetry.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/test.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/tinyunit/tinyunit.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/tinyunit/tinyunit_lib.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/workspace.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/workspace_members.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb_cli.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb_cli_modules/auth.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb_cli_modules/branch.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb_cli_modules/cicd.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb_cli_modules/cli.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb_cli_modules/common.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb_cli_modules/config.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb_cli_modules/connection.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb_cli_modules/datasource.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb_cli_modules/exceptions.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb_cli_modules/fmt.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb_cli_modules/job.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb_cli_modules/pipe.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb_cli_modules/regions.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb_cli_modules/tag.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb_cli_modules/telemetry.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb_cli_modules/test.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb_cli_modules/workspace.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb_cli_modules/workspace_members.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tornado_template.py +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird.egg-info/SOURCES.txt +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird.egg-info/dependency_links.txt +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird.egg-info/entry_points.txt +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird.egg-info/requires.txt +0 -0
- {tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird.egg-info/top_level.txt +0 -0
|
@@ -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.
|
|
8
|
-
__revision__ = '
|
|
7
|
+
__version__ = '0.0.1.dev58'
|
|
8
|
+
__revision__ = '35a8d6e'
|
|
@@ -14,7 +14,7 @@ from tinybird.client import TinyB
|
|
|
14
14
|
from tinybird.tb.modules.cli import cli
|
|
15
15
|
from tinybird.tb.modules.common import push_data
|
|
16
16
|
from tinybird.tb.modules.datafile.build import folder_build
|
|
17
|
-
from tinybird.tb.modules.datafile.fixture import
|
|
17
|
+
from tinybird.tb.modules.datafile.fixture import get_fixture_dir
|
|
18
18
|
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
19
19
|
from tinybird.tb.modules.project import Project
|
|
20
20
|
from tinybird.tb.modules.shell import Shell, print_table_formatted
|
|
@@ -31,13 +31,18 @@ def build(ctx: click.Context, watch: bool) -> None:
|
|
|
31
31
|
project: Project = ctx.ensure_object(dict)["project"]
|
|
32
32
|
tb_client: TinyB = ctx.ensure_object(dict)["client"]
|
|
33
33
|
click.echo(FeedbackManager.highlight_building_project())
|
|
34
|
-
time_start = time.time()
|
|
35
34
|
|
|
36
|
-
def process(file_changed: Optional[str] = None, diff: Optional[str] = None) -> None:
|
|
35
|
+
def process(watch: bool, file_changed: Optional[str] = None, diff: Optional[str] = None) -> None:
|
|
36
|
+
time_start = time.time()
|
|
37
|
+
build_failed = False
|
|
37
38
|
if file_changed and file_changed.endswith(".ndjson"):
|
|
38
39
|
rebuild_fixture(project, tb_client, file_changed)
|
|
39
40
|
else:
|
|
40
|
-
|
|
41
|
+
try:
|
|
42
|
+
build_project(project, tb_client, file_changed)
|
|
43
|
+
except click.ClickException as e:
|
|
44
|
+
click.echo(e)
|
|
45
|
+
build_failed = True
|
|
41
46
|
try:
|
|
42
47
|
if file_changed:
|
|
43
48
|
asyncio.run(folder_build(project, tb_client, filenames=[file_changed]))
|
|
@@ -45,16 +50,18 @@ def build(ctx: click.Context, watch: bool) -> None:
|
|
|
45
50
|
except Exception:
|
|
46
51
|
pass
|
|
47
52
|
|
|
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
53
|
time_end = time.time()
|
|
56
54
|
elapsed_time = time_end - time_start
|
|
57
|
-
|
|
55
|
+
|
|
56
|
+
rebuild_str = "Rebuild" if watch else "Build"
|
|
57
|
+
if build_failed:
|
|
58
|
+
click.echo(FeedbackManager.error(message=f"\n✗ {rebuild_str} failed"))
|
|
59
|
+
if not watch:
|
|
60
|
+
sys.exit(1)
|
|
61
|
+
else:
|
|
62
|
+
click.echo(FeedbackManager.success(message=f"\n✓ {rebuild_str} completed in {elapsed_time:.1f}s"))
|
|
63
|
+
|
|
64
|
+
process(watch=watch)
|
|
58
65
|
|
|
59
66
|
if watch:
|
|
60
67
|
shell = Shell(project=project, tb_client=tb_client)
|
|
@@ -134,18 +141,13 @@ def build_project(project: Project, tb_client: TinyB, file_changed: Optional[str
|
|
|
134
141
|
if filename.endswith(".datasource"):
|
|
135
142
|
ds_path = Path(filename)
|
|
136
143
|
ds_name = ds_path.stem
|
|
137
|
-
name = build_fixture_name(filename, ds_name, ds_path.read_text())
|
|
138
144
|
fixture_folder = get_fixture_dir(project.folder)
|
|
139
|
-
fixture_path = fixture_folder / f"{
|
|
140
|
-
|
|
141
|
-
if not fixture_path.exists():
|
|
142
|
-
fixture_path = fixture_folder / f"{ds_name}.ndjson"
|
|
143
|
-
|
|
145
|
+
fixture_path = fixture_folder / f"{ds_name}.ndjson"
|
|
144
146
|
if fixture_path.exists():
|
|
145
147
|
append_fixture(tb_client, ds_name, str(fixture_path))
|
|
146
148
|
|
|
147
|
-
except Exception:
|
|
148
|
-
|
|
149
|
+
except Exception as e:
|
|
150
|
+
click.echo(FeedbackManager.error_exception(error=f"Error appending fixtures for '{ds_name}': {e}"))
|
|
149
151
|
|
|
150
152
|
feedback = result.get("feedback", [])
|
|
151
153
|
for f in feedback:
|
|
@@ -12,7 +12,7 @@ from tinybird.tb.modules.cicd import init_cicd
|
|
|
12
12
|
from tinybird.tb.modules.cli import cli
|
|
13
13
|
from tinybird.tb.modules.common import _generate_datafile, check_user_token_with_client, coro, generate_datafile
|
|
14
14
|
from tinybird.tb.modules.config import CLIConfig
|
|
15
|
-
from tinybird.tb.modules.datafile.fixture import
|
|
15
|
+
from tinybird.tb.modules.datafile.fixture import persist_fixture
|
|
16
16
|
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
|
|
@@ -108,11 +108,8 @@ async def create(
|
|
|
108
108
|
ds_name = os.path.basename(data.split(".")[0])
|
|
109
109
|
data_content = Path(data).read_text()
|
|
110
110
|
datasource_path = Path(folder) / "datasources" / f"{ds_name}.datasource"
|
|
111
|
-
fixture_name = build_fixture_name(
|
|
112
|
-
datasource_path.absolute().as_posix(), ds_name, datasource_path.read_text()
|
|
113
|
-
)
|
|
114
111
|
click.echo(FeedbackManager.info(message=f"✓ /fixtures/{ds_name}"))
|
|
115
|
-
persist_fixture(
|
|
112
|
+
persist_fixture(ds_name, data_content, folder)
|
|
116
113
|
elif prompt and user_token:
|
|
117
114
|
datasource_files = [f for f in os.listdir(Path(folder) / "datasources") if f.endswith(".datasource")]
|
|
118
115
|
for datasource_file in datasource_files:
|
|
@@ -128,11 +125,8 @@ async def create(
|
|
|
128
125
|
sql = sql.split("FORMAT")[0]
|
|
129
126
|
query_result = await local_client.query(f"{sql} FORMAT JSON")
|
|
130
127
|
data = query_result.get("data", [])
|
|
131
|
-
fixture_name = build_fixture_name(
|
|
132
|
-
datasource_path.absolute().as_posix(), datasource_name, datasource_content
|
|
133
|
-
)
|
|
134
128
|
if data:
|
|
135
|
-
persist_fixture(
|
|
129
|
+
persist_fixture(datasource_name, data, folder)
|
|
136
130
|
click.echo(FeedbackManager.info(message=f"✓ /fixtures/{datasource_name}"))
|
|
137
131
|
except Exception as e:
|
|
138
132
|
click.echo(FeedbackManager.error(message=f"Error: {str(e)}"))
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Any, Dict, List, Union
|
|
3
|
+
|
|
4
|
+
from tinybird.tb.modules.common import format_data_to_ndjson
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_fixture_dir(folder: str) -> Path:
|
|
8
|
+
fixture_dir = Path(folder) / "fixtures"
|
|
9
|
+
if not fixture_dir.exists():
|
|
10
|
+
fixture_dir.mkdir()
|
|
11
|
+
return fixture_dir
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def persist_fixture(fixture_name: str, data: Union[List[Dict[str, Any]], str], folder: str, format="ndjson") -> Path:
|
|
15
|
+
fixture_dir = get_fixture_dir(folder)
|
|
16
|
+
fixture_file = fixture_dir / f"{fixture_name}.{format}"
|
|
17
|
+
fixture_file.write_text(data if isinstance(data, str) else format_data_to_ndjson(data))
|
|
18
|
+
return fixture_file
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def load_fixture(
|
|
22
|
+
fixture_name: str,
|
|
23
|
+
folder: str,
|
|
24
|
+
format="ndjson",
|
|
25
|
+
) -> Union[Path, None]:
|
|
26
|
+
fixture_dir = get_fixture_dir(folder)
|
|
27
|
+
fixture_file = fixture_dir / f"{fixture_name}.{format}"
|
|
28
|
+
if not fixture_file.exists():
|
|
29
|
+
return None
|
|
30
|
+
return fixture_file
|
|
@@ -153,12 +153,18 @@ def deployment_group() -> None:
|
|
|
153
153
|
default=False,
|
|
154
154
|
help="Auto-promote the deployment. Only works if --wait is enabled. Disabled by default.",
|
|
155
155
|
)
|
|
156
|
+
@click.option(
|
|
157
|
+
"--check/--no-check",
|
|
158
|
+
is_flag=True,
|
|
159
|
+
default=False,
|
|
160
|
+
help="Validate the deployment before creating it. Disabled by default.",
|
|
161
|
+
)
|
|
156
162
|
@click.pass_context
|
|
157
|
-
def deployment_create(ctx: click.Context, wait: bool, auto: bool) -> None:
|
|
163
|
+
def deployment_create(ctx: click.Context, wait: bool, auto: bool, check: bool) -> None:
|
|
158
164
|
"""
|
|
159
165
|
Validate and deploy the project server side.
|
|
160
166
|
"""
|
|
161
|
-
create_deployment(ctx, wait, auto)
|
|
167
|
+
create_deployment(ctx, wait, auto, check)
|
|
162
168
|
|
|
163
169
|
|
|
164
170
|
@deployment_group.command(name="ls")
|
|
@@ -246,15 +252,21 @@ def deployment_rollback(ctx: click.Context, wait: bool) -> None:
|
|
|
246
252
|
default=False,
|
|
247
253
|
help="Auto-promote the deployment. Only works if --wait is enabled. Disabled by default.",
|
|
248
254
|
)
|
|
255
|
+
@click.option(
|
|
256
|
+
"--check",
|
|
257
|
+
is_flag=True,
|
|
258
|
+
default=False,
|
|
259
|
+
help="Validate the deployment before creating it. Disabled by default.",
|
|
260
|
+
)
|
|
249
261
|
@click.pass_context
|
|
250
|
-
def deploy(ctx: click.Context, wait: bool, auto: bool) -> None:
|
|
262
|
+
def deploy(ctx: click.Context, wait: bool, auto: bool, check: bool) -> None:
|
|
251
263
|
"""
|
|
252
264
|
Deploy the project.
|
|
253
265
|
"""
|
|
254
|
-
create_deployment(ctx, wait, auto)
|
|
266
|
+
create_deployment(ctx, wait, auto, check)
|
|
255
267
|
|
|
256
268
|
|
|
257
|
-
def create_deployment(ctx: click.Context, wait: bool, auto: bool) -> None:
|
|
269
|
+
def create_deployment(ctx: click.Context, wait: bool, auto: bool, check: Optional[bool] = None) -> None:
|
|
258
270
|
# TODO: This code is duplicated in build_server.py
|
|
259
271
|
# Should be refactored to be shared
|
|
260
272
|
MULTIPART_BOUNDARY_DATA_PROJECT = "data_project://"
|
|
@@ -281,11 +293,23 @@ def create_deployment(ctx: click.Context, wait: bool, auto: bool) -> None:
|
|
|
281
293
|
deployment = None
|
|
282
294
|
try:
|
|
283
295
|
HEADERS = {"Authorization": f"Bearer {TINYBIRD_API_KEY}"}
|
|
284
|
-
|
|
285
|
-
|
|
296
|
+
params = {}
|
|
297
|
+
if check:
|
|
298
|
+
click.echo(FeedbackManager.highlight(message="\n» Validating deployment...\n"))
|
|
299
|
+
params["check"] = "true"
|
|
300
|
+
r = requests.post(TINYBIRD_API_URL, files=files, headers=HEADERS, params=params)
|
|
286
301
|
result = r.json()
|
|
287
302
|
logging.debug(json.dumps(result, indent=2))
|
|
288
303
|
|
|
304
|
+
if check:
|
|
305
|
+
print_changes(result, project)
|
|
306
|
+
status = result.get("result")
|
|
307
|
+
if status == "success":
|
|
308
|
+
click.echo(FeedbackManager.success(message="\n✓ Deployment is valid"))
|
|
309
|
+
else:
|
|
310
|
+
click.echo(FeedbackManager.error(message="\n✗ Deployment is not valid"))
|
|
311
|
+
return
|
|
312
|
+
|
|
289
313
|
deploy_result = result.get("result")
|
|
290
314
|
if deploy_result == "success":
|
|
291
315
|
click.echo(FeedbackManager.success(message="Deployment submitted successfully"))
|
|
@@ -308,6 +332,8 @@ def create_deployment(ctx: click.Context, wait: bool, auto: bool) -> None:
|
|
|
308
332
|
click.echo(FeedbackManager.error(message=f"{deploy_error.get('error')}"))
|
|
309
333
|
else:
|
|
310
334
|
click.echo(FeedbackManager.error(message=f"Unknown build result {deploy_result}"))
|
|
335
|
+
except Exception as e:
|
|
336
|
+
click.echo(FeedbackManager.error_exception(error=e))
|
|
311
337
|
finally:
|
|
312
338
|
for fd in fds:
|
|
313
339
|
fd.close()
|
|
@@ -343,3 +369,29 @@ def create_deployment(ctx: click.Context, wait: bool, auto: bool) -> None:
|
|
|
343
369
|
|
|
344
370
|
if auto:
|
|
345
371
|
promote_deployment(client.host, HEADERS, wait=wait)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def print_changes(result: dict, project: Project) -> None:
|
|
375
|
+
deployment = result.get("deployment", {})
|
|
376
|
+
columns = ["status", "name", "path"]
|
|
377
|
+
resources: list[list[str]] = []
|
|
378
|
+
|
|
379
|
+
for ds in deployment.get("new_datasources", []):
|
|
380
|
+
resources.append(["new", ds, project.get_resource_path(ds, "datasource")])
|
|
381
|
+
|
|
382
|
+
for p in deployment.get("new_pipes", []):
|
|
383
|
+
resources.append(["new", p, project.get_resource_path(p, "pipe")])
|
|
384
|
+
|
|
385
|
+
for ds in deployment.get("changed_datasources", []):
|
|
386
|
+
resources.append(["modified", ds, project.get_resource_path(ds, "datasource")])
|
|
387
|
+
|
|
388
|
+
for p in deployment.get("changed_pipes", []):
|
|
389
|
+
resources.append(["modified", p, project.get_resource_path(p, "pipe")])
|
|
390
|
+
|
|
391
|
+
for ds in deployment.get("deleted_datasources", []):
|
|
392
|
+
resources.append(["deleted", ds, project.get_resource_path(ds, "datasource")])
|
|
393
|
+
|
|
394
|
+
for p in deployment.get("deleted_pipes", []):
|
|
395
|
+
resources.append(["deleted", p, project.get_resource_path(p, "pipe")])
|
|
396
|
+
|
|
397
|
+
echo_safe_humanfriendly_tables_format_smart_table(resources, column_names=columns)
|
|
@@ -10,7 +10,7 @@ from tinybird.prompts import mock_prompt
|
|
|
10
10
|
from tinybird.tb.modules.cli import cli
|
|
11
11
|
from tinybird.tb.modules.common import CLIException, check_user_token_with_client, coro
|
|
12
12
|
from tinybird.tb.modules.config import CLIConfig
|
|
13
|
-
from tinybird.tb.modules.datafile.fixture import
|
|
13
|
+
from tinybird.tb.modules.datafile.fixture import persist_fixture
|
|
14
14
|
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
15
15
|
from tinybird.tb.modules.llm import LLM
|
|
16
16
|
from tinybird.tb.modules.llm_utils import extract_xml
|
|
@@ -99,9 +99,8 @@ async def mock(ctx: click.Context, datasource: str, rows: int, prompt: str, skip
|
|
|
99
99
|
sql = extract_xml(response, "sql")
|
|
100
100
|
result = await tb_client.query(f"{sql} FORMAT JSON")
|
|
101
101
|
data = result.get("data", [])[:rows]
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
click.echo(FeedbackManager.info(message=f"✓ /fixtures/{fixture_name}.ndjson created"))
|
|
102
|
+
fixture_path = persist_fixture(datasource_name, data, folder)
|
|
103
|
+
click.echo(FeedbackManager.info(message=f"✓ /fixtures/{datasource_name}.ndjson created"))
|
|
105
104
|
|
|
106
105
|
if os.environ.get("TB_DEBUG", "") != "":
|
|
107
106
|
logging.debug(sql)
|
|
@@ -32,6 +32,12 @@ class Project:
|
|
|
32
32
|
project_files.append(project_file)
|
|
33
33
|
return project_files
|
|
34
34
|
|
|
35
|
+
def get_resource_path(self, resource_name: str, resource_type: str) -> Optional[str]:
|
|
36
|
+
full_path = next((p for p in self.get_project_files() if p.endswith(resource_name + f".{resource_type}")), "")
|
|
37
|
+
if not full_path:
|
|
38
|
+
return None
|
|
39
|
+
return Path(full_path).relative_to(self.path)
|
|
40
|
+
|
|
35
41
|
def get_vendor_files(self) -> List[str]:
|
|
36
42
|
vendor_files: List[str] = []
|
|
37
43
|
for project_file in glob.glob(f"{self.vendor_path}/**/*.datasource", recursive=True):
|
|
@@ -301,7 +301,8 @@ async def create_static_token(ctx, name: str):
|
|
|
301
301
|
if current_scope:
|
|
302
302
|
scopes.append(current_scope)
|
|
303
303
|
current_scope = {}
|
|
304
|
-
|
|
304
|
+
unsafe_scope = args[i + 1]
|
|
305
|
+
current_scope = {"scope": unsafe_scope.upper() if isinstance(unsafe_scope, str) else unsafe_scope}
|
|
305
306
|
i += 2
|
|
306
307
|
elif args[i] == "--resource":
|
|
307
308
|
if current_scope is None:
|
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
import asyncio
|
|
2
1
|
import os
|
|
3
2
|
import time
|
|
4
3
|
from pathlib import Path
|
|
5
|
-
from typing import Any, Callable,
|
|
4
|
+
from typing import Any, Callable, Optional, Union
|
|
6
5
|
|
|
7
6
|
import click
|
|
8
7
|
from watchdog.events import (
|
|
9
8
|
DirDeletedEvent,
|
|
10
|
-
DirMovedEvent,
|
|
11
9
|
FileDeletedEvent,
|
|
12
|
-
FileMovedEvent,
|
|
13
|
-
FileSystemEventHandler,
|
|
14
10
|
PatternMatchingEventHandler,
|
|
15
11
|
)
|
|
16
12
|
from watchdog.observers import Observer
|
|
@@ -21,113 +17,12 @@ from tinybird.tb.modules.project import Project
|
|
|
21
17
|
from tinybird.tb.modules.shell import Shell
|
|
22
18
|
|
|
23
19
|
|
|
24
|
-
class FileChangeHandler(FileSystemEventHandler):
|
|
25
|
-
def __init__(self, filenames: List[str], process: Callable[[List[str]], None], build_ok: bool):
|
|
26
|
-
self.unprocessed_filenames = [os.path.abspath(f) for f in filenames]
|
|
27
|
-
self.process = process
|
|
28
|
-
self.build_ok = build_ok
|
|
29
|
-
|
|
30
|
-
@property
|
|
31
|
-
def filenames(self) -> List[str]:
|
|
32
|
-
return [f for f in self.unprocessed_filenames if os.path.exists(f)]
|
|
33
|
-
|
|
34
|
-
def should_process(self, event: Any) -> Optional[str]:
|
|
35
|
-
if event.is_directory:
|
|
36
|
-
return None
|
|
37
|
-
|
|
38
|
-
def should_process_path(path: str) -> bool:
|
|
39
|
-
if not os.path.exists(path):
|
|
40
|
-
return False
|
|
41
|
-
is_vendor = "vendor/" in path
|
|
42
|
-
if is_vendor:
|
|
43
|
-
return False
|
|
44
|
-
return any(path.endswith(ext) for ext in [".datasource", ".pipe", ".ndjson"])
|
|
45
|
-
|
|
46
|
-
if should_process_path(event.src_path):
|
|
47
|
-
return event.src_path
|
|
48
|
-
|
|
49
|
-
if should_process_path(event.dest_path):
|
|
50
|
-
return event.dest_path
|
|
51
|
-
|
|
52
|
-
return None
|
|
53
|
-
|
|
54
|
-
def on_modified(self, event: Any) -> None:
|
|
55
|
-
if path := self.should_process(event):
|
|
56
|
-
filename = path.split("/")[-1]
|
|
57
|
-
click.echo(FeedbackManager.highlight(message=f"\n\n⟲ Changes detected in {filename}\n"))
|
|
58
|
-
try:
|
|
59
|
-
to_process = [path] if self.build_ok else self.filenames
|
|
60
|
-
self.process(to_process)
|
|
61
|
-
self.build_ok = True
|
|
62
|
-
except Exception as e:
|
|
63
|
-
click.echo(FeedbackManager.error_exception(error=e))
|
|
64
|
-
|
|
65
|
-
def on_moved(self, event: Union[DirMovedEvent, FileMovedEvent]) -> None:
|
|
66
|
-
if path := self.should_process(event):
|
|
67
|
-
is_new_file = False
|
|
68
|
-
if path not in self.unprocessed_filenames:
|
|
69
|
-
is_new_file = True
|
|
70
|
-
self.unprocessed_filenames.append(path)
|
|
71
|
-
|
|
72
|
-
filename = path.split("/")[-1]
|
|
73
|
-
if is_new_file:
|
|
74
|
-
click.echo(FeedbackManager.highlight(message=f"\n\n⟲ New file detected: {filename}\n"))
|
|
75
|
-
else:
|
|
76
|
-
click.echo(FeedbackManager.highlight(message=f"\n\n⟲ Changes detected in {filename}\n"))
|
|
77
|
-
try:
|
|
78
|
-
should_rebuild_all = is_new_file or not self.build_ok
|
|
79
|
-
to_process = self.filenames if should_rebuild_all else [path]
|
|
80
|
-
self.process(to_process)
|
|
81
|
-
self.build_ok = True
|
|
82
|
-
except Exception as e:
|
|
83
|
-
click.echo(FeedbackManager.error_exception(error=e))
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
def watch_files(
|
|
87
|
-
filenames: List[str],
|
|
88
|
-
process: Callable,
|
|
89
|
-
shell: Shell,
|
|
90
|
-
project: Project,
|
|
91
|
-
build_ok: bool,
|
|
92
|
-
) -> None:
|
|
93
|
-
# Handle both sync and async process functions
|
|
94
|
-
async def process_wrapper(files: List[str]) -> None:
|
|
95
|
-
click.echo(FeedbackManager.highlight(message="» Rebuilding project..."))
|
|
96
|
-
time_start = time.time()
|
|
97
|
-
if asyncio.iscoroutinefunction(process):
|
|
98
|
-
await process(files, watch=True)
|
|
99
|
-
else:
|
|
100
|
-
process(files, watch=True)
|
|
101
|
-
time_end = time.time()
|
|
102
|
-
elapsed_time = time_end - time_start
|
|
103
|
-
click.echo(
|
|
104
|
-
FeedbackManager.success(message="\n✓ ")
|
|
105
|
-
+ FeedbackManager.gray(message=f"Rebuild completed in {elapsed_time:.1f}s")
|
|
106
|
-
)
|
|
107
|
-
shell.reprint_prompt()
|
|
108
|
-
|
|
109
|
-
event_handler = FileChangeHandler(filenames, lambda f: asyncio.run(process_wrapper(f)), build_ok)
|
|
110
|
-
observer = Observer()
|
|
111
|
-
|
|
112
|
-
observer.schedule(event_handler, path=str(project.path), recursive=True)
|
|
113
|
-
|
|
114
|
-
observer.start()
|
|
115
|
-
|
|
116
|
-
try:
|
|
117
|
-
while True:
|
|
118
|
-
time.sleep(1)
|
|
119
|
-
except KeyboardInterrupt:
|
|
120
|
-
observer.stop()
|
|
121
|
-
|
|
122
|
-
observer.join()
|
|
123
|
-
|
|
124
|
-
|
|
125
20
|
class WatchProjectHandler(PatternMatchingEventHandler):
|
|
126
21
|
def __init__(
|
|
127
22
|
self,
|
|
128
23
|
shell: Shell,
|
|
129
24
|
project: Project,
|
|
130
|
-
process: Callable[[Optional[str], Optional[str]], None],
|
|
25
|
+
process: Callable[[bool, Optional[str], Optional[str]], None],
|
|
131
26
|
):
|
|
132
27
|
self.shell = shell
|
|
133
28
|
self.project = project
|
|
@@ -156,14 +51,7 @@ class WatchProjectHandler(PatternMatchingEventHandler):
|
|
|
156
51
|
|
|
157
52
|
def _process(self, path: Optional[str] = None) -> None:
|
|
158
53
|
click.echo(FeedbackManager.highlight(message="» Rebuilding project..."))
|
|
159
|
-
|
|
160
|
-
self.process(path, self.diff(path))
|
|
161
|
-
time_end = time.time()
|
|
162
|
-
elapsed_time = time_end - time_start
|
|
163
|
-
click.echo(
|
|
164
|
-
FeedbackManager.success(message="\n✓ ")
|
|
165
|
-
+ FeedbackManager.gray(message=f"Rebuild completed in {elapsed_time:.1f}s")
|
|
166
|
-
)
|
|
54
|
+
self.process(True, path, self.diff(path))
|
|
167
55
|
self.shell.reprint_prompt()
|
|
168
56
|
|
|
169
57
|
def diff(self, path: Optional[str] = None) -> Optional[str]:
|
|
@@ -225,7 +113,7 @@ class WatchProjectHandler(PatternMatchingEventHandler):
|
|
|
225
113
|
|
|
226
114
|
def watch_project(
|
|
227
115
|
shell: Shell,
|
|
228
|
-
process: Callable[[Optional[str], Optional[str]], None],
|
|
116
|
+
process: Callable[[bool, Optional[str], Optional[str]], None],
|
|
229
117
|
project: Project,
|
|
230
118
|
) -> None:
|
|
231
119
|
event_handler = WatchProjectHandler(shell=shell, project=project, process=process)
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import hashlib
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
from typing import Any, Dict, List, Union
|
|
4
|
-
|
|
5
|
-
from tinybird.tb.modules.common import format_data_to_ndjson
|
|
6
|
-
from tinybird.tb.modules.datafile.parse_datasource import parse_datasource
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def build_fixture_name(filename: str, datasource_name: str, datasource_content: str) -> str:
|
|
10
|
-
"""Generate a unique fixture name based on datasource properties.
|
|
11
|
-
|
|
12
|
-
Args:
|
|
13
|
-
datasource_name: Name of the datasource
|
|
14
|
-
datasource_content: Content of the datasource file
|
|
15
|
-
row_count: Number of rows requested
|
|
16
|
-
|
|
17
|
-
Returns:
|
|
18
|
-
str: A unique fixture name combining a hash of the inputs with the datasource name
|
|
19
|
-
"""
|
|
20
|
-
|
|
21
|
-
doc = parse_datasource(filename, content=datasource_content)
|
|
22
|
-
schema = doc.nodes[0].get("schema", "").strip()
|
|
23
|
-
# Combine all inputs into a single string
|
|
24
|
-
combined = f"{datasource_name}{schema}"
|
|
25
|
-
|
|
26
|
-
# Generate hash
|
|
27
|
-
hash_obj = hashlib.sha256(combined.encode())
|
|
28
|
-
hash_str = hash_obj.hexdigest()[:8]
|
|
29
|
-
|
|
30
|
-
# Return fixture name with hash
|
|
31
|
-
return f"{datasource_name}_{hash_str}"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def get_fixture_dir(folder: str) -> Path:
|
|
35
|
-
fixture_dir = Path(folder) / "fixtures"
|
|
36
|
-
if not fixture_dir.exists():
|
|
37
|
-
fixture_dir.mkdir()
|
|
38
|
-
return fixture_dir
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def persist_fixture(fixture_name: str, data: Union[List[Dict[str, Any]], str], folder: str, format="ndjson") -> Path:
|
|
42
|
-
fixture_dir = get_fixture_dir(folder)
|
|
43
|
-
fixture_file = fixture_dir / f"{fixture_name}.{format}"
|
|
44
|
-
fixture_file.write_text(data if isinstance(data, str) else format_data_to_ndjson(data))
|
|
45
|
-
return fixture_file
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def load_fixture(
|
|
49
|
-
fixture_name: str,
|
|
50
|
-
folder: str,
|
|
51
|
-
format="ndjson",
|
|
52
|
-
) -> Union[Path, None]:
|
|
53
|
-
fixture_dir = get_fixture_dir(folder)
|
|
54
|
-
fixture_file = fixture_dir / f"{fixture_name}.{format}"
|
|
55
|
-
if not fixture_file.exists():
|
|
56
|
-
return None
|
|
57
|
-
return fixture_file
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/datafile/build_datasource.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/datafile/format_datasource.py
RENAMED
|
File without changes
|
|
File without changes
|
{tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb/modules/datafile/parse_datasource.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tinybird-0.0.1.dev56 → tinybird-0.0.1.dev58}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|