tinybird-cli 2.1.1.dev6__tar.gz → 2.1.1.dev8__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.
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/PKG-INFO +12 -2
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/__cli__.py +2 -2
- tinybird-cli-2.1.1.dev8/tinybird/connector_settings.py +153 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/datafile.py +27 -9
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/feedback_manager.py +22 -22
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/tb_cli_modules/branch.py +44 -44
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/tb_cli_modules/cicd.py +16 -10
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/tb_cli_modules/cli.py +6 -5
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/tb_cli_modules/common.py +25 -4
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/tb_cli_modules/connection.py +5 -7
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/tb_cli_modules/datasource.py +1 -1
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird_cli.egg-info/PKG-INFO +12 -2
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird_cli.egg-info/requires.txt +1 -0
- tinybird-cli-2.1.1.dev6/tinybird/connector_settings.py +0 -102
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/setup.cfg +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/ch_utils/constants.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/ch_utils/engine.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/check_pypi.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/client.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/config.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/connectors.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/context.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/datatypes.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/sql.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/sql_template.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/sql_template_fmt.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/sql_toolset.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/syncasync.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/tb_cli.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/tb_cli_modules/auth.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/tb_cli_modules/config.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/tb_cli_modules/exceptions.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/tb_cli_modules/job.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/tb_cli_modules/pipe.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/tb_cli_modules/regions.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/tb_cli_modules/telemetry.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/tb_cli_modules/test.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/tb_cli_modules/token.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/tb_cli_modules/workspace.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/tb_cli_modules/workspace_members.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/tornado_template.py +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird_cli.egg-info/SOURCES.txt +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird_cli.egg-info/dependency_links.txt +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird_cli.egg-info/entry_points.txt +0 -0
- {tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird_cli.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: tinybird-cli
|
|
3
|
-
Version: 2.1.1.
|
|
3
|
+
Version: 2.1.1.dev8
|
|
4
4
|
Summary: Tinybird Command Line Tool
|
|
5
5
|
Home-page: https://docs.tinybird.co/cli.html
|
|
6
6
|
Author: Tinybird
|
|
@@ -19,7 +19,17 @@ Changelog
|
|
|
19
19
|
|
|
20
20
|
---------
|
|
21
21
|
|
|
22
|
-
2.1.1.
|
|
22
|
+
2.1.1.dev8
|
|
23
|
+
************
|
|
24
|
+
|
|
25
|
+
- `Changed` Use `tb branch` instead of `tb env`. `tb env` is still usable but deprecated and will be removed in the next version.
|
|
26
|
+
|
|
27
|
+
2.1.1.dev7
|
|
28
|
+
************
|
|
29
|
+
|
|
30
|
+
- `Fixed` Be smarter in `tb fmt` when detecting if `.incl` file contains Data Source or Pipe definitions.
|
|
31
|
+
|
|
32
|
+
2.1.1.dev6
|
|
23
33
|
************
|
|
24
34
|
|
|
25
35
|
- `Fixed` `tb fmt` adds empty quoted strings in .datasource connection section.
|
|
@@ -4,5 +4,5 @@ __description__ = 'Tinybird Command Line Tool'
|
|
|
4
4
|
__url__ = 'https://docs.tinybird.co/cli.html'
|
|
5
5
|
__author__ = 'Tinybird'
|
|
6
6
|
__author_email__ = 'support@tinybird.co'
|
|
7
|
-
__version__ = '2.1.1.
|
|
8
|
-
__revision__ = '
|
|
7
|
+
__version__ = '2.1.1.dev8'
|
|
8
|
+
__revision__ = '96a93ca'
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import List, Optional
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class DataConnectorType(str, Enum):
|
|
10
|
+
KAFKA = "kafka"
|
|
11
|
+
GCLOUD_SCHEDULER = "gcscheduler"
|
|
12
|
+
SNOWFLAKE = "snowflake"
|
|
13
|
+
BIGQUERY = "bigquery"
|
|
14
|
+
GCLOUD_STORAGE = "gcs"
|
|
15
|
+
GCLOUD_STORAGE_HMAC = "gcs_hmac"
|
|
16
|
+
AMAZON_S3 = "s3"
|
|
17
|
+
AMAZON_S3_IAMROLE = "s3_iamrole"
|
|
18
|
+
|
|
19
|
+
def __str__(self) -> str:
|
|
20
|
+
return self.value
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
DataConnectors = DataConnectorType # this is just to make picking happy
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DataConnectorSetting(BaseModel):
|
|
27
|
+
...
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class KafkaConnectorSetting(DataConnectorSetting):
|
|
31
|
+
kafka_bootstrap_servers: str
|
|
32
|
+
kafka_sasl_plain_username: str
|
|
33
|
+
kafka_sasl_plain_password: str
|
|
34
|
+
cli_version: Optional[str] = None
|
|
35
|
+
tb_endpoint: Optional[str] = None
|
|
36
|
+
kafka_security_protocol: Optional[str] = None
|
|
37
|
+
kafka_sasl_mechanism: Optional[str] = None
|
|
38
|
+
kafka_schema_registry_url: Optional[str] = None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class S3ConnectorSetting(DataConnectorSetting):
|
|
42
|
+
s3_access_key_id: str
|
|
43
|
+
s3_secret_access_key: str
|
|
44
|
+
s3_region: str
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class S3IAMConnectorSetting(DataConnectorSetting):
|
|
48
|
+
s3_iamrole_arn: str
|
|
49
|
+
s3_iamrole_region: str
|
|
50
|
+
s3_iamrole_external_id: Optional[str] = None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class SnowflakeConnectorSetting(DataConnectorSetting):
|
|
54
|
+
account: str
|
|
55
|
+
username: str
|
|
56
|
+
password: str
|
|
57
|
+
role: str
|
|
58
|
+
warehouse: str
|
|
59
|
+
warehouse_size: Optional[str] = None
|
|
60
|
+
stage: Optional[str] = None
|
|
61
|
+
integration: Optional[str] = None
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class GCSchedulerConnectorSetting(DataConnectorSetting):
|
|
65
|
+
gcscheduler_region: Optional[str] = None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class BigQueryConnectorSetting(DataConnectorSetting):
|
|
69
|
+
account: Optional[str] = None
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class GCSHmacConnectorSetting(DataConnectorSetting):
|
|
73
|
+
gcs_hmac_access_id: str
|
|
74
|
+
gcs_hmac_secret: str
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class GCSConnectorSetting(DataConnectorSetting):
|
|
78
|
+
gcs_private_key_id: str
|
|
79
|
+
gcs_client_x509_cert_url: str
|
|
80
|
+
gcs_project_id: str
|
|
81
|
+
gcs_client_id: str
|
|
82
|
+
gcs_client_email: str
|
|
83
|
+
gcs_private_key: str
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
DATA_CONNECTOR_SETTINGS: dict[DataConnectors, type[DataConnectorSetting]] = {
|
|
87
|
+
DataConnectors.KAFKA: KafkaConnectorSetting,
|
|
88
|
+
DataConnectors.GCLOUD_SCHEDULER: GCSchedulerConnectorSetting,
|
|
89
|
+
DataConnectors.SNOWFLAKE: SnowflakeConnectorSetting,
|
|
90
|
+
DataConnectors.BIGQUERY: BigQueryConnectorSetting,
|
|
91
|
+
DataConnectors.GCLOUD_STORAGE: GCSConnectorSetting,
|
|
92
|
+
DataConnectors.GCLOUD_STORAGE_HMAC: GCSHmacConnectorSetting,
|
|
93
|
+
DataConnectors.AMAZON_S3: S3ConnectorSetting,
|
|
94
|
+
DataConnectors.AMAZON_S3_IAMROLE: S3IAMConnectorSetting,
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class DataLinkerSettings:
|
|
99
|
+
kafka = [
|
|
100
|
+
"tb_datasource",
|
|
101
|
+
"tb_token",
|
|
102
|
+
"kafka_topic",
|
|
103
|
+
"kafka_group_id",
|
|
104
|
+
"kafka_auto_offset_reset",
|
|
105
|
+
"kafka_store_raw_value",
|
|
106
|
+
"kafka_store_headers",
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class DataSinkSettings:
|
|
111
|
+
gcscheduler = [
|
|
112
|
+
"cron",
|
|
113
|
+
"timezone",
|
|
114
|
+
"status",
|
|
115
|
+
"gcscheduler_target_url",
|
|
116
|
+
"gcscheduler_job_name",
|
|
117
|
+
"gcscheduler_region",
|
|
118
|
+
]
|
|
119
|
+
gcs_hmac = [
|
|
120
|
+
"bucket_path",
|
|
121
|
+
"file_template",
|
|
122
|
+
"partition_node",
|
|
123
|
+
"format",
|
|
124
|
+
"compression",
|
|
125
|
+
]
|
|
126
|
+
s3 = [
|
|
127
|
+
"bucket_path",
|
|
128
|
+
"file_template",
|
|
129
|
+
"partition_node",
|
|
130
|
+
"format",
|
|
131
|
+
"compression",
|
|
132
|
+
]
|
|
133
|
+
s3_iamrole = [
|
|
134
|
+
"bucket_path",
|
|
135
|
+
"file_template",
|
|
136
|
+
"partition_node",
|
|
137
|
+
"format",
|
|
138
|
+
"compression",
|
|
139
|
+
]
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class DataSensitiveSettings:
|
|
143
|
+
kafka = ["kafka_sasl_plain_password"]
|
|
144
|
+
gcscheduler = [
|
|
145
|
+
"gcscheduler_target_url",
|
|
146
|
+
"gcscheduler_job_name",
|
|
147
|
+
"gcscheduler_region",
|
|
148
|
+
]
|
|
149
|
+
bigquery: List[str] = []
|
|
150
|
+
snowflake: List[str] = []
|
|
151
|
+
gcs_hmac: List[str] = ["gcs_hmac_secret"]
|
|
152
|
+
s3: List[str] = ["s3_secret_access_key"]
|
|
153
|
+
s3_iamrole: List[str] = ["s3_iamrole_arn"]
|
|
@@ -2682,7 +2682,7 @@ async def share_and_unshare_datasource(
|
|
|
2682
2682
|
datasource_id = datasource.get("id", "")
|
|
2683
2683
|
workspaces: List[Dict[str, Any]]
|
|
2684
2684
|
|
|
2685
|
-
# In case we are pushing to
|
|
2685
|
+
# In case we are pushing to a branch, we don't share the datasource
|
|
2686
2686
|
# FIXME: Have only once way to get the current workspace
|
|
2687
2687
|
if current_ws:
|
|
2688
2688
|
# Force to get all the workspaces the user can access
|
|
@@ -2693,7 +2693,7 @@ async def share_and_unshare_datasource(
|
|
|
2693
2693
|
workspaces = workspace.get("workspaces", [])
|
|
2694
2694
|
|
|
2695
2695
|
if workspace.get("is_branch", False):
|
|
2696
|
-
click.echo(FeedbackManager.
|
|
2696
|
+
click.echo(FeedbackManager.info_skipping_sharing_datasources_branch(datasource=datasource["name"]))
|
|
2697
2697
|
return
|
|
2698
2698
|
|
|
2699
2699
|
# We duplicate the client to use the user_token
|
|
@@ -3550,7 +3550,7 @@ async def folder_push(
|
|
|
3550
3550
|
current_ws: Dict[str, Any] = next(
|
|
3551
3551
|
(workspace for workspace in workspaces if config and workspace.get("id", ".") == config.get("id", "..")), {}
|
|
3552
3552
|
)
|
|
3553
|
-
|
|
3553
|
+
is_branch = current_ws.get("is_branch", False)
|
|
3554
3554
|
has_semver = False
|
|
3555
3555
|
if config and config.get("semver"):
|
|
3556
3556
|
has_semver = True
|
|
@@ -3639,7 +3639,7 @@ async def folder_push(
|
|
|
3639
3639
|
current_ws=current_ws,
|
|
3640
3640
|
changed=changed,
|
|
3641
3641
|
only_changes=only_changes or (deployment.is_git_release and has_semver),
|
|
3642
|
-
skip_connectors=
|
|
3642
|
+
skip_connectors=is_branch,
|
|
3643
3643
|
fork_downstream=fork_downstream,
|
|
3644
3644
|
is_internal=is_internal,
|
|
3645
3645
|
)
|
|
@@ -3864,7 +3864,7 @@ async def folder_push(
|
|
|
3864
3864
|
click.echo(FeedbackManager.info_pushing_fixtures())
|
|
3865
3865
|
|
|
3866
3866
|
# We need to upload the fixtures even if there is no change
|
|
3867
|
-
if
|
|
3867
|
+
if is_branch:
|
|
3868
3868
|
filenames = get_project_filenames(folder, with_vendor=True)
|
|
3869
3869
|
to_run, dep_map, back_to_run, back_dep_map = await build_graph(
|
|
3870
3870
|
filenames,
|
|
@@ -3876,7 +3876,7 @@ async def folder_push(
|
|
|
3876
3876
|
verbose=verbose,
|
|
3877
3877
|
workspace_lib_paths=workspace_lib_paths,
|
|
3878
3878
|
current_ws=current_ws,
|
|
3879
|
-
skip_connectors=
|
|
3879
|
+
skip_connectors=is_branch,
|
|
3880
3880
|
)
|
|
3881
3881
|
|
|
3882
3882
|
processed = set()
|
|
@@ -3891,13 +3891,13 @@ async def folder_push(
|
|
|
3891
3891
|
debug,
|
|
3892
3892
|
folder,
|
|
3893
3893
|
force,
|
|
3894
|
-
mode="append" if
|
|
3894
|
+
mode="append" if is_branch else "replace",
|
|
3895
3895
|
)
|
|
3896
3896
|
processed.add(name)
|
|
3897
3897
|
for f in to_run:
|
|
3898
3898
|
if f not in processed:
|
|
3899
3899
|
await check_fixtures_data(
|
|
3900
|
-
tb_client, to_run[f], debug, folder, force, mode="append" if
|
|
3900
|
+
tb_client, to_run[f], debug, folder, force, mode="append" if is_branch else "replace"
|
|
3901
3901
|
)
|
|
3902
3902
|
else:
|
|
3903
3903
|
if verbose:
|
|
@@ -4534,7 +4534,7 @@ async def diff_files(
|
|
|
4534
4534
|
async def parse(filename, with_format=True, unroll_includes=False):
|
|
4535
4535
|
extensions = Path(filename).suffixes
|
|
4536
4536
|
lines = None
|
|
4537
|
-
if
|
|
4537
|
+
if is_file_a_datasource(filename):
|
|
4538
4538
|
lines = (
|
|
4539
4539
|
await format_datasource(
|
|
4540
4540
|
filename, unroll_includes=unroll_includes, for_diff=True, client=client, replace_includes=True
|
|
@@ -4649,3 +4649,21 @@ def has_internal_datafiles(folder: str) -> bool:
|
|
|
4649
4649
|
folder = folder or "."
|
|
4650
4650
|
filenames = get_project_filenames(folder)
|
|
4651
4651
|
return any([f for f in filenames if "spans" in str(f) and "vendor" not in str(f)])
|
|
4652
|
+
|
|
4653
|
+
|
|
4654
|
+
def is_file_a_datasource(filename: str) -> bool:
|
|
4655
|
+
extensions = Path(filename).suffixes
|
|
4656
|
+
if ".datasource" in extensions: # Accepts '.datasource' and '.datasource.incl'
|
|
4657
|
+
return True
|
|
4658
|
+
|
|
4659
|
+
if ".incl" in extensions:
|
|
4660
|
+
lines = []
|
|
4661
|
+
with open(filename) as file:
|
|
4662
|
+
lines = file.readlines()
|
|
4663
|
+
|
|
4664
|
+
for line in lines:
|
|
4665
|
+
trimmed_line = line.strip().lower()
|
|
4666
|
+
if trimmed_line.startswith("schema") or trimmed_line.startswith("engine"):
|
|
4667
|
+
return True
|
|
4668
|
+
|
|
4669
|
+
return False
|
|
@@ -194,15 +194,15 @@ class FeedbackManager:
|
|
|
194
194
|
error_connection_create = error_message("Connection {connection_name} could not be created: {error}")
|
|
195
195
|
error_workspace = error_message("Workspace {workspace} not found. use 'tb workspace ls' to list your workspaces")
|
|
196
196
|
error_branch = error_message(
|
|
197
|
-
"
|
|
197
|
+
"Branch {branch} not found. use 'tb branch ls' to list your Branches, make sure you are authenticated using the right workspace token"
|
|
198
198
|
)
|
|
199
199
|
error_not_a_branch = error_message(
|
|
200
|
-
"To use this command you need to be authenticated on
|
|
200
|
+
"To use this command you need to be authenticated on a Branch. Use 'tb branch ls' and 'tb branch use' and retry the command."
|
|
201
201
|
)
|
|
202
202
|
error_not_allowed_in_branch = error_message(
|
|
203
|
-
"You need to be in Main to run this command. Hint: run tb
|
|
203
|
+
"You need to be in Main to run this command. Hint: run `tb branch use main` to switch to Main"
|
|
204
204
|
)
|
|
205
|
-
error_not_allowed_in_main_branch = error_message("Command disabled for 'main'
|
|
205
|
+
error_not_allowed_in_main_branch = error_message("Command disabled for 'main' Branch")
|
|
206
206
|
error_getting_region_by_index = error_message(
|
|
207
207
|
"Unable to get region by index, list available regions using 'tb auth ls'"
|
|
208
208
|
)
|
|
@@ -281,8 +281,8 @@ class FeedbackManager:
|
|
|
281
281
|
error_init_release = error_message(
|
|
282
282
|
"No release on workspace '{workspace}'. Hint: use 'tb init --git' to start working with git releases"
|
|
283
283
|
)
|
|
284
|
-
|
|
285
|
-
"
|
|
284
|
+
error_branch_init_release = error_message(
|
|
285
|
+
"Branch '{workspace}' not ready for deploy. Hint: use 'tb init --git' to start working with git releases and assure Branch is completely created using '--wait' on 'tb branch create'"
|
|
286
286
|
)
|
|
287
287
|
error_commit_changes_to_init_release = error_message(
|
|
288
288
|
"Data project has changes: 'git status -s {path}'\n\n{git_output} \n\n You need to commit your changes in the data project, then re-run 'tb init --git' to finish git initialization."
|
|
@@ -357,8 +357,8 @@ Ready? """
|
|
|
357
357
|
)
|
|
358
358
|
|
|
359
359
|
warning_no_test_results = warning_message("Warning: No test results to show")
|
|
360
|
-
|
|
361
|
-
|
|
360
|
+
warning_using_branch_token = warning_message("** You're using the token defined in $TB_TOKEN.")
|
|
361
|
+
warning_using_branch_host = warning_message("** You're using the token defined in $TB_HOST.")
|
|
362
362
|
|
|
363
363
|
error_bigquery_improper_permissions = info_message(
|
|
364
364
|
"** Error: no access detected. It might take a minute to detect the new permissions.\n"
|
|
@@ -420,7 +420,7 @@ Ready? """
|
|
|
420
420
|
"Do you want to remove all pipes and Data Sources from this workspace?"
|
|
421
421
|
)
|
|
422
422
|
warning_confirm_delete_workspace = prompt_message("Do you want to remove {workspace_name} workspace?")
|
|
423
|
-
warning_confirm_delete_branch = prompt_message("Do you want to remove '{branch}'
|
|
423
|
+
warning_confirm_delete_branch = prompt_message("Do you want to remove '{branch}' Branch?")
|
|
424
424
|
warning_confirm_delete_release = prompt_message("Do you want to remove Release {semver}?")
|
|
425
425
|
warning_confirm_rollback_release = prompt_message("Do you want to rollback current Release {semver} to {rollback}?")
|
|
426
426
|
|
|
@@ -476,9 +476,9 @@ Ready? """
|
|
|
476
476
|
)
|
|
477
477
|
info_populate_job_result = info_message("** Populating job result\n {result}")
|
|
478
478
|
info_populate_job_url = info_message("** Populating job url {url}")
|
|
479
|
-
info_data_branch_job_url = info_message("**
|
|
480
|
-
info_regression_tests_branch_job_url = info_message("**
|
|
481
|
-
info_merge_branch_job_url = info_message("** Merge
|
|
479
|
+
info_data_branch_job_url = info_message("** Branch job url {url}")
|
|
480
|
+
info_regression_tests_branch_job_url = info_message("** Branch regression tests job url {url}")
|
|
481
|
+
info_merge_branch_job_url = info_message("** Merge Branch deployment job url {url}")
|
|
482
482
|
info_copy_from_main_job_url = info_message("** Copy from 'main' Workspace to '{datasource_name}' job url {url}")
|
|
483
483
|
info_copy_with_sql_job_url = info_message("** Copy with --sql `{sql}` to '{datasource_name}' job url {url}")
|
|
484
484
|
info_populate_subset_job_url = info_message("** Populating (subset {subset}) job url {url}")
|
|
@@ -542,14 +542,14 @@ Ready? """
|
|
|
542
542
|
info_progress_blocks = info_message("\N{egg} blocks")
|
|
543
543
|
info_progress_current_blocks = info_message("\N{hatching chick} blocks")
|
|
544
544
|
info_release_generated = info_message(
|
|
545
|
-
"** Custom deployment files for Release {semver} generated. Edit the `ci-deploy.sh` file with the instructions to deploy the branch to a CI
|
|
545
|
+
"** Custom deployment files for Release {semver} generated. Edit the `ci-deploy.sh` file with the instructions to deploy the branch to a CI Branch and the `cd-deploy.sh` file with the instructions to deploy to the Main Branch after merge."
|
|
546
546
|
)
|
|
547
547
|
info_jobs = info_message("** Jobs:")
|
|
548
548
|
info_workspaces = info_message("** Workspaces:")
|
|
549
|
-
info_branches = info_message("**
|
|
549
|
+
info_branches = info_message("** Branches:")
|
|
550
550
|
info_releases = info_message("** Releases:")
|
|
551
551
|
info_current_workspace = info_message("** Current workspace:")
|
|
552
|
-
info_current_branch = info_message("** Current
|
|
552
|
+
info_current_branch = info_message("** Current Branch:")
|
|
553
553
|
info_job = info_message(" ** Job: {job}")
|
|
554
554
|
info_data_pushed = info_message("** Data pushed to {datasource}")
|
|
555
555
|
info_materialized_datasource_created = info_message(
|
|
@@ -587,7 +587,7 @@ Ready? """
|
|
|
587
587
|
"Now let's pick a starter template! 🐣\nStarter template are pre-built data projects for different use cases, that you can use as a starting point and then build on top of that.\nYou can bypass this step by supplying a value for the --starter-kit option."
|
|
588
588
|
)
|
|
589
589
|
info_workspace_branch_create_greeting = info_message(
|
|
590
|
-
"Please enter the name for your new
|
|
590
|
+
"Please enter the name for your new Branch. Remember the name you choose must be unique, you can add a suffix in case of collision.\nYou can bypass this step by supplying a value for the --name option."
|
|
591
591
|
)
|
|
592
592
|
info_no_git_release_yet = info_message("\n** Initializing releases based on git for Workspace '{workspace}'")
|
|
593
593
|
info_diff_resources_for_git_init = info_message(
|
|
@@ -612,8 +612,8 @@ Ready? """
|
|
|
612
612
|
info_deleting_resource = info_message("** {dry_run}Deleting '{resource_name}'")
|
|
613
613
|
|
|
614
614
|
info_cicd_generation_cancelled_by_user = info_message("** CI/CD files generation cancelled by user.")
|
|
615
|
-
|
|
616
|
-
"** Skipping sharing the datasoure {datasource} in
|
|
615
|
+
info_skipping_sharing_datasources_branch = info_message(
|
|
616
|
+
"** Skipping sharing the datasoure {datasource} in a Branch"
|
|
617
617
|
)
|
|
618
618
|
info_skipping_shared_with_entry = info_message(
|
|
619
619
|
"** Skipping `SHARED_WITH` entry as the flag --user-token was not used"
|
|
@@ -755,20 +755,20 @@ Ready? """
|
|
|
755
755
|
success_using_host = success_message("** Using host: {host} ({name})")
|
|
756
756
|
success_workspace_created = success_message("** Workspace '{workspace_name}' has been created")
|
|
757
757
|
success_workspace_branch_created = success_message(
|
|
758
|
-
"**
|
|
758
|
+
"** Branch '{branch_name}' from '{workspace_name}' has been created"
|
|
759
759
|
)
|
|
760
760
|
success_workspace_data_branch = success_message(
|
|
761
|
-
"** Partitions from 'main' Workspace have been attached to the
|
|
761
|
+
"** Partitions from 'main' Workspace have been attached to the Branch"
|
|
762
762
|
)
|
|
763
763
|
success_workspace_data_branch_in_progress = success_message(
|
|
764
|
-
"** Partitions from 'main' Workspace are being attached to the
|
|
764
|
+
"** Partitions from 'main' Workspace are being attached to the Branch in job {job_url}"
|
|
765
765
|
)
|
|
766
766
|
|
|
767
767
|
success_workspace_deploying_template = success_message(
|
|
768
768
|
"Deploying your new '{workspace_name}' workspace, using the '{template}' template:"
|
|
769
769
|
)
|
|
770
770
|
success_workspace_deleted = success_message("** Workspace '{workspace_name}' deleted")
|
|
771
|
-
success_branch_deleted = success_message("**
|
|
771
|
+
success_branch_deleted = success_message("** Branch '{branch_name}' deleted")
|
|
772
772
|
success_workspace_user_added = success_message("** User {user} added to workspace '{workspace_name}'")
|
|
773
773
|
success_workspace_users_added = success_message("** Users added to workspace '{workspace_name}'")
|
|
774
774
|
success_workspace_user_removed = success_message("** User {user} removed from workspace '{workspace_name}'")
|
|
@@ -266,15 +266,15 @@ async def release_rm(semver: str, force: bool, yes: bool, dry_run: bool) -> None
|
|
|
266
266
|
|
|
267
267
|
|
|
268
268
|
@cli.group()
|
|
269
|
-
def
|
|
270
|
-
"""
|
|
269
|
+
def branch() -> None:
|
|
270
|
+
"""Branch commands. Branches are an experimental feature only available in beta. Running branch commands without activation will return an error"""
|
|
271
271
|
pass
|
|
272
272
|
|
|
273
273
|
|
|
274
|
-
@
|
|
274
|
+
@branch.command(name="ls")
|
|
275
275
|
@coro
|
|
276
276
|
async def branch_ls() -> None:
|
|
277
|
-
"""List all the
|
|
277
|
+
"""List all the branches available using the workspace token"""
|
|
278
278
|
|
|
279
279
|
config = CLIConfig.get_project_config()
|
|
280
280
|
_ = await try_update_config_with_remote(config, only_if_needed=True)
|
|
@@ -303,43 +303,43 @@ async def branch_ls() -> None:
|
|
|
303
303
|
echo_safe_humanfriendly_tables_format_smart_table(table, column_names=columns)
|
|
304
304
|
|
|
305
305
|
|
|
306
|
-
@
|
|
307
|
-
@click.argument("
|
|
306
|
+
@branch.command(name="use")
|
|
307
|
+
@click.argument("branch_name_or_id")
|
|
308
308
|
@coro
|
|
309
|
-
async def branch_use(
|
|
310
|
-
"""Switch to another
|
|
309
|
+
async def branch_use(branch_name_or_id: str) -> None:
|
|
310
|
+
"""Switch to another Branch (requires an admin token associated with a user). Use 'tb branch ls' to list the Branches you can access"""
|
|
311
311
|
|
|
312
312
|
config = CLIConfig.get_project_config()
|
|
313
313
|
|
|
314
|
-
if
|
|
314
|
+
if branch_name_or_id == MAIN_BRANCH:
|
|
315
315
|
current_main_workspace = await get_current_main_workspace_temp(config)
|
|
316
316
|
assert isinstance(current_main_workspace, dict)
|
|
317
317
|
await switch_to_workspace_by_user_workspace_data(config, current_main_workspace)
|
|
318
318
|
else:
|
|
319
|
-
await switch_workspace(config,
|
|
319
|
+
await switch_workspace(config, branch_name_or_id, only_environments=True)
|
|
320
320
|
|
|
321
321
|
|
|
322
|
-
@
|
|
322
|
+
@branch.command(name="current")
|
|
323
323
|
@coro
|
|
324
324
|
async def branch_current() -> None:
|
|
325
|
-
"""Show the
|
|
325
|
+
"""Show the Branch you're currently authenticated to"""
|
|
326
326
|
config = CLIConfig.get_project_config()
|
|
327
327
|
await print_current_branch(config)
|
|
328
328
|
|
|
329
329
|
|
|
330
|
-
@
|
|
331
|
-
@click.argument("
|
|
330
|
+
@branch.command(name="create", short_help="Create a new Branch in the current 'main' Workspace")
|
|
331
|
+
@click.argument("branch_name", required=False)
|
|
332
332
|
@click.option(
|
|
333
333
|
"--last-partition",
|
|
334
334
|
is_flag=True,
|
|
335
335
|
default=False,
|
|
336
|
-
help="Attach the last modified partition from 'main' to the new
|
|
336
|
+
help="Attach the last modified partition from 'main' to the new Branch",
|
|
337
337
|
)
|
|
338
338
|
@click.option(
|
|
339
339
|
"--all",
|
|
340
340
|
is_flag=True,
|
|
341
341
|
default=False,
|
|
342
|
-
help="Attach all data from 'main' to the new
|
|
342
|
+
help="Attach all data from 'main' to the new Branch. Use only if you actually need all the data in the Branch",
|
|
343
343
|
)
|
|
344
344
|
@click.option(
|
|
345
345
|
"-i",
|
|
@@ -357,26 +357,26 @@ async def branch_current() -> None:
|
|
|
357
357
|
)
|
|
358
358
|
@coro
|
|
359
359
|
async def create_branch(
|
|
360
|
-
|
|
360
|
+
branch_name: Optional[str], last_partition: bool, all: bool, ignore_datasources: List[str], wait: bool
|
|
361
361
|
) -> None:
|
|
362
362
|
if last_partition and all:
|
|
363
363
|
raise CLIException(FeedbackManager.error_exception(error="Use --last-partition or --all but not both"))
|
|
364
|
-
await create_workspace_branch(
|
|
364
|
+
await create_workspace_branch(branch_name, last_partition, all, list(ignore_datasources), wait)
|
|
365
365
|
|
|
366
366
|
|
|
367
|
-
@
|
|
368
|
-
@click.argument("
|
|
367
|
+
@branch.command(name="rm", short_help="Removes a Branch from the Workspace. It can't be recovered.")
|
|
368
|
+
@click.argument("branch_name_or_id")
|
|
369
369
|
@click.option("--yes", is_flag=True, default=False, help="Do not ask for confirmation")
|
|
370
370
|
@coro
|
|
371
|
-
async def delete_branch(
|
|
372
|
-
"""Remove an
|
|
371
|
+
async def delete_branch(branch_name_or_id: str, yes: bool) -> None:
|
|
372
|
+
"""Remove an Branch (not Main)"""
|
|
373
373
|
|
|
374
374
|
config = CLIConfig.get_project_config()
|
|
375
375
|
_ = await try_update_config_with_remote(config)
|
|
376
376
|
|
|
377
377
|
client = config.get_client()
|
|
378
378
|
|
|
379
|
-
if
|
|
379
|
+
if branch_name_or_id == MAIN_BRANCH:
|
|
380
380
|
raise CLIException(FeedbackManager.error_not_allowed_in_main_branch())
|
|
381
381
|
|
|
382
382
|
try:
|
|
@@ -385,7 +385,7 @@ async def delete_branch(env_name_or_id: str, yes: bool) -> None:
|
|
|
385
385
|
(
|
|
386
386
|
workspace
|
|
387
387
|
for workspace in workspace_branches
|
|
388
|
-
if workspace["name"] ==
|
|
388
|
+
if workspace["name"] == branch_name_or_id or workspace["id"] == branch_name_or_id
|
|
389
389
|
),
|
|
390
390
|
None,
|
|
391
391
|
)
|
|
@@ -393,7 +393,7 @@ async def delete_branch(env_name_or_id: str, yes: bool) -> None:
|
|
|
393
393
|
raise CLIBranchException(FeedbackManager.error_exception(error=str(e)))
|
|
394
394
|
|
|
395
395
|
if not workspace_to_delete:
|
|
396
|
-
raise CLIBranchException(FeedbackManager.error_branch(branch=
|
|
396
|
+
raise CLIBranchException(FeedbackManager.error_branch(branch=branch_name_or_id))
|
|
397
397
|
|
|
398
398
|
if yes or click.confirm(FeedbackManager.warning_confirm_delete_branch(branch=workspace_to_delete["name"])):
|
|
399
399
|
need_to_switch_to_main = workspace_to_delete.get("main") and config["id"] == workspace_to_delete["id"]
|
|
@@ -419,21 +419,21 @@ async def delete_branch(env_name_or_id: str, yes: bool) -> None:
|
|
|
419
419
|
raise CLIException(FeedbackManager.error_switching_to_main())
|
|
420
420
|
|
|
421
421
|
|
|
422
|
-
@
|
|
422
|
+
@branch.command(
|
|
423
423
|
name="data",
|
|
424
|
-
short_help="Perform a data branch operation to bring data into the current
|
|
424
|
+
short_help="Perform a data branch operation to bring data into the current Branch. Check flags for details",
|
|
425
425
|
)
|
|
426
426
|
@click.option(
|
|
427
427
|
"--last-partition",
|
|
428
428
|
is_flag=True,
|
|
429
429
|
default=False,
|
|
430
|
-
help="Attach the last modified partition from 'main' to the new
|
|
430
|
+
help="Attach the last modified partition from 'main' to the new Branch",
|
|
431
431
|
)
|
|
432
432
|
@click.option(
|
|
433
433
|
"--all",
|
|
434
434
|
is_flag=True,
|
|
435
435
|
default=False,
|
|
436
|
-
help="Attach all data from 'main' to the new
|
|
436
|
+
help="Attach all data from 'main' to the new Branch. Use only if you actually need all the data in the Branch",
|
|
437
437
|
)
|
|
438
438
|
@click.option(
|
|
439
439
|
"-i",
|
|
@@ -482,7 +482,7 @@ async def data_branch(last_partition: bool, all: bool, ignore_datasources: List[
|
|
|
482
482
|
job_id = response["job"]["job_id"]
|
|
483
483
|
job_url = response["job"]["job_url"]
|
|
484
484
|
click.echo(FeedbackManager.info_data_branch_job_url(url=job_url))
|
|
485
|
-
job_response = await wait_job(client, job_id, job_url, "
|
|
485
|
+
job_response = await wait_job(client, job_id, job_url, "Branch creation")
|
|
486
486
|
response = job_response["result"]
|
|
487
487
|
is_job = False
|
|
488
488
|
is_summary = "partitions" in response
|
|
@@ -500,7 +500,7 @@ async def data_branch(last_partition: bool, all: bool, ignore_datasources: List[
|
|
|
500
500
|
raise CLIBranchException(FeedbackManager.error_exception(error=str(e)))
|
|
501
501
|
|
|
502
502
|
|
|
503
|
-
@
|
|
503
|
+
@branch.group("regression-tests", invoke_without_command=True)
|
|
504
504
|
@click.option(
|
|
505
505
|
"-f",
|
|
506
506
|
"--filename",
|
|
@@ -518,20 +518,20 @@ async def data_branch(last_partition: bool, all: bool, ignore_datasources: List[
|
|
|
518
518
|
"--skip-regression-tests/--no-skip-regression-tests",
|
|
519
519
|
envvar="TB_SKIP_REGRESSION",
|
|
520
520
|
default=False,
|
|
521
|
-
help="Flag to skip execution of regression tests. This is handy for CI
|
|
521
|
+
help="Flag to skip execution of regression tests. This is handy for CI branches where regression might be flaky",
|
|
522
522
|
)
|
|
523
523
|
@click.option(
|
|
524
524
|
"--main",
|
|
525
525
|
is_flag=True,
|
|
526
526
|
default=False,
|
|
527
|
-
help="Run regression tests in the main
|
|
527
|
+
help="Run regression tests in the main Branch. For this flag to work all the resources in the Branch pipe endpoints need to exist in the main Branch.",
|
|
528
528
|
)
|
|
529
529
|
@click.pass_context
|
|
530
530
|
@coro
|
|
531
531
|
async def regression_tests(
|
|
532
532
|
ctx, filename: str, wait: bool, skip_regression_tests: Optional[bool] = False, main: Optional[bool] = False
|
|
533
533
|
):
|
|
534
|
-
"""Regression test commands for
|
|
534
|
+
"""Regression test commands for Branches"""
|
|
535
535
|
if skip_regression_tests:
|
|
536
536
|
click.echo(FeedbackManager.warning_regression_skipped())
|
|
537
537
|
return
|
|
@@ -633,7 +633,7 @@ async def _run_regression(
|
|
|
633
633
|
|
|
634
634
|
@regression_tests.command(
|
|
635
635
|
name="coverage",
|
|
636
|
-
short_help="Run regression tests using coverage requests for
|
|
636
|
+
short_help="Run regression tests using coverage requests for Branch vs Main Workspace. It creates a regression-tests job. The argument pipe_name supports regular expressions. Using '.*' if no pipe_name is provided",
|
|
637
637
|
)
|
|
638
638
|
@click.argument("pipe_name", required=False)
|
|
639
639
|
@click.option(
|
|
@@ -694,13 +694,13 @@ async def _run_regression(
|
|
|
694
694
|
"--skip-regression-tests/--no-skip-regression-tests",
|
|
695
695
|
envvar="TB_SKIP_REGRESSION",
|
|
696
696
|
default=False,
|
|
697
|
-
help="Flag to skip execution of regression tests. This is handy for CI
|
|
697
|
+
help="Flag to skip execution of regression tests. This is handy for CI branches where regression might be flaky",
|
|
698
698
|
)
|
|
699
699
|
@click.option(
|
|
700
700
|
"--main",
|
|
701
701
|
is_flag=True,
|
|
702
702
|
default=False,
|
|
703
|
-
help="Run regression tests in the main
|
|
703
|
+
help="Run regression tests in the main Branch. For this flag to work all the resources in the Branch pipe endpoints need to exist in the main Branch.",
|
|
704
704
|
)
|
|
705
705
|
@coro
|
|
706
706
|
async def coverage(
|
|
@@ -736,7 +736,7 @@ async def coverage(
|
|
|
736
736
|
|
|
737
737
|
@regression_tests.command(
|
|
738
738
|
name="last",
|
|
739
|
-
short_help="Run regression tests using last requests for
|
|
739
|
+
short_help="Run regression tests using last requests for Branch vs Main Workspace. It creates a regression-tests job. The argument pipe_name supports regular expressions. Using '.*' if no pipe_name is provided",
|
|
740
740
|
)
|
|
741
741
|
@click.argument("pipe_name", required=False)
|
|
742
742
|
@click.option(
|
|
@@ -805,7 +805,7 @@ async def coverage(
|
|
|
805
805
|
"--skip-regression-tests/--no-skip-regression-tests",
|
|
806
806
|
envvar="TB_SKIP_REGRESSION",
|
|
807
807
|
default=False,
|
|
808
|
-
help="Flag to skip execution of regression tests. This is handy for CI
|
|
808
|
+
help="Flag to skip execution of regression tests. This is handy for CI branches where regression might be flaky",
|
|
809
809
|
)
|
|
810
810
|
@coro
|
|
811
811
|
async def last(
|
|
@@ -841,7 +841,7 @@ async def last(
|
|
|
841
841
|
|
|
842
842
|
@regression_tests.command(
|
|
843
843
|
name="manual",
|
|
844
|
-
short_help="Run regression tests using manual requests for
|
|
844
|
+
short_help="Run regression tests using manual requests for Branch vs Main Workspace. It creates a regression-tests job. The argument pipe_name supports regular expressions. Using '.*' if no pipe_name is provided",
|
|
845
845
|
context_settings=dict(allow_extra_args=True, ignore_unknown_options=True),
|
|
846
846
|
)
|
|
847
847
|
@click.argument("pipe_name", required=False)
|
|
@@ -903,7 +903,7 @@ async def last(
|
|
|
903
903
|
"--skip-regression-tests/--no-skip-regression-tests",
|
|
904
904
|
envvar="TB_SKIP_REGRESSION",
|
|
905
905
|
default=False,
|
|
906
|
-
help="Flag to skip execution of regression tests. This is handy for CI
|
|
906
|
+
help="Flag to skip execution of regression tests. This is handy for CI branches where regression might be flaky",
|
|
907
907
|
)
|
|
908
908
|
@click.pass_context
|
|
909
909
|
@coro
|
|
@@ -939,9 +939,9 @@ async def manual(
|
|
|
939
939
|
)
|
|
940
940
|
|
|
941
941
|
|
|
942
|
-
@
|
|
942
|
+
@branch.group()
|
|
943
943
|
def datasource() -> None:
|
|
944
|
-
"""
|
|
944
|
+
"""Branch data source commands."""
|
|
945
945
|
|
|
946
946
|
|
|
947
947
|
@datasource.command(name="copy")
|
|
@@ -949,7 +949,7 @@ def datasource() -> None:
|
|
|
949
949
|
@click.option(
|
|
950
950
|
"--sql",
|
|
951
951
|
default=None,
|
|
952
|
-
help="Freeform SQL query to select what is copied from Main into the
|
|
952
|
+
help="Freeform SQL query to select what is copied from Main into the Branch Data Source",
|
|
953
953
|
required=False,
|
|
954
954
|
)
|
|
955
955
|
@click.option(
|
|
@@ -16,6 +16,10 @@ class Provider(Enum):
|
|
|
16
16
|
GitLab = 1
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
WORKFLOW_VERSION = "v2.5.1"
|
|
20
|
+
|
|
21
|
+
TB_DEPLOY = "false"
|
|
22
|
+
|
|
19
23
|
GITHUB_CI_YML = """
|
|
20
24
|
##################################################
|
|
21
25
|
### Visit https://github.com/tinybirdco/ci ###
|
|
@@ -37,10 +41,10 @@ GITHUB_CI_YML = """
|
|
|
37
41
|
concurrency: ${{! github.workflow }}-${{! github.event.pull_request.number }}
|
|
38
42
|
|
|
39
43
|
jobs:
|
|
40
|
-
ci: # ci using
|
|
41
|
-
uses: tinybirdco/ci/.github/workflows/ci.yml@
|
|
44
|
+
ci: # ci using branches from workspace '{{ workspace_name }}'
|
|
45
|
+
uses: tinybirdco/ci/.github/workflows/ci.yml@{{ workflow_version }}
|
|
42
46
|
with:
|
|
43
|
-
tb_deploy:
|
|
47
|
+
tb_deploy: {{ tb_deploy }}
|
|
44
48
|
data_project_dir: {{ data_project_dir }}
|
|
45
49
|
secrets:
|
|
46
50
|
tb_admin_token: ${{! secrets.TB_ADMIN_TOKEN }} # set the Workspace admin token in GitHub secrets
|
|
@@ -65,9 +69,9 @@ GITHUB_CD_YML = """
|
|
|
65
69
|
- '{{ data_project_dir }}/**'{% end %}
|
|
66
70
|
jobs:
|
|
67
71
|
cd: # deploy changes to workspace '{{ workspace_name }}'
|
|
68
|
-
uses: tinybirdco/ci/.github/workflows/cd.yml@
|
|
72
|
+
uses: tinybirdco/ci/.github/workflows/cd.yml@{{ workflow_version }}
|
|
69
73
|
with:
|
|
70
|
-
tb_deploy:
|
|
74
|
+
tb_deploy: {{ tb_deploy }}
|
|
71
75
|
data_project_dir: {{ data_project_dir }}
|
|
72
76
|
secrets:
|
|
73
77
|
tb_admin_token: ${{! secrets.TB_ADMIN_TOKEN }} # set the Workspace admin token in GitHub secrets
|
|
@@ -92,11 +96,11 @@ GITHUB_RELEASES_YML = """
|
|
|
92
96
|
|
|
93
97
|
jobs:
|
|
94
98
|
cd: # manage releases for workspace '{{ workspace_name }}'
|
|
95
|
-
uses: tinybirdco/ci/.github/workflows/release.yml@
|
|
99
|
+
uses: tinybirdco/ci/.github/workflows/release.yml@{{ workflow_version }}
|
|
96
100
|
with:
|
|
97
101
|
job_to_run: ${{! inputs.job_to_run }}
|
|
98
102
|
data_project_dir: {{ data_project_dir }}
|
|
99
|
-
tb_deploy:
|
|
103
|
+
tb_deploy: {{ tb_deploy }}
|
|
100
104
|
secrets:
|
|
101
105
|
tb_admin_token: ${{! secrets.TB_ADMIN_TOKEN }} # set the Workspace admin token in GitHub secrets
|
|
102
106
|
tb_host: {{ tb_host }}
|
|
@@ -109,7 +113,7 @@ GITLAB_YML = """
|
|
|
109
113
|
### for more details or custom CI/CD ###
|
|
110
114
|
##################################################
|
|
111
115
|
|
|
112
|
-
include: "https://raw.githubusercontent.com/tinybirdco/ci/
|
|
116
|
+
include: "https://raw.githubusercontent.com/tinybirdco/ci/{{ workflow_version }}/.gitlab/ci_cd.yaml"
|
|
113
117
|
|
|
114
118
|
.ci_config_rules:
|
|
115
119
|
- &ci_config_rule
|
|
@@ -136,9 +140,9 @@ GITLAB_YML = """
|
|
|
136
140
|
TB_HOST: "{{ tb_host }}"
|
|
137
141
|
TB_ADMIN_TOKEN: $TB_ADMIN_TOKEN # set the Workspace admin token in GitLab CI/CD Variables
|
|
138
142
|
DATA_PROJECT_DIR: "{{ data_project_dir }}"
|
|
139
|
-
TB_DEPLOY: "
|
|
143
|
+
TB_DEPLOY: "{{ tb_deploy }}"
|
|
140
144
|
|
|
141
|
-
run_ci: # ci using
|
|
145
|
+
run_ci: # ci using branches from workspace '{{ workspace_name }}'
|
|
142
146
|
extends: .run_ci
|
|
143
147
|
rules:
|
|
144
148
|
- *ci_config_rule
|
|
@@ -305,6 +309,8 @@ async def init_cicd(
|
|
|
305
309
|
"token_name": token["name"],
|
|
306
310
|
"token_id": token["id"],
|
|
307
311
|
"data_project_dir": data_project_dir,
|
|
312
|
+
"workflow_version": WORKFLOW_VERSION,
|
|
313
|
+
"tb_deploy": TB_DEPLOY,
|
|
308
314
|
}
|
|
309
315
|
warning_message = generator(path, params)
|
|
310
316
|
if warning_message:
|
|
@@ -40,6 +40,7 @@ from tinybird.datafile import (
|
|
|
40
40
|
get_project_filenames,
|
|
41
41
|
get_resource_versions,
|
|
42
42
|
has_internal_datafiles,
|
|
43
|
+
is_file_a_datasource,
|
|
43
44
|
parse_datasource,
|
|
44
45
|
parse_pipe,
|
|
45
46
|
parse_token,
|
|
@@ -764,7 +765,7 @@ async def fmt(
|
|
|
764
765
|
if not diff:
|
|
765
766
|
click.echo(filename)
|
|
766
767
|
extensions = Path(filename).suffixes
|
|
767
|
-
if
|
|
768
|
+
if is_file_a_datasource(filename):
|
|
768
769
|
result = await format_datasource(filename)
|
|
769
770
|
elif (".pipe" in extensions) or (".incl" in extensions):
|
|
770
771
|
result = await format_pipe(filename, line_length)
|
|
@@ -819,7 +820,7 @@ async def fmt(
|
|
|
819
820
|
"--main",
|
|
820
821
|
is_flag=True,
|
|
821
822
|
default=False,
|
|
822
|
-
help="Diffs local datafiles to the corresponding remote files in the main workspace. Only works when authenticated on
|
|
823
|
+
help="Diffs local datafiles to the corresponding remote files in the main workspace. Only works when authenticated on a Branch.",
|
|
823
824
|
hidden=True,
|
|
824
825
|
)
|
|
825
826
|
@click.pass_context
|
|
@@ -1482,15 +1483,15 @@ async def deploy(
|
|
|
1482
1483
|
)
|
|
1483
1484
|
|
|
1484
1485
|
semver = config.get("semver")
|
|
1485
|
-
|
|
1486
|
+
is_branch = current_ws.get("is_branch")
|
|
1486
1487
|
# FIXME: we need a better way
|
|
1487
|
-
auto_promote = (
|
|
1488
|
+
auto_promote = (is_branch and current_ws.get("name", "").startswith("tmp_ci")) or auto_promote
|
|
1488
1489
|
release = current_ws.get("release", {})
|
|
1489
1490
|
current_semver: Optional[str] = "no release in remote Workspace"
|
|
1490
1491
|
if release and isinstance(release, dict):
|
|
1491
1492
|
current_semver = release.get("semver")
|
|
1492
1493
|
|
|
1493
|
-
if not
|
|
1494
|
+
if not is_branch:
|
|
1494
1495
|
# TODO upload fixtures on tb test
|
|
1495
1496
|
fixtures = False
|
|
1496
1497
|
click.echo(FeedbackManager.info_fixtures_branch())
|
|
@@ -226,7 +226,28 @@ async def get_current_workspace_branches(config: CLIConfig) -> List[Dict[str, An
|
|
|
226
226
|
return [branch for branch in branches if branch.get("main") == current_main_workspace["id"]]
|
|
227
227
|
|
|
228
228
|
|
|
229
|
-
class
|
|
229
|
+
class AliasedGroup(click.Group):
|
|
230
|
+
def get_command(self, ctx, cmd_name):
|
|
231
|
+
# Step one: built-in commands as normal
|
|
232
|
+
cm = click.Group.get_command(self, ctx, cmd_name)
|
|
233
|
+
if cm is not None:
|
|
234
|
+
return cm
|
|
235
|
+
|
|
236
|
+
if cmd_name == "env":
|
|
237
|
+
deprecation_notice = FeedbackManager.warning_deprecated(
|
|
238
|
+
warning="'tb env' is deprecated, use 'tb branch' instead. 'tb env' will be removed in the next 3.0.0 release, please update your scripts."
|
|
239
|
+
)
|
|
240
|
+
click.echo(deprecation_notice)
|
|
241
|
+
|
|
242
|
+
return click.Group.get_command(self, ctx, "branch")
|
|
243
|
+
|
|
244
|
+
def resolve_command(self, ctx, args):
|
|
245
|
+
# always return the command's name, not the alias
|
|
246
|
+
_, cmd, args = super().resolve_command(ctx, args)
|
|
247
|
+
return cmd.name, cmd, args
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
class CatchAuthExceptions(AliasedGroup):
|
|
230
251
|
"""utility class to get all the auth exceptions"""
|
|
231
252
|
|
|
232
253
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
@@ -953,7 +974,7 @@ async def create_workspace_branch(
|
|
|
953
974
|
if not branch_name:
|
|
954
975
|
click.echo(FeedbackManager.info_workspace_branch_create_greeting())
|
|
955
976
|
default_name = f"{workspace['name']}_{uuid.uuid4().hex[0:4]}"
|
|
956
|
-
branch_name = click.prompt("\
|
|
977
|
+
branch_name = click.prompt("\Branch name", default=default_name, err=True, type=str)
|
|
957
978
|
assert isinstance(branch_name, str)
|
|
958
979
|
|
|
959
980
|
response = await config.get_client().create_workspace_branch(
|
|
@@ -988,7 +1009,7 @@ async def create_workspace_branch(
|
|
|
988
1009
|
assert isinstance(job_id, str)
|
|
989
1010
|
|
|
990
1011
|
# Await the job to finish and get the result dict
|
|
991
|
-
job_response = await wait_job(config.get_client(), job_id, job_url, "
|
|
1012
|
+
job_response = await wait_job(config.get_client(), job_id, job_url, "Branch creation")
|
|
992
1013
|
if job_response is None:
|
|
993
1014
|
raise CLIException(f"Empty job API response (job_id: {job_id}, job_url: {job_url})")
|
|
994
1015
|
else:
|
|
@@ -1056,7 +1077,7 @@ async def print_branch_regression_tests_summary(client, job_id, host, response=N
|
|
|
1056
1077
|
for step in response["progress"]:
|
|
1057
1078
|
run = step["run"]
|
|
1058
1079
|
if run.get("metrics_summary") and run.get("metrics_timing"):
|
|
1059
|
-
column_names = [f"{run['pipe_name']}({run['test_type']})", "Origin", "
|
|
1080
|
+
column_names = [f"{run['pipe_name']}({run['test_type']})", "Origin", "Branch", "Delta"]
|
|
1060
1081
|
|
|
1061
1082
|
click.echo(
|
|
1062
1083
|
format_pretty_table(
|
|
@@ -12,6 +12,7 @@ import click
|
|
|
12
12
|
from click import Context
|
|
13
13
|
|
|
14
14
|
from tinybird.client import DoesNotExistException, TinyB
|
|
15
|
+
from tinybird.connector_settings import DATA_CONNECTOR_SETTINGS, DataConnectors, DataSensitiveSettings
|
|
15
16
|
from tinybird.feedback_manager import FeedbackManager
|
|
16
17
|
from tinybird.tb_cli_modules.cli import cli
|
|
17
18
|
from tinybird.tb_cli_modules.common import (
|
|
@@ -339,11 +340,8 @@ async def connection_rm(ctx: Context, connection_id: str, force: bool) -> None:
|
|
|
339
340
|
@click.option("--connector", help="Filter by connector")
|
|
340
341
|
@click.pass_context
|
|
341
342
|
@coro
|
|
342
|
-
async def connection_ls(ctx: Context, connector:
|
|
343
|
+
async def connection_ls(ctx: Context, connector: Optional[DataConnectors] = None) -> None:
|
|
343
344
|
"""List connections."""
|
|
344
|
-
|
|
345
|
-
from tinybird.connector_settings import DataConnectorSettings, DataSensitiveSettings
|
|
346
|
-
|
|
347
345
|
obj: Dict[str, Any] = ctx.ensure_object(dict)
|
|
348
346
|
client: TinyB = obj["client"]
|
|
349
347
|
|
|
@@ -358,9 +356,9 @@ async def connection_ls(ctx: Context, connector: str) -> None:
|
|
|
358
356
|
columns = ["service", "name", "id", "connected_datasources"]
|
|
359
357
|
else:
|
|
360
358
|
sensitive_settings = getattr(DataSensitiveSettings, connector)
|
|
361
|
-
columns = ["service", "name", "id", "connected_datasources"]
|
|
362
|
-
|
|
363
|
-
|
|
359
|
+
columns = ["service", "name", "id", "connected_datasources"]
|
|
360
|
+
if connector_settings := DATA_CONNECTOR_SETTINGS.get(connector):
|
|
361
|
+
columns += [setting.replace("tb_", "") for setting in connector_settings.model_fields.keys()]
|
|
364
362
|
|
|
365
363
|
for connection in connections:
|
|
366
364
|
row = [_get_setting_value(connection, setting, sensitive_settings) for setting in columns]
|
|
@@ -741,7 +741,7 @@ async def datasource_exchange(ctx, datasource_a, datasource_b):
|
|
|
741
741
|
@click.option(
|
|
742
742
|
"--sql",
|
|
743
743
|
default=None,
|
|
744
|
-
help="Freeform SQL query to select what is copied from Main into the
|
|
744
|
+
help="Freeform SQL query to select what is copied from Main into the Branch Data Source",
|
|
745
745
|
required=False,
|
|
746
746
|
)
|
|
747
747
|
@click.option(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: tinybird-cli
|
|
3
|
-
Version: 2.1.1.
|
|
3
|
+
Version: 2.1.1.dev8
|
|
4
4
|
Summary: Tinybird Command Line Tool
|
|
5
5
|
Home-page: https://docs.tinybird.co/cli.html
|
|
6
6
|
Author: Tinybird
|
|
@@ -19,7 +19,17 @@ Changelog
|
|
|
19
19
|
|
|
20
20
|
---------
|
|
21
21
|
|
|
22
|
-
2.1.1.
|
|
22
|
+
2.1.1.dev8
|
|
23
|
+
************
|
|
24
|
+
|
|
25
|
+
- `Changed` Use `tb branch` instead of `tb env`. `tb env` is still usable but deprecated and will be removed in the next version.
|
|
26
|
+
|
|
27
|
+
2.1.1.dev7
|
|
28
|
+
************
|
|
29
|
+
|
|
30
|
+
- `Fixed` Be smarter in `tb fmt` when detecting if `.incl` file contains Data Source or Pipe definitions.
|
|
31
|
+
|
|
32
|
+
2.1.1.dev6
|
|
23
33
|
************
|
|
24
34
|
|
|
25
35
|
- `Fixed` `tb fmt` adds empty quoted strings in .datasource connection section.
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
from enum import Enum
|
|
2
|
-
from typing import List
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class DataConnectorType(str, Enum):
|
|
6
|
-
KAFKA = "kafka"
|
|
7
|
-
GCLOUD_SCHEDULER = "gcscheduler"
|
|
8
|
-
SNOWFLAKE = "snowflake"
|
|
9
|
-
BIGQUERY = "bigquery"
|
|
10
|
-
GCLOUD_STORAGE = "gcs"
|
|
11
|
-
GCLOUD_STORAGE_HMAC = "gcs_hmac"
|
|
12
|
-
AMAZON_S3 = "s3"
|
|
13
|
-
AMAZON_S3_IAMROLE = "s3_iamrole"
|
|
14
|
-
|
|
15
|
-
def __str__(self) -> str:
|
|
16
|
-
return self.value
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class DataConnectorSettings:
|
|
20
|
-
kafka = [
|
|
21
|
-
"cli_version",
|
|
22
|
-
"tb_endpoint",
|
|
23
|
-
"kafka_bootstrap_servers",
|
|
24
|
-
"kafka_sasl_plain_username",
|
|
25
|
-
"kafka_sasl_plain_password",
|
|
26
|
-
"kafka_security_protocol",
|
|
27
|
-
"kafka_sasl_mechanism",
|
|
28
|
-
"kafka_schema_registry_url",
|
|
29
|
-
]
|
|
30
|
-
gcscheduler = ["gcscheduler_region"]
|
|
31
|
-
bigquery = ["account"]
|
|
32
|
-
snowflake = ["account", "username", "password", "warehouse", "warehouse_size", "role", "stage", "integration"]
|
|
33
|
-
gcs_hmac = ["gcs_hmac_access_id", "gcs_hmac_secret"]
|
|
34
|
-
s3 = [
|
|
35
|
-
"s3_access_key_id",
|
|
36
|
-
"s3_secret_access_key",
|
|
37
|
-
"s3_region",
|
|
38
|
-
]
|
|
39
|
-
s3_iamrole = [
|
|
40
|
-
"s3_iamrole_arn",
|
|
41
|
-
"s3_iamrole_external_id",
|
|
42
|
-
"s3_iamrole_region",
|
|
43
|
-
]
|
|
44
|
-
gcs = [
|
|
45
|
-
"gcs_private_key_id",
|
|
46
|
-
"gcs_client_x509_cert_url",
|
|
47
|
-
"gcs_project_id",
|
|
48
|
-
"gcs_client_id",
|
|
49
|
-
"gcs_client_email",
|
|
50
|
-
"gcs_private_key",
|
|
51
|
-
]
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
class DataLinkerSettings:
|
|
55
|
-
kafka = [
|
|
56
|
-
"tb_datasource",
|
|
57
|
-
"tb_token",
|
|
58
|
-
"kafka_topic",
|
|
59
|
-
"kafka_group_id",
|
|
60
|
-
"kafka_auto_offset_reset",
|
|
61
|
-
"kafka_store_raw_value",
|
|
62
|
-
"kafka_store_headers",
|
|
63
|
-
]
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
class DataSinkSettings:
|
|
67
|
-
gcscheduler = ["cron", "timezone", "status", "gcscheduler_target_url", "gcscheduler_job_name", "gcscheduler_region"]
|
|
68
|
-
gcs_hmac = [
|
|
69
|
-
"bucket_path",
|
|
70
|
-
"file_template",
|
|
71
|
-
"partition_node",
|
|
72
|
-
"format",
|
|
73
|
-
"compression",
|
|
74
|
-
]
|
|
75
|
-
s3 = [
|
|
76
|
-
"bucket_path",
|
|
77
|
-
"file_template",
|
|
78
|
-
"partition_node",
|
|
79
|
-
"format",
|
|
80
|
-
"compression",
|
|
81
|
-
]
|
|
82
|
-
s3_iamrole = [
|
|
83
|
-
"bucket_path",
|
|
84
|
-
"file_template",
|
|
85
|
-
"partition_node",
|
|
86
|
-
"format",
|
|
87
|
-
"compression",
|
|
88
|
-
]
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
class DataSensitiveSettings:
|
|
92
|
-
kafka = ["kafka_sasl_plain_password"]
|
|
93
|
-
gcscheduler = [
|
|
94
|
-
"gcscheduler_target_url",
|
|
95
|
-
"gcscheduler_job_name",
|
|
96
|
-
"gcscheduler_region",
|
|
97
|
-
]
|
|
98
|
-
bigquery: List[str] = []
|
|
99
|
-
snowflake: List[str] = []
|
|
100
|
-
gcs_hmac: List[str] = ["gcs_hmac_secret"]
|
|
101
|
-
s3: List[str] = ["s3_secret_access_key"]
|
|
102
|
-
s3_iamrole: List[str] = ["s3_iamrole_arn"]
|
|
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-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/tb_cli_modules/tinyunit/tinyunit.py
RENAMED
|
File without changes
|
{tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird/tb_cli_modules/workspace_members.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tinybird-cli-2.1.1.dev6 → tinybird-cli-2.1.1.dev8}/tinybird_cli.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|