tinybird 0.0.1.dev306__py3-none-any.whl → 1.0.5__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/datafile/common.py +4 -1
- tinybird/feedback_manager.py +3 -0
- tinybird/service_datasources.py +57 -8
- tinybird/sql_template.py +1 -1
- tinybird/sql_template_fmt.py +14 -4
- tinybird/tb/__cli__.py +2 -2
- tinybird/tb/cli.py +1 -0
- tinybird/tb/client.py +104 -22
- tinybird/tb/modules/agent/tools/execute_query.py +1 -1
- tinybird/tb/modules/agent/tools/request_endpoint.py +1 -1
- tinybird/tb/modules/branch.py +150 -0
- tinybird/tb/modules/build.py +51 -10
- tinybird/tb/modules/build_common.py +4 -2
- tinybird/tb/modules/cli.py +32 -10
- tinybird/tb/modules/common.py +161 -134
- tinybird/tb/modules/connection.py +125 -194
- tinybird/tb/modules/connection_kafka.py +382 -0
- tinybird/tb/modules/copy.py +3 -1
- tinybird/tb/modules/create.py +11 -0
- tinybird/tb/modules/datafile/build.py +1 -1
- tinybird/tb/modules/datafile/format_pipe.py +44 -5
- tinybird/tb/modules/datafile/playground.py +1 -1
- tinybird/tb/modules/datasource.py +475 -324
- tinybird/tb/modules/deployment.py +2 -0
- tinybird/tb/modules/deployment_common.py +81 -43
- tinybird/tb/modules/deprecations.py +4 -4
- tinybird/tb/modules/dev_server.py +33 -12
- tinybird/tb/modules/info.py +50 -7
- tinybird/tb/modules/job_common.py +15 -0
- tinybird/tb/modules/local.py +91 -21
- tinybird/tb/modules/local_common.py +320 -13
- tinybird/tb/modules/local_logs.py +209 -0
- tinybird/tb/modules/login.py +3 -2
- tinybird/tb/modules/login_common.py +252 -9
- tinybird/tb/modules/open.py +10 -5
- tinybird/tb/modules/project.py +14 -5
- tinybird/tb/modules/shell.py +14 -6
- tinybird/tb/modules/sink.py +3 -1
- tinybird/tb/modules/telemetry.py +7 -3
- tinybird/tb_cli_modules/telemetry.py +1 -1
- {tinybird-0.0.1.dev306.dist-info → tinybird-1.0.5.dist-info}/METADATA +29 -4
- {tinybird-0.0.1.dev306.dist-info → tinybird-1.0.5.dist-info}/RECORD +45 -41
- {tinybird-0.0.1.dev306.dist-info → tinybird-1.0.5.dist-info}/WHEEL +1 -1
- {tinybird-0.0.1.dev306.dist-info → tinybird-1.0.5.dist-info}/entry_points.txt +0 -0
- {tinybird-0.0.1.dev306.dist-info → tinybird-1.0.5.dist-info}/top_level.txt +0 -0
tinybird/tb/modules/build.py
CHANGED
|
@@ -3,7 +3,7 @@ import time
|
|
|
3
3
|
from copy import deepcopy
|
|
4
4
|
from functools import partial
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import Any, Callable, Dict, List
|
|
6
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
7
7
|
from urllib.parse import urlencode
|
|
8
8
|
|
|
9
9
|
import click
|
|
@@ -35,7 +35,10 @@ def build(ctx: click.Context, watch: bool) -> None:
|
|
|
35
35
|
project: Project = ctx.ensure_object(dict)["project"]
|
|
36
36
|
tb_client: TinyB = ctx.ensure_object(dict)["client"]
|
|
37
37
|
config: Dict[str, Any] = ctx.ensure_object(dict)["config"]
|
|
38
|
-
|
|
38
|
+
is_branch = bool(ctx.ensure_object(dict)["branch"])
|
|
39
|
+
|
|
40
|
+
# TODO: Explain that you can use custom branches too once they are open for everyone
|
|
41
|
+
if obj["env"] == "cloud" and not is_branch:
|
|
39
42
|
raise click.ClickException(FeedbackManager.error_build_only_supported_in_local())
|
|
40
43
|
|
|
41
44
|
if project.has_deeper_level():
|
|
@@ -48,47 +51,85 @@ def build(ctx: click.Context, watch: bool) -> None:
|
|
|
48
51
|
)
|
|
49
52
|
|
|
50
53
|
click.echo(FeedbackManager.highlight_building_project())
|
|
51
|
-
process(project=project, tb_client=tb_client, watch=False, config=config)
|
|
54
|
+
process(project=project, tb_client=tb_client, watch=False, config=config, is_branch=is_branch)
|
|
52
55
|
if watch:
|
|
53
56
|
run_watch(
|
|
54
57
|
project=project,
|
|
55
58
|
tb_client=tb_client,
|
|
56
59
|
config=config,
|
|
57
|
-
process=partial(
|
|
60
|
+
process=partial(
|
|
61
|
+
process,
|
|
62
|
+
project=project,
|
|
63
|
+
tb_client=tb_client,
|
|
64
|
+
watch=True,
|
|
65
|
+
config=config,
|
|
66
|
+
is_branch=is_branch,
|
|
67
|
+
),
|
|
58
68
|
)
|
|
59
69
|
|
|
60
70
|
|
|
61
71
|
@cli.command("dev", help="Build the project server side and watch for changes.")
|
|
62
72
|
@click.option("--data-origin", type=str, default="", help="Data origin: local or cloud")
|
|
63
|
-
@click.option("--ui", is_flag=True, default=
|
|
73
|
+
@click.option("--ui/--skip-ui", is_flag=True, default=True, help="Connect your local project to Tinybird UI")
|
|
64
74
|
@click.pass_context
|
|
65
75
|
def dev(ctx: click.Context, data_origin: str, ui: bool) -> None:
|
|
76
|
+
obj: Dict[str, Any] = ctx.ensure_object(dict)
|
|
77
|
+
branch: Optional[str] = ctx.ensure_object(dict)["branch"]
|
|
78
|
+
is_branch = bool(branch)
|
|
79
|
+
|
|
80
|
+
if obj["env"] == "cloud" and not is_branch:
|
|
81
|
+
raise click.ClickException(FeedbackManager.error_build_only_supported_in_local())
|
|
82
|
+
|
|
66
83
|
if data_origin == "cloud":
|
|
84
|
+
click.echo(
|
|
85
|
+
FeedbackManager.warning(
|
|
86
|
+
message="--data-origin=cloud is deprecated and will be removed in a future version. Create an branch and use `tb --branch <branch_name> dev`"
|
|
87
|
+
)
|
|
88
|
+
)
|
|
67
89
|
return dev_cloud(ctx)
|
|
90
|
+
|
|
68
91
|
project: Project = ctx.ensure_object(dict)["project"]
|
|
69
92
|
tb_client: TinyB = ctx.ensure_object(dict)["client"]
|
|
70
93
|
config: Dict[str, Any] = ctx.ensure_object(dict)["config"]
|
|
94
|
+
|
|
71
95
|
build_status = BuildStatus()
|
|
72
96
|
if ui:
|
|
73
97
|
server_thread = threading.Thread(
|
|
74
|
-
target=start_server, args=(project, tb_client, process, build_status), daemon=True
|
|
98
|
+
target=start_server, args=(project, tb_client, process, build_status, branch), daemon=True
|
|
75
99
|
)
|
|
76
100
|
server_thread.start()
|
|
77
101
|
# Wait for the server to start
|
|
78
102
|
time.sleep(0.5)
|
|
79
103
|
|
|
80
104
|
click.echo(FeedbackManager.highlight_building_project())
|
|
81
|
-
process(
|
|
105
|
+
process(
|
|
106
|
+
project=project,
|
|
107
|
+
tb_client=tb_client,
|
|
108
|
+
watch=True,
|
|
109
|
+
config=config,
|
|
110
|
+
build_status=build_status,
|
|
111
|
+
is_branch=is_branch,
|
|
112
|
+
)
|
|
82
113
|
run_watch(
|
|
83
114
|
project=project,
|
|
84
115
|
tb_client=tb_client,
|
|
85
116
|
config=config,
|
|
86
|
-
|
|
117
|
+
branch=branch,
|
|
118
|
+
process=partial(
|
|
119
|
+
process,
|
|
120
|
+
project=project,
|
|
121
|
+
tb_client=tb_client,
|
|
122
|
+
build_status=build_status,
|
|
123
|
+
config=config,
|
|
124
|
+
is_branch=is_branch,
|
|
125
|
+
),
|
|
87
126
|
)
|
|
88
127
|
|
|
89
128
|
|
|
90
|
-
def run_watch(
|
|
91
|
-
|
|
129
|
+
def run_watch(
|
|
130
|
+
project: Project, tb_client: TinyB, process: Callable, config: dict[str, Any], branch: Optional[str] = None
|
|
131
|
+
) -> None:
|
|
132
|
+
shell = Shell(project=project, tb_client=tb_client, branch=branch)
|
|
92
133
|
click.echo(FeedbackManager.gray(message="\nWatching for changes..."))
|
|
93
134
|
watcher_thread = threading.Thread(
|
|
94
135
|
target=watch_project,
|
|
@@ -33,15 +33,17 @@ def process(
|
|
|
33
33
|
exit_on_error: bool = True,
|
|
34
34
|
load_fixtures: bool = True,
|
|
35
35
|
project_with_vendors: Optional[Project] = None,
|
|
36
|
+
is_branch: bool = False,
|
|
36
37
|
) -> Optional[str]:
|
|
37
38
|
time_start = time.time()
|
|
38
39
|
|
|
39
40
|
# Build vendored workspaces before build
|
|
40
|
-
if not project_with_vendors:
|
|
41
|
+
if not project_with_vendors and not is_branch:
|
|
41
42
|
build_vendored_workspaces(project=project, tb_client=tb_client, config=config)
|
|
42
43
|
|
|
43
44
|
# Ensure SHARED_WITH workspaces exist before build
|
|
44
|
-
|
|
45
|
+
if not is_branch:
|
|
46
|
+
build_shared_with_workspaces(project=project, tb_client=tb_client, config=config)
|
|
45
47
|
|
|
46
48
|
build_failed = False
|
|
47
49
|
build_error: Optional[str] = None
|
tinybird/tb/modules/cli.py
CHANGED
|
@@ -42,6 +42,7 @@ from tinybird.tb.modules.datafile.pull import folder_pull
|
|
|
42
42
|
from tinybird.tb.modules.exceptions import CLIChException
|
|
43
43
|
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
44
44
|
from tinybird.tb.modules.local_common import get_tinybird_local_client
|
|
45
|
+
from tinybird.tb.modules.login_common import check_current_folder_in_sessions
|
|
45
46
|
from tinybird.tb.modules.project import Project
|
|
46
47
|
|
|
47
48
|
__old_click_echo = click.echo
|
|
@@ -76,6 +77,7 @@ VERSION = f"{__cli__.__version__} (rev {__cli__.__revision__})"
|
|
|
76
77
|
)
|
|
77
78
|
@click.option("--show-tokens", is_flag=True, default=False, help="Enable the output of tokens.")
|
|
78
79
|
@click.option("--cloud/--local", is_flag=True, default=False, help="Run against cloud or local.")
|
|
80
|
+
@click.option("--branch", help="Run against a branch.")
|
|
79
81
|
@click.option("--staging", is_flag=True, default=False, help="Run against a staging deployment.")
|
|
80
82
|
@click.option(
|
|
81
83
|
"--output", type=click.Choice(["human", "json", "csv"], case_sensitive=False), default="human", help="Output format"
|
|
@@ -103,6 +105,7 @@ def cli(
|
|
|
103
105
|
version_warning: bool,
|
|
104
106
|
show_tokens: bool,
|
|
105
107
|
cloud: bool,
|
|
108
|
+
branch: Optional[str],
|
|
106
109
|
staging: bool,
|
|
107
110
|
output: str,
|
|
108
111
|
max_depth: int,
|
|
@@ -200,21 +203,33 @@ def cli(
|
|
|
200
203
|
return
|
|
201
204
|
|
|
202
205
|
ctx.ensure_object(dict)["project"] = project
|
|
203
|
-
client = create_ctx_client(
|
|
206
|
+
client = create_ctx_client(
|
|
207
|
+
ctx,
|
|
208
|
+
config,
|
|
209
|
+
cloud or bool(branch),
|
|
210
|
+
staging,
|
|
211
|
+
project=project,
|
|
212
|
+
show_warnings=version_warning,
|
|
213
|
+
branch=branch,
|
|
214
|
+
)
|
|
204
215
|
|
|
205
216
|
if client:
|
|
206
217
|
ctx.ensure_object(dict)["client"] = client
|
|
207
218
|
|
|
208
|
-
ctx.ensure_object(dict)["env"] = get_target_env(cloud)
|
|
219
|
+
ctx.ensure_object(dict)["env"] = get_target_env(cloud, branch)
|
|
220
|
+
ctx.ensure_object(dict)["branch"] = branch
|
|
209
221
|
ctx.ensure_object(dict)["output"] = output
|
|
210
222
|
|
|
223
|
+
# Check if current folder is tracked from previous sessions
|
|
224
|
+
check_current_folder_in_sessions(ctx)
|
|
225
|
+
|
|
211
226
|
is_prompt_mode = prompt is not None
|
|
212
227
|
|
|
213
228
|
if is_agent_mode or is_prompt_mode:
|
|
214
|
-
if any(arg in sys.argv for arg in ["--cloud", "--local"]):
|
|
229
|
+
if any(arg in sys.argv for arg in ["--cloud", "--local", "--branch"]):
|
|
215
230
|
raise CLIException(
|
|
216
231
|
FeedbackManager.error(
|
|
217
|
-
message="Tinybird Code does not support --cloud or --
|
|
232
|
+
message="Tinybird Code does not support --cloud, --local or --branch flags. It will choose the correct environment based on your prompts."
|
|
218
233
|
)
|
|
219
234
|
)
|
|
220
235
|
|
|
@@ -484,7 +499,13 @@ def __hide_click_output() -> None:
|
|
|
484
499
|
|
|
485
500
|
|
|
486
501
|
def create_ctx_client(
|
|
487
|
-
ctx: Context,
|
|
502
|
+
ctx: Context,
|
|
503
|
+
config: Dict[str, Any],
|
|
504
|
+
cloud: bool,
|
|
505
|
+
staging: bool,
|
|
506
|
+
project: Project,
|
|
507
|
+
show_warnings: bool = True,
|
|
508
|
+
branch: Optional[str] = None,
|
|
488
509
|
):
|
|
489
510
|
commands_without_ctx_client = [
|
|
490
511
|
"auth",
|
|
@@ -498,6 +519,7 @@ def create_ctx_client(
|
|
|
498
519
|
"tag",
|
|
499
520
|
"push",
|
|
500
521
|
"branch",
|
|
522
|
+
"environment",
|
|
501
523
|
"diff",
|
|
502
524
|
"fmt",
|
|
503
525
|
"init",
|
|
@@ -506,8 +528,8 @@ def create_ctx_client(
|
|
|
506
528
|
if not command or command in commands_without_ctx_client:
|
|
507
529
|
return None
|
|
508
530
|
|
|
509
|
-
commands_always_cloud = ["infra"]
|
|
510
|
-
commands_always_local = ["
|
|
531
|
+
commands_always_cloud = ["infra", "branch", "environment"]
|
|
532
|
+
commands_always_local = ["create"]
|
|
511
533
|
command_always_test = ["test"]
|
|
512
534
|
|
|
513
535
|
if (
|
|
@@ -530,7 +552,7 @@ def create_ctx_client(
|
|
|
530
552
|
if method and show_warnings:
|
|
531
553
|
click.echo(FeedbackManager.gray(message=f"Authentication method: {method}"))
|
|
532
554
|
|
|
533
|
-
return _get_tb_client(config.get("token", ""), config["host"], staging=staging)
|
|
555
|
+
return _get_tb_client(config.get("token", ""), config["host"], staging=staging, branch=branch)
|
|
534
556
|
local = command in commands_always_local
|
|
535
557
|
test = command in command_always_test
|
|
536
558
|
if show_warnings and not local and command not in commands_always_local and command:
|
|
@@ -538,8 +560,8 @@ def create_ctx_client(
|
|
|
538
560
|
return get_tinybird_local_client(config, test=test, staging=staging)
|
|
539
561
|
|
|
540
562
|
|
|
541
|
-
def get_target_env(cloud: bool) -> str:
|
|
542
|
-
if cloud:
|
|
563
|
+
def get_target_env(cloud: bool, branch: Optional[str]) -> str:
|
|
564
|
+
if cloud or bool(branch):
|
|
543
565
|
return "cloud"
|
|
544
566
|
return "local"
|
|
545
567
|
|
tinybird/tb/modules/common.py
CHANGED
|
@@ -30,7 +30,6 @@ import requests
|
|
|
30
30
|
from click import Context
|
|
31
31
|
from click._termui_impl import ProgressBar
|
|
32
32
|
from humanfriendly.tables import format_pretty_table
|
|
33
|
-
from packaging.version import Version
|
|
34
33
|
from thefuzz import process
|
|
35
34
|
|
|
36
35
|
from tinybird.tb.client import (
|
|
@@ -61,7 +60,6 @@ from tinybird.tb.modules.exceptions import (
|
|
|
61
60
|
CLIAuthException,
|
|
62
61
|
CLIConnectionException,
|
|
63
62
|
CLIException,
|
|
64
|
-
CLIReleaseException,
|
|
65
63
|
CLIWorkspaceException,
|
|
66
64
|
)
|
|
67
65
|
from tinybird.tb.modules.feedback_manager import FeedbackManager, warning_message
|
|
@@ -378,9 +376,33 @@ def getenv_bool(key: str, default: bool) -> bool:
|
|
|
378
376
|
return v.lower() == "true" or v == "1"
|
|
379
377
|
|
|
380
378
|
|
|
381
|
-
def _get_tb_client(token: str, host: str, staging: bool = False) -> TinyB:
|
|
379
|
+
def _get_tb_client(token: str, host: str, staging: bool = False, branch: Optional[str] = None) -> TinyB:
|
|
382
380
|
disable_ssl: bool = getenv_bool("TB_DISABLE_SSL_CHECKS", False)
|
|
383
|
-
|
|
381
|
+
cloud_client = TinyB(
|
|
382
|
+
token,
|
|
383
|
+
host,
|
|
384
|
+
version=VERSION,
|
|
385
|
+
disable_ssl_checks=disable_ssl,
|
|
386
|
+
send_telemetry=True,
|
|
387
|
+
staging=staging,
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
if not branch:
|
|
391
|
+
return cloud_client
|
|
392
|
+
|
|
393
|
+
workspaces = cloud_client.user_workspaces_and_branches(version="v1")
|
|
394
|
+
workspace = next((w for w in workspaces.get("workspaces", []) if w.get("name") == branch), None)
|
|
395
|
+
if not workspace:
|
|
396
|
+
raise CLIException(FeedbackManager.error_exception(error=f"Branch {branch} not found"))
|
|
397
|
+
|
|
398
|
+
return TinyB(
|
|
399
|
+
workspace.get("token", ""),
|
|
400
|
+
host,
|
|
401
|
+
version=VERSION,
|
|
402
|
+
disable_ssl_checks=disable_ssl,
|
|
403
|
+
send_telemetry=True,
|
|
404
|
+
staging=staging,
|
|
405
|
+
)
|
|
384
406
|
|
|
385
407
|
|
|
386
408
|
def create_tb_client(ctx: Context) -> TinyB:
|
|
@@ -414,7 +436,16 @@ def analyze_file(filename: str, client: TinyB, format: str):
|
|
|
414
436
|
meta, data = _analyze(filename, client, format)
|
|
415
437
|
schema = meta["analysis"]["schema"]
|
|
416
438
|
schema = schema.replace(", ", ",\n ")
|
|
417
|
-
content = f"""DESCRIPTION
|
|
439
|
+
content = f"""DESCRIPTION >
|
|
440
|
+
Generated from {filename}
|
|
441
|
+
|
|
442
|
+
SCHEMA >
|
|
443
|
+
{schema}
|
|
444
|
+
|
|
445
|
+
ENGINE "MergeTree"
|
|
446
|
+
# ENGINE_SORTING_KEY "user_id, timestamp"
|
|
447
|
+
# ENGINE_TTL "timestamp + toIntervalDay(60)"
|
|
448
|
+
# Learn more at https://www.tinybird.co/docs/forward/dev-reference/datafiles/datasource-files"""
|
|
418
449
|
return content
|
|
419
450
|
|
|
420
451
|
|
|
@@ -429,7 +460,16 @@ def _generate_datafile(
|
|
|
429
460
|
meta, data = _analyze(filename, client, format, connector=connector)
|
|
430
461
|
schema = meta["analysis"]["schema"]
|
|
431
462
|
schema = schema.replace(", ", ",\n ")
|
|
432
|
-
datafile = f"""DESCRIPTION
|
|
463
|
+
datafile = f"""DESCRIPTION >
|
|
464
|
+
Generated from {filename}
|
|
465
|
+
|
|
466
|
+
SCHEMA >
|
|
467
|
+
{schema}
|
|
468
|
+
|
|
469
|
+
ENGINE "MergeTree"
|
|
470
|
+
# ENGINE_SORTING_KEY "user_id, timestamp"
|
|
471
|
+
# ENGINE_TTL "timestamp + toIntervalDay(60)"
|
|
472
|
+
# Learn more at https://www.tinybird.co/docs/forward/dev-reference/datafiles/datasource-files"""
|
|
433
473
|
return generate_datafile(datafile, filename, data, force, _format=format, folder=folder)
|
|
434
474
|
|
|
435
475
|
|
|
@@ -965,7 +1005,10 @@ def push_data(
|
|
|
965
1005
|
cb.prev_done = 0 # type: ignore[attr-defined]
|
|
966
1006
|
|
|
967
1007
|
if not silent:
|
|
968
|
-
|
|
1008
|
+
if mode == "replace":
|
|
1009
|
+
click.echo(FeedbackManager.highlight(message=f"\n» Replacing data in {datasource_name}..."))
|
|
1010
|
+
else:
|
|
1011
|
+
click.echo(FeedbackManager.highlight(message=f"\n» Appending data to {datasource_name}..."))
|
|
969
1012
|
|
|
970
1013
|
if isinstance(url, list):
|
|
971
1014
|
urls = url
|
|
@@ -1165,10 +1208,26 @@ def _get_setting_value(connection, setting, sensitive_settings):
|
|
|
1165
1208
|
return connection.get(setting, "")
|
|
1166
1209
|
|
|
1167
1210
|
|
|
1168
|
-
def
|
|
1211
|
+
def get_current_workspace_branches(config: CLIConfig) -> List[Dict[str, Any]]:
|
|
1212
|
+
current_main_workspace: Optional[Dict[str, Any]] = get_current_main_workspace(config)
|
|
1213
|
+
if not current_main_workspace:
|
|
1214
|
+
raise CLIException(FeedbackManager.error_unable_to_identify_main_workspace())
|
|
1215
|
+
|
|
1216
|
+
client = config.get_client()
|
|
1217
|
+
user_branches: List[Dict[str, Any]] = (client.user_workspace_branches("v1")).get("workspaces", [])
|
|
1218
|
+
all_branches: List[Dict[str, Any]] = (client.branches()).get("environments", [])
|
|
1219
|
+
branches = user_branches + [branch for branch in all_branches if branch not in user_branches]
|
|
1220
|
+
|
|
1221
|
+
return [branch for branch in branches if branch.get("main") == current_main_workspace["id"]]
|
|
1222
|
+
|
|
1223
|
+
|
|
1224
|
+
def switch_workspace(config: CLIConfig, workspace_name_or_id: str, only_environments: bool = False) -> None:
|
|
1169
1225
|
try:
|
|
1170
|
-
|
|
1171
|
-
|
|
1226
|
+
if only_environments:
|
|
1227
|
+
workspaces = get_current_workspace_branches(config)
|
|
1228
|
+
else:
|
|
1229
|
+
response = config.get_client().user_workspaces(version="v1")
|
|
1230
|
+
workspaces = response["workspaces"]
|
|
1172
1231
|
|
|
1173
1232
|
workspace = next(
|
|
1174
1233
|
(
|
|
@@ -1563,130 +1622,6 @@ def _get_current_main_workspace_common(
|
|
|
1563
1622
|
return current
|
|
1564
1623
|
|
|
1565
1624
|
|
|
1566
|
-
def is_post_semver(new_version: Version, current_version: Version) -> bool:
|
|
1567
|
-
"""
|
|
1568
|
-
Check if only the post part of the semantic version has changed.
|
|
1569
|
-
|
|
1570
|
-
Args:
|
|
1571
|
-
new_version (Version): The new version to check.
|
|
1572
|
-
current_version (Version): The current version to compare with.
|
|
1573
|
-
|
|
1574
|
-
Returns:
|
|
1575
|
-
bool: True if only the post part of the version has changed, False otherwise.
|
|
1576
|
-
|
|
1577
|
-
Examples:
|
|
1578
|
-
>>> is_post_semver(Version("0.0.0-2"), Version("0.0.0-1"))
|
|
1579
|
-
True
|
|
1580
|
-
>>> is_post_semver(Version("0.0.0-1"), Version("0.0.0-1"))
|
|
1581
|
-
False
|
|
1582
|
-
>>> is_post_semver(Version("0.0.1-1"), Version("0.0.0-1"))
|
|
1583
|
-
False
|
|
1584
|
-
>>> is_post_semver(Version("0.1.0-1"), Version("0.0.0-1"))
|
|
1585
|
-
False
|
|
1586
|
-
>>> is_post_semver(Version("1.0.0-1"), Version("0.0.0-1"))
|
|
1587
|
-
False
|
|
1588
|
-
>>> is_post_semver(Version("0.0.1-1"), Version("0.0.0"))
|
|
1589
|
-
False
|
|
1590
|
-
"""
|
|
1591
|
-
if (
|
|
1592
|
-
new_version.major == current_version.major
|
|
1593
|
-
and new_version.minor == current_version.minor
|
|
1594
|
-
and new_version.micro == current_version.micro
|
|
1595
|
-
):
|
|
1596
|
-
return new_version.post is not None and new_version.post != current_version.post
|
|
1597
|
-
|
|
1598
|
-
return False
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
def is_major_semver(new_version: Version, current_version: Version) -> bool:
|
|
1602
|
-
"""
|
|
1603
|
-
Check if only the major part of the semantic version has changed.
|
|
1604
|
-
|
|
1605
|
-
Args:
|
|
1606
|
-
new_version (Version): The new version to check.
|
|
1607
|
-
current_version (Version): The current version to compare with.
|
|
1608
|
-
|
|
1609
|
-
Returns:
|
|
1610
|
-
bool: True if only the major part of the version has changed, False otherwise.
|
|
1611
|
-
|
|
1612
|
-
Examples:
|
|
1613
|
-
>>> is_major_semver(Version("1.0.0"), Version("0.0.0"))
|
|
1614
|
-
True
|
|
1615
|
-
>>> is_major_semver(Version("0.0.0"), Version("0.0.0"))
|
|
1616
|
-
False
|
|
1617
|
-
>>> is_major_semver(Version("1.0.1"), Version("1.0.0"))
|
|
1618
|
-
False
|
|
1619
|
-
>>> is_major_semver(Version("2.0.0-1"), Version("1.0.1-2"))
|
|
1620
|
-
True
|
|
1621
|
-
"""
|
|
1622
|
-
|
|
1623
|
-
return new_version.major != current_version.major
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
def print_release_summary(config: CLIConfig, semver: Optional[str], info: bool = False, dry_run=False):
|
|
1627
|
-
if not semver:
|
|
1628
|
-
click.echo(FeedbackManager.info_release_no_rollback())
|
|
1629
|
-
return
|
|
1630
|
-
try:
|
|
1631
|
-
client = config.get_client()
|
|
1632
|
-
response = client.release_rm(config["id"], semver, confirmation=config["name"], dry_run=True)
|
|
1633
|
-
except Exception as e:
|
|
1634
|
-
raise CLIReleaseException(FeedbackManager.error_exception(error=str(e)))
|
|
1635
|
-
else:
|
|
1636
|
-
columns = ["name", "id", "notes"]
|
|
1637
|
-
if not response:
|
|
1638
|
-
click.echo(FeedbackManager.info_release_no_rollback())
|
|
1639
|
-
return
|
|
1640
|
-
|
|
1641
|
-
if len(response["datasources"]) or len(response["pipes"]):
|
|
1642
|
-
semver = response.get("semver", semver)
|
|
1643
|
-
if info:
|
|
1644
|
-
if dry_run:
|
|
1645
|
-
click.echo(FeedbackManager.info_release_rm_resources_dry_run(semver=semver))
|
|
1646
|
-
else:
|
|
1647
|
-
click.echo(FeedbackManager.info_release_rm_resources())
|
|
1648
|
-
else:
|
|
1649
|
-
click.echo(FeedbackManager.info_release_rollback(semver=semver))
|
|
1650
|
-
|
|
1651
|
-
if len(response["datasources"]):
|
|
1652
|
-
click.echo("\nDatasources:")
|
|
1653
|
-
rows = [
|
|
1654
|
-
[ds, response["datasources"][ds], response["notes"].get(response["datasources"][ds], "")]
|
|
1655
|
-
for ds in response["datasources"]
|
|
1656
|
-
]
|
|
1657
|
-
echo_safe_humanfriendly_tables_format_smart_table(rows, column_names=columns)
|
|
1658
|
-
|
|
1659
|
-
if len(response["pipes"]):
|
|
1660
|
-
click.echo("\nPipes:")
|
|
1661
|
-
rows = [
|
|
1662
|
-
[pipe, response["pipes"][pipe], response["notes"].get(response["pipes"][pipe], "")]
|
|
1663
|
-
for pipe in response["pipes"]
|
|
1664
|
-
]
|
|
1665
|
-
echo_safe_humanfriendly_tables_format_smart_table(rows, column_names=columns)
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
def get_oldest_rollback(config: CLIConfig, client: TinyB) -> Optional[str]:
|
|
1669
|
-
oldest_rollback_response = client.release_oldest_rollback(config["id"])
|
|
1670
|
-
return oldest_rollback_response.get("semver") if oldest_rollback_response else None
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
def remove_release(
|
|
1674
|
-
dry_run: bool, config: CLIConfig, semver: Optional[str], client: TinyB, force: bool, show_print=True
|
|
1675
|
-
):
|
|
1676
|
-
if semver == OLDEST_ROLLBACK:
|
|
1677
|
-
semver = get_oldest_rollback(config, client)
|
|
1678
|
-
if show_print:
|
|
1679
|
-
print_release_summary(config, semver, info=True, dry_run=True)
|
|
1680
|
-
if not dry_run:
|
|
1681
|
-
if semver:
|
|
1682
|
-
response = client.release_rm(
|
|
1683
|
-
config["id"], semver, confirmation=config["name"], dry_run=dry_run, force=force
|
|
1684
|
-
)
|
|
1685
|
-
click.echo(FeedbackManager.success_release_delete(semver=response.get("semver")))
|
|
1686
|
-
else:
|
|
1687
|
-
click.echo(FeedbackManager.info_no_release_deleted())
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
1625
|
def run_aws_iamrole_connection_flow(
|
|
1691
1626
|
client: TinyB,
|
|
1692
1627
|
service: str,
|
|
@@ -2261,3 +2196,95 @@ def update_cli() -> None:
|
|
|
2261
2196
|
click.echo(FeedbackManager.success(message="✓ Tinybird CLI updated"))
|
|
2262
2197
|
else:
|
|
2263
2198
|
click.echo(FeedbackManager.info(message="✓ Tinybird CLI is already up-to-date"))
|
|
2199
|
+
|
|
2200
|
+
|
|
2201
|
+
def create_workspace_branch(
|
|
2202
|
+
branch_name: Optional[str],
|
|
2203
|
+
last_partition: bool,
|
|
2204
|
+
all: bool,
|
|
2205
|
+
ignore_datasources: Optional[List[str]],
|
|
2206
|
+
wait: Optional[bool],
|
|
2207
|
+
) -> None:
|
|
2208
|
+
"""
|
|
2209
|
+
Creates a workspace branch
|
|
2210
|
+
"""
|
|
2211
|
+
config = CLIConfig.get_project_config()
|
|
2212
|
+
_ = try_update_config_with_remote(config)
|
|
2213
|
+
|
|
2214
|
+
try:
|
|
2215
|
+
workspace = get_current_workspace(config)
|
|
2216
|
+
if not workspace:
|
|
2217
|
+
raise CLIWorkspaceException(FeedbackManager.error_workspace())
|
|
2218
|
+
|
|
2219
|
+
if not branch_name:
|
|
2220
|
+
click.echo(FeedbackManager.info_workspace_branch_create_greeting())
|
|
2221
|
+
default_name = f"{workspace['name']}_{uuid.uuid4().hex[0:4]}"
|
|
2222
|
+
branch_name = click.prompt("\Branch name", default=default_name, err=True, type=str)
|
|
2223
|
+
assert isinstance(branch_name, str)
|
|
2224
|
+
|
|
2225
|
+
response = config.get_client().create_workspace_branch(
|
|
2226
|
+
branch_name,
|
|
2227
|
+
last_partition,
|
|
2228
|
+
all,
|
|
2229
|
+
ignore_datasources,
|
|
2230
|
+
)
|
|
2231
|
+
assert isinstance(response, dict)
|
|
2232
|
+
|
|
2233
|
+
is_job: bool = "job" in response
|
|
2234
|
+
is_summary: bool = "partitions" in response
|
|
2235
|
+
|
|
2236
|
+
if not is_job and not is_summary:
|
|
2237
|
+
raise CLIException(str(response))
|
|
2238
|
+
|
|
2239
|
+
if all and not is_job:
|
|
2240
|
+
raise CLIException(str(response))
|
|
2241
|
+
|
|
2242
|
+
click.echo(
|
|
2243
|
+
FeedbackManager.success_workspace_branch_created(workspace_name=workspace["name"], branch_name=branch_name)
|
|
2244
|
+
)
|
|
2245
|
+
|
|
2246
|
+
job_id: Optional[str] = None
|
|
2247
|
+
|
|
2248
|
+
if is_job:
|
|
2249
|
+
job_id = response["job"]["job_id"]
|
|
2250
|
+
job_url = response["job"]["job_url"]
|
|
2251
|
+
click.echo(FeedbackManager.info_data_branch_job_url(url=job_url))
|
|
2252
|
+
|
|
2253
|
+
if wait and is_job:
|
|
2254
|
+
assert isinstance(job_id, str)
|
|
2255
|
+
|
|
2256
|
+
# Await the job to finish and get the result dict
|
|
2257
|
+
job_response = wait_job(config.get_client(), job_id, job_url, "Environment creation")
|
|
2258
|
+
if job_response is None:
|
|
2259
|
+
raise CLIException(f"Empty job API response (job_id: {job_id}, job_url: {job_url})")
|
|
2260
|
+
else:
|
|
2261
|
+
response = job_response.get("result", {})
|
|
2262
|
+
is_summary = "partitions" in response
|
|
2263
|
+
|
|
2264
|
+
except Exception as e:
|
|
2265
|
+
raise CLIException(FeedbackManager.error_exception(error=str(e)))
|
|
2266
|
+
|
|
2267
|
+
|
|
2268
|
+
async def print_current_branch(config: CLIConfig) -> None:
|
|
2269
|
+
_ = try_update_config_with_remote(config, only_if_needed=True)
|
|
2270
|
+
|
|
2271
|
+
response = config.get_client().user_workspaces_and_branches("v1")
|
|
2272
|
+
|
|
2273
|
+
columns = ["name", "id", "workspace"]
|
|
2274
|
+
table = []
|
|
2275
|
+
|
|
2276
|
+
for workspace in response["workspaces"]:
|
|
2277
|
+
if config["id"] == workspace["id"]:
|
|
2278
|
+
click.echo(FeedbackManager.info_current_branch())
|
|
2279
|
+
if workspace.get("is_branch"):
|
|
2280
|
+
name = workspace["name"]
|
|
2281
|
+
main_workspace = get_current_main_workspace(config)
|
|
2282
|
+
assert isinstance(main_workspace, dict)
|
|
2283
|
+
main_name = main_workspace["name"]
|
|
2284
|
+
else:
|
|
2285
|
+
name = MAIN_BRANCH
|
|
2286
|
+
main_name = workspace["name"]
|
|
2287
|
+
table.append([name, workspace["id"], main_name])
|
|
2288
|
+
break
|
|
2289
|
+
|
|
2290
|
+
echo_safe_humanfriendly_tables_format_smart_table(table, column_names=columns)
|