tinybird-cli 5.8.0.dev0__tar.gz → 5.8.0.dev2__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-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/PKG-INFO +11 -1
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/__cli__.py +2 -2
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/client.py +33 -9
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/datafile.py +56 -27
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/feedback_manager.py +11 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/tb_cli.py +2 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/tb_cli_modules/cli.py +0 -84
- tinybird-cli-5.8.0.dev2/tinybird/tb_cli_modules/fmt.py +90 -0
- tinybird-cli-5.8.0.dev2/tinybird/tb_cli_modules/tag.py +116 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird_cli.egg-info/PKG-INFO +11 -1
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird_cli.egg-info/SOURCES.txt +2 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/setup.cfg +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/ch_utils/constants.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/ch_utils/engine.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/check_pypi.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/config.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/connectors.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/context.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/datatypes.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/git_settings.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/sql.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/sql_template.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/sql_template_fmt.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/sql_toolset.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/syncasync.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/tb_cli_modules/auth.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/tb_cli_modules/branch.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/tb_cli_modules/cicd.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/tb_cli_modules/common.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/tb_cli_modules/config.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/tb_cli_modules/connection.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/tb_cli_modules/datasource.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/tb_cli_modules/exceptions.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/tb_cli_modules/job.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/tb_cli_modules/pipe.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/tb_cli_modules/regions.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/tb_cli_modules/telemetry.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/tb_cli_modules/test.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/tb_cli_modules/token.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/tb_cli_modules/workspace.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/tb_cli_modules/workspace_members.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/tornado_template.py +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird_cli.egg-info/dependency_links.txt +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird_cli.egg-info/entry_points.txt +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird_cli.egg-info/requires.txt +0 -0
- {tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/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: 5.8.0.
|
|
3
|
+
Version: 5.8.0.dev2
|
|
4
4
|
Summary: Tinybird Command Line Tool
|
|
5
5
|
Home-page: https://www.tinybird.co/docs/cli/introduction.html
|
|
6
6
|
Author: Tinybird
|
|
@@ -18,6 +18,16 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
|
|
|
18
18
|
Changelog
|
|
19
19
|
----------
|
|
20
20
|
|
|
21
|
+
5.8.0.dev2
|
|
22
|
+
***********
|
|
23
|
+
|
|
24
|
+
- `Added` support to `TAGS` in `tb fmt`.
|
|
25
|
+
|
|
26
|
+
5.8.0.dev1
|
|
27
|
+
***********
|
|
28
|
+
|
|
29
|
+
- `Added` new `tb tag` command.
|
|
30
|
+
|
|
21
31
|
5.8.0.dev0
|
|
22
32
|
***********
|
|
23
33
|
|
|
@@ -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__ = '5.8.0.
|
|
8
|
-
__revision__ = '
|
|
7
|
+
__version__ = '5.8.0.dev2'
|
|
8
|
+
__revision__ = 'edc984c'
|
|
@@ -103,12 +103,20 @@ class TinyB(object):
|
|
|
103
103
|
self.semver = semver
|
|
104
104
|
|
|
105
105
|
async def _req(
|
|
106
|
-
self,
|
|
106
|
+
self,
|
|
107
|
+
endpoint: str,
|
|
108
|
+
data=None,
|
|
109
|
+
files=None,
|
|
110
|
+
method: str = "GET",
|
|
111
|
+
retries: int = LIMIT_RETRIES,
|
|
112
|
+
use_token: Optional[str] = None,
|
|
113
|
+
**kwargs,
|
|
107
114
|
): # noqa: C901
|
|
108
115
|
url = f"{self.host.strip('/')}/{endpoint.strip('/')}"
|
|
109
116
|
|
|
110
|
-
if self.token
|
|
111
|
-
|
|
117
|
+
token_to_use = use_token if use_token else self.token
|
|
118
|
+
if token_to_use:
|
|
119
|
+
url += ("&" if "?" in endpoint else "?") + "token=" + token_to_use
|
|
112
120
|
if self.version:
|
|
113
121
|
url += ("&" if "?" in url else "?") + "cli_version=" + quote(self.version)
|
|
114
122
|
if self.semver:
|
|
@@ -162,7 +170,7 @@ class TinyB(object):
|
|
|
162
170
|
|
|
163
171
|
if response.status_code == 403:
|
|
164
172
|
error = parse_error_response(response)
|
|
165
|
-
if not
|
|
173
|
+
if not token_to_use:
|
|
166
174
|
raise AuthNoTokenException(f"Forbidden: {error}")
|
|
167
175
|
raise AuthException(f"Forbidden: {error}")
|
|
168
176
|
if response.status_code == 204 or response.status_code == 205:
|
|
@@ -1211,10 +1219,12 @@ class TinyB(object):
|
|
|
1211
1219
|
async def check_auth_login(self) -> Dict[str, Any]:
|
|
1212
1220
|
return await self._req("/v0/auth")
|
|
1213
1221
|
|
|
1214
|
-
async def get_all_tags(self) -> Dict[str, Any]:
|
|
1215
|
-
return await self._req("/v0/tags")
|
|
1222
|
+
async def get_all_tags(self, token: Optional[str] = None) -> Dict[str, Any]:
|
|
1223
|
+
return await self._req("/v0/tags", use_token=token)
|
|
1216
1224
|
|
|
1217
|
-
async def create_tag_with_resource(
|
|
1225
|
+
async def create_tag_with_resource(
|
|
1226
|
+
self, name: str, resource_id: str, resource_name: str, resource_type: str, token: Optional[str] = None
|
|
1227
|
+
):
|
|
1218
1228
|
return await self._req(
|
|
1219
1229
|
"/v0/tags",
|
|
1220
1230
|
method="POST",
|
|
@@ -1222,12 +1232,22 @@ class TinyB(object):
|
|
|
1222
1232
|
data=json.dumps(
|
|
1223
1233
|
{
|
|
1224
1234
|
"name": name,
|
|
1225
|
-
"resources": [{"id":
|
|
1235
|
+
"resources": [{"id": resource_id, "name": resource_name, "type": resource_type}],
|
|
1226
1236
|
}
|
|
1227
1237
|
),
|
|
1238
|
+
use_token=token,
|
|
1228
1239
|
)
|
|
1229
1240
|
|
|
1230
|
-
async def
|
|
1241
|
+
async def create_tag(self, name: str, token: Optional[str] = None):
|
|
1242
|
+
return await self._req(
|
|
1243
|
+
"/v0/tags",
|
|
1244
|
+
method="POST",
|
|
1245
|
+
headers={"Content-Type": "application/json"},
|
|
1246
|
+
data=json.dumps({"name": name}),
|
|
1247
|
+
use_token=token,
|
|
1248
|
+
)
|
|
1249
|
+
|
|
1250
|
+
async def update_tag(self, name: str, resources: List[Dict[str, Any]], token: Optional[str] = None):
|
|
1231
1251
|
await self._req(
|
|
1232
1252
|
f"/v0/tags/{name}",
|
|
1233
1253
|
method="PUT",
|
|
@@ -1237,4 +1257,8 @@ class TinyB(object):
|
|
|
1237
1257
|
"resources": resources,
|
|
1238
1258
|
}
|
|
1239
1259
|
),
|
|
1260
|
+
use_token=token,
|
|
1240
1261
|
)
|
|
1262
|
+
|
|
1263
|
+
async def delete_tag(self, name: str, token: Optional[str] = None):
|
|
1264
|
+
await self._req(f"/v0/tags/{name}", method="DELETE", use_token=token)
|
|
@@ -904,30 +904,31 @@ def eval_var(s: str, skip: bool = False) -> str:
|
|
|
904
904
|
return Template(s).safe_substitute(os.environ)
|
|
905
905
|
|
|
906
906
|
|
|
907
|
-
def parse_tags(tags: str) -> Tuple[
|
|
907
|
+
def parse_tags(tags: str) -> Tuple[str, List[str]]:
|
|
908
908
|
"""
|
|
909
909
|
Parses a string of tags into:
|
|
910
|
-
- kv_tags: a
|
|
911
|
-
|
|
910
|
+
- kv_tags: a string of key-value tags: the previous tags we have for operational purposes. It
|
|
911
|
+
has the format key=value&key2=value2 (with_staging=true&with_last_date=true)
|
|
912
912
|
- filtering_tags: a list of tags that are used for filtering.
|
|
913
913
|
|
|
914
914
|
Example: "with_staging=true&with_last_date=true,billing,stats" ->
|
|
915
915
|
kv_tags = {"with_staging": "true", "with_last_date": "true"}
|
|
916
916
|
filtering_tags = ["billing", "stats"]
|
|
917
917
|
"""
|
|
918
|
-
kv_tags =
|
|
918
|
+
kv_tags = []
|
|
919
919
|
filtering_tags = []
|
|
920
920
|
|
|
921
921
|
entries = tags.split(",")
|
|
922
922
|
for entry in entries:
|
|
923
923
|
trimmed_entry = entry.strip()
|
|
924
924
|
if "=" in trimmed_entry:
|
|
925
|
-
|
|
926
|
-
kv_tags.update(the_tags)
|
|
925
|
+
kv_tags.append(trimmed_entry)
|
|
927
926
|
else:
|
|
928
927
|
filtering_tags.append(trimmed_entry)
|
|
929
928
|
|
|
930
|
-
|
|
929
|
+
all_kv_tags = "&".join(kv_tags)
|
|
930
|
+
|
|
931
|
+
return all_kv_tags, filtering_tags
|
|
931
932
|
|
|
932
933
|
|
|
933
934
|
def parse(
|
|
@@ -1128,14 +1129,19 @@ def parse(
|
|
|
1128
1129
|
return _f
|
|
1129
1130
|
|
|
1130
1131
|
def tags(*args: str, **kwargs: Any) -> None:
|
|
1132
|
+
raw_tags = _unquote((" ".join(args)).strip())
|
|
1133
|
+
operational_tags, filtering_tags = parse_tags(raw_tags)
|
|
1134
|
+
|
|
1131
1135
|
# Pipe nodes or Data Sources
|
|
1132
|
-
if parser_state.current_node:
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1136
|
+
if parser_state.current_node and operational_tags:
|
|
1137
|
+
operational_tags_args = (operational_tags,)
|
|
1138
|
+
assign_node_var("tags")(*operational_tags_args, **kwargs)
|
|
1139
|
+
|
|
1140
|
+
if filtering_tags:
|
|
1141
|
+
if doc.filtering_tags is None:
|
|
1142
|
+
doc.filtering_tags = filtering_tags
|
|
1143
|
+
else:
|
|
1144
|
+
doc.filtering_tags += filtering_tags
|
|
1139
1145
|
|
|
1140
1146
|
cmds = {
|
|
1141
1147
|
"from": assign("from"),
|
|
@@ -1552,9 +1558,8 @@ async def process_file(
|
|
|
1552
1558
|
del params["format"]
|
|
1553
1559
|
|
|
1554
1560
|
if "tags" in node:
|
|
1555
|
-
tags,
|
|
1561
|
+
tags = {k: v[0] for k, v in urllib.parse.parse_qs(node["tags"]).items()}
|
|
1556
1562
|
params.update(tags)
|
|
1557
|
-
doc.filtering_tags = filtering_tags
|
|
1558
1563
|
|
|
1559
1564
|
resources: List[Dict[str, Any]] = []
|
|
1560
1565
|
|
|
@@ -1654,7 +1659,7 @@ async def process_file(
|
|
|
1654
1659
|
sql = re.sub("([\t \\n']+|^)" + old + "([\t \\n'\\)]+|$)", "\\1" + new + "\\2", sql)
|
|
1655
1660
|
|
|
1656
1661
|
if "tags" in node:
|
|
1657
|
-
tags,
|
|
1662
|
+
tags = {k: v[0] for k, v in urllib.parse.parse_qs(node["tags"]).items()}
|
|
1658
1663
|
params.update(tags)
|
|
1659
1664
|
|
|
1660
1665
|
nodes.append(
|
|
@@ -2686,7 +2691,7 @@ def show_materialized_view_warnings(warnings):
|
|
|
2686
2691
|
)
|
|
2687
2692
|
|
|
2688
2693
|
|
|
2689
|
-
async def update_tags(
|
|
2694
|
+
async def update_tags(resource_id: str, resource_name: str, resource_type: str, tags: List[str], tb_client: TinyB):
|
|
2690
2695
|
def get_tags_for_resource(all_tags: dict, resource_id: str, resource_name: str) -> List[str]:
|
|
2691
2696
|
tag_names = []
|
|
2692
2697
|
|
|
@@ -2709,13 +2714,15 @@ async def update_tags(resourceId: str, resourceName: str, resource_type: str, ta
|
|
|
2709
2714
|
tags_to_remove = list(set(current_tags) - set(new_tags))
|
|
2710
2715
|
return tags_to_add, tags_to_remove
|
|
2711
2716
|
|
|
2717
|
+
token_from_main = await get_token_from_main_branch(tb_client)
|
|
2718
|
+
|
|
2712
2719
|
try:
|
|
2713
|
-
all_tags = await tb_client.get_all_tags()
|
|
2720
|
+
all_tags = await tb_client.get_all_tags(token=token_from_main)
|
|
2714
2721
|
except Exception as e:
|
|
2715
2722
|
raise Exception(FeedbackManager.error_getting_tags(error=str(e)))
|
|
2716
2723
|
|
|
2717
2724
|
# Get all tags of that resource
|
|
2718
|
-
current_tags = get_tags_for_resource(all_tags,
|
|
2725
|
+
current_tags = get_tags_for_resource(all_tags, resource_id, resource_name)
|
|
2719
2726
|
|
|
2720
2727
|
# Get the tags to add and remove
|
|
2721
2728
|
tags_to_add, tags_to_remove = compare_tags(current_tags, tags)
|
|
@@ -2727,15 +2734,21 @@ async def update_tags(resourceId: str, resourceName: str, resource_type: str, ta
|
|
|
2727
2734
|
if not tag:
|
|
2728
2735
|
# Create new tag
|
|
2729
2736
|
try:
|
|
2730
|
-
await tb_client.create_tag_with_resource(
|
|
2737
|
+
await tb_client.create_tag_with_resource(
|
|
2738
|
+
name=tag_name,
|
|
2739
|
+
resource_id=resource_id,
|
|
2740
|
+
resource_name=resource_name,
|
|
2741
|
+
resource_type=resource_type,
|
|
2742
|
+
token=token_from_main,
|
|
2743
|
+
)
|
|
2731
2744
|
except Exception as e:
|
|
2732
2745
|
raise Exception(FeedbackManager.error_creating_tag(error=str(e)))
|
|
2733
2746
|
else:
|
|
2734
2747
|
# Update tag with new resource
|
|
2735
2748
|
resources = tag.get("resources", [])
|
|
2736
|
-
resources.append({"id":
|
|
2749
|
+
resources.append({"id": resource_id, "name": resource_name, "type": resource_type})
|
|
2737
2750
|
try:
|
|
2738
|
-
await tb_client.update_tag(tag.get("name", tag_name), resources)
|
|
2751
|
+
await tb_client.update_tag(tag.get("name", tag_name), resources, token=token_from_main)
|
|
2739
2752
|
except Exception as e:
|
|
2740
2753
|
raise Exception(FeedbackManager.error_updating_tag(error=str(e)))
|
|
2741
2754
|
|
|
@@ -2745,9 +2758,9 @@ async def update_tags(resourceId: str, resourceName: str, resource_type: str, ta
|
|
|
2745
2758
|
|
|
2746
2759
|
if tag:
|
|
2747
2760
|
resources = tag.get("resources", [])
|
|
2748
|
-
resources = [resource for resource in resources if resource.get("name") !=
|
|
2761
|
+
resources = [resource for resource in resources if resource.get("name") != resource_name]
|
|
2749
2762
|
try:
|
|
2750
|
-
await tb_client.update_tag(tag.get("name", tag_name), resources)
|
|
2763
|
+
await tb_client.update_tag(tag.get("name", tag_name), resources, token=token_from_main)
|
|
2751
2764
|
except Exception as e:
|
|
2752
2765
|
raise Exception(FeedbackManager.error_updating_tag(error=str(e)))
|
|
2753
2766
|
|
|
@@ -2782,7 +2795,13 @@ async def update_tags_in_resource(rs: Dict[str, Any], resource_type: str, client
|
|
|
2782
2795
|
|
|
2783
2796
|
if resource_id and resource_name:
|
|
2784
2797
|
try:
|
|
2785
|
-
await update_tags(
|
|
2798
|
+
await update_tags(
|
|
2799
|
+
resource_id=resource_id,
|
|
2800
|
+
resource_name=resource_name,
|
|
2801
|
+
resource_type=resource_type,
|
|
2802
|
+
tags=filtering_tags,
|
|
2803
|
+
tb_client=client,
|
|
2804
|
+
)
|
|
2786
2805
|
except Exception as e:
|
|
2787
2806
|
click.echo(FeedbackManager.error_tag_generic(error=str(e)))
|
|
2788
2807
|
|
|
@@ -4803,19 +4822,20 @@ async def format_datasource(
|
|
|
4803
4822
|
if for_deploy_diff:
|
|
4804
4823
|
format_description(file_parts, doc)
|
|
4805
4824
|
format_tokens(file_parts, doc)
|
|
4825
|
+
format_tags(file_parts, doc)
|
|
4806
4826
|
format_schema(file_parts, doc.nodes[0])
|
|
4807
4827
|
format_indices(file_parts, doc.nodes[0])
|
|
4808
4828
|
await format_engine(file_parts, doc.nodes[0], only_ttl=True if not for_deploy_diff else False, client=client)
|
|
4809
4829
|
if for_deploy_diff:
|
|
4810
4830
|
format_import_settings(file_parts, doc.nodes[0])
|
|
4811
4831
|
format_shared_with(file_parts, doc)
|
|
4812
|
-
|
|
4813
4832
|
else:
|
|
4814
4833
|
format_sources(file_parts, doc)
|
|
4815
4834
|
format_maintainer(file_parts, doc)
|
|
4816
4835
|
format_version(file_parts, doc)
|
|
4817
4836
|
format_description(file_parts, doc)
|
|
4818
4837
|
format_tokens(file_parts, doc)
|
|
4838
|
+
format_tags(file_parts, doc)
|
|
4819
4839
|
format_schema(file_parts, doc.nodes[0])
|
|
4820
4840
|
format_indices(file_parts, doc.nodes[0])
|
|
4821
4841
|
await format_engine(file_parts, doc.nodes[0])
|
|
@@ -4891,6 +4911,14 @@ def format_shared_with(file_parts: List[str], doc: Datafile) -> List[str]:
|
|
|
4891
4911
|
return file_parts
|
|
4892
4912
|
|
|
4893
4913
|
|
|
4914
|
+
def format_tags(file_parts: List[str], doc: Datafile) -> List[str]:
|
|
4915
|
+
if doc.filtering_tags:
|
|
4916
|
+
file_parts.append(f'TAGS {", ".join(doc.filtering_tags)}')
|
|
4917
|
+
file_parts.append(DATAFILE_NEW_LINE)
|
|
4918
|
+
file_parts.append(DATAFILE_NEW_LINE)
|
|
4919
|
+
return file_parts
|
|
4920
|
+
|
|
4921
|
+
|
|
4894
4922
|
async def format_engine(
|
|
4895
4923
|
file_parts: List[str], node: Dict[str, Any], only_ttl: bool = False, client: Optional[TinyB] = None
|
|
4896
4924
|
) -> List[str]:
|
|
@@ -5027,6 +5055,7 @@ async def format_pipe(
|
|
|
5027
5055
|
format_version(file_parts, doc)
|
|
5028
5056
|
format_description(file_parts, doc)
|
|
5029
5057
|
format_tokens(file_parts, doc)
|
|
5058
|
+
format_tags(file_parts, doc)
|
|
5030
5059
|
if doc.includes and not unroll_includes:
|
|
5031
5060
|
for k in doc.includes:
|
|
5032
5061
|
# We filter only the include files as we currently have 2 items for each include
|
|
@@ -396,6 +396,7 @@ class FeedbackManager:
|
|
|
396
396
|
error_creating_tag = error_message("Error creating new tag: {error}")
|
|
397
397
|
error_updating_tag = error_message("Error updating tag: {error}")
|
|
398
398
|
error_tag_generic = error_message("There was an issue updating tags. {error}")
|
|
399
|
+
error_tag_not_found = error_message("Tag {tag_name} not found.")
|
|
399
400
|
|
|
400
401
|
info_incl_relative_path = info_message("** Relative path {path} does not exist, skipping.")
|
|
401
402
|
info_ignoring_incl_file = info_message(
|
|
@@ -610,6 +611,12 @@ Ready? """
|
|
|
610
611
|
warning_pipe_restricted_params = warning_message(
|
|
611
612
|
"** The parameter names {words} and '{last_word}' are reserved words. Please, choose another name or the pipe will not work as expected."
|
|
612
613
|
)
|
|
614
|
+
warning_tag_remove_no_resources = prompt_message(
|
|
615
|
+
"Tag {tag_name} is not used by any resource. Do you want to remove it?"
|
|
616
|
+
)
|
|
617
|
+
warning_tag_remove = prompt_message(
|
|
618
|
+
"Tag {tag_name} is used by {resources_len} resources. Do you want to remove it?"
|
|
619
|
+
)
|
|
613
620
|
|
|
614
621
|
info_fixtures_branch = info_message("** Data Fixtures are only pushed to Branches")
|
|
615
622
|
info_materialize_push_datasource_exists = warning_message("** Data Source {name} already exists")
|
|
@@ -801,6 +808,8 @@ Ready? """
|
|
|
801
808
|
info_release_rollback = info_message(
|
|
802
809
|
"** The following resources IDs are present in the {semver} Release and will be restored:"
|
|
803
810
|
)
|
|
811
|
+
info_tag_list = info_message("** Tags:")
|
|
812
|
+
info_tag_resources = info_message("** Resources tagged by {tag_name}:")
|
|
804
813
|
warning_no_release = warning_message(
|
|
805
814
|
"** Warning: Workspace does not have Releases, run `tb init --git` to activate them."
|
|
806
815
|
)
|
|
@@ -985,5 +994,7 @@ Ready? """
|
|
|
985
994
|
success_delete_token = success_message("** Token '{token}' removed successfully")
|
|
986
995
|
success_refresh_token = success_message("** Token '{token}' refreshed successfully")
|
|
987
996
|
success_copy_token = success_message("** Token '{token}' copied to clipboard")
|
|
997
|
+
success_tag_created = success_message("** Tag '{tag_name}' created!")
|
|
998
|
+
success_tag_removed = success_message("** Tag '{tag_name}' removed!")
|
|
988
999
|
|
|
989
1000
|
debug_running_file = print_message("** Running {file}", bcolors.CGREY)
|
|
@@ -10,8 +10,10 @@ import tinybird.tb_cli_modules.cli
|
|
|
10
10
|
import tinybird.tb_cli_modules.common
|
|
11
11
|
import tinybird.tb_cli_modules.connection
|
|
12
12
|
import tinybird.tb_cli_modules.datasource
|
|
13
|
+
import tinybird.tb_cli_modules.fmt
|
|
13
14
|
import tinybird.tb_cli_modules.job
|
|
14
15
|
import tinybird.tb_cli_modules.pipe
|
|
16
|
+
import tinybird.tb_cli_modules.tag
|
|
15
17
|
import tinybird.tb_cli_modules.test
|
|
16
18
|
import tinybird.tb_cli_modules.token
|
|
17
19
|
import tinybird.tb_cli_modules.workspace
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
# - If it makes sense and only when strictly necessary, you can create utility functions in this file.
|
|
4
4
|
# - But please, **do not** interleave utility functions and command definitions.
|
|
5
5
|
|
|
6
|
-
import difflib
|
|
7
6
|
import json
|
|
8
7
|
import logging
|
|
9
8
|
import os
|
|
@@ -36,21 +35,16 @@ from tinybird.datafile import (
|
|
|
36
35
|
Datafile,
|
|
37
36
|
ParseException,
|
|
38
37
|
build_graph,
|
|
39
|
-
color_diff,
|
|
40
38
|
create_release,
|
|
41
39
|
diff_command,
|
|
42
40
|
folder_pull,
|
|
43
41
|
folder_push,
|
|
44
|
-
format_datasource,
|
|
45
|
-
format_pipe,
|
|
46
42
|
get_project_filenames,
|
|
47
43
|
get_resource_versions,
|
|
48
44
|
has_internal_datafiles,
|
|
49
|
-
is_file_a_datasource,
|
|
50
45
|
parse_datasource,
|
|
51
46
|
parse_pipe,
|
|
52
47
|
parse_token,
|
|
53
|
-
peek,
|
|
54
48
|
wait_job,
|
|
55
49
|
)
|
|
56
50
|
from tinybird.feedback_manager import FeedbackManager
|
|
@@ -753,84 +747,6 @@ async def dependencies(
|
|
|
753
747
|
raise CLIException(FeedbackManager.error_partial_replace_cant_be_executed(datasource=datasource))
|
|
754
748
|
|
|
755
749
|
|
|
756
|
-
@cli.command()
|
|
757
|
-
@click.argument("filenames", type=click.Path(exists=True), nargs=-1, required=True)
|
|
758
|
-
@click.option(
|
|
759
|
-
"--line-length",
|
|
760
|
-
is_flag=False,
|
|
761
|
-
default=100,
|
|
762
|
-
help="A number indicating the maximum characters per line in the node SQL, lines will be splitted based on the SQL syntax and the number of characters passed as a parameter",
|
|
763
|
-
)
|
|
764
|
-
@click.option("--dry-run", is_flag=True, default=False, help="Don't ask to override the local file")
|
|
765
|
-
@click.option("--yes", is_flag=True, default=False, help="Do not ask for confirmation to overwrite the local file")
|
|
766
|
-
@click.option(
|
|
767
|
-
"--diff",
|
|
768
|
-
is_flag=True,
|
|
769
|
-
default=False,
|
|
770
|
-
help="Formats local file, prints the diff and exits 1 if different, 0 if equal",
|
|
771
|
-
)
|
|
772
|
-
@click.pass_context
|
|
773
|
-
@coro
|
|
774
|
-
async def fmt(
|
|
775
|
-
ctx: Context, filenames: List[str], line_length: int, dry_run: bool, yes: bool, diff: bool
|
|
776
|
-
) -> Optional[str]:
|
|
777
|
-
"""
|
|
778
|
-
Formats a .datasource, .pipe or .incl file
|
|
779
|
-
|
|
780
|
-
This command removes comments starting with # from the file, use DESCRIPTION instead.
|
|
781
|
-
|
|
782
|
-
The format command tries to parse the datafile so syntax errors might rise.
|
|
783
|
-
|
|
784
|
-
.incl files must contain a NODE definition
|
|
785
|
-
"""
|
|
786
|
-
|
|
787
|
-
result = ""
|
|
788
|
-
failed = []
|
|
789
|
-
for filename in filenames:
|
|
790
|
-
if not diff:
|
|
791
|
-
click.echo(filename)
|
|
792
|
-
extensions = Path(filename).suffixes
|
|
793
|
-
if is_file_a_datasource(filename):
|
|
794
|
-
result = await format_datasource(filename, skip_eval=True)
|
|
795
|
-
elif (".pipe" in extensions) or (".incl" in extensions):
|
|
796
|
-
result = await format_pipe(filename, line_length, skip_eval=True)
|
|
797
|
-
else:
|
|
798
|
-
click.echo("Unsupported file type. Supported files types are: .pipe, .incl and .datasource")
|
|
799
|
-
return None
|
|
800
|
-
|
|
801
|
-
if diff:
|
|
802
|
-
result = result.rstrip("\n")
|
|
803
|
-
lines_fmt = [f"{line}\n" for line in result.split("\n")]
|
|
804
|
-
with open(filename, "r") as file:
|
|
805
|
-
lines_file = file.readlines()
|
|
806
|
-
diff_result = difflib.unified_diff(
|
|
807
|
-
lines_file, lines_fmt, fromfile=f"{Path(filename).name} local", tofile="fmt datafile"
|
|
808
|
-
)
|
|
809
|
-
diff_result = color_diff(diff_result)
|
|
810
|
-
not_empty, diff_lines = peek(diff_result)
|
|
811
|
-
if not_empty:
|
|
812
|
-
sys.stdout.writelines(diff_lines)
|
|
813
|
-
failed.append(filename)
|
|
814
|
-
click.echo("")
|
|
815
|
-
else:
|
|
816
|
-
click.echo(result)
|
|
817
|
-
if dry_run:
|
|
818
|
-
return None
|
|
819
|
-
|
|
820
|
-
if yes or click.confirm(FeedbackManager.prompt_override_local_file(name=filename)):
|
|
821
|
-
with open(f"{filename}", "w") as file:
|
|
822
|
-
file.write(result)
|
|
823
|
-
|
|
824
|
-
click.echo(FeedbackManager.success_generated_local_file(file=filename))
|
|
825
|
-
|
|
826
|
-
if len(failed):
|
|
827
|
-
click.echo(FeedbackManager.error_failed_to_format_files(number=len(failed)))
|
|
828
|
-
for f in failed:
|
|
829
|
-
click.echo(f"tb fmt {f} --yes")
|
|
830
|
-
sys.exit(1)
|
|
831
|
-
return result
|
|
832
|
-
|
|
833
|
-
|
|
834
750
|
@cli.command(
|
|
835
751
|
name="diff",
|
|
836
752
|
short_help="Diffs local datafiles to the corresponding remote files in the workspace. For the case of .datasource files it just diffs VERSION and SCHEMA, since ENGINE, KAFKA or other metadata is considered immutable.",
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import difflib
|
|
2
|
+
import sys
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import List, Optional
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
from click import Context
|
|
8
|
+
|
|
9
|
+
from tinybird.datafile import color_diff, format_datasource, format_pipe, is_file_a_datasource, peek
|
|
10
|
+
from tinybird.feedback_manager import FeedbackManager
|
|
11
|
+
from tinybird.tb_cli_modules.cli import cli
|
|
12
|
+
from tinybird.tb_cli_modules.common import coro
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@cli.command()
|
|
16
|
+
@click.argument("filenames", type=click.Path(exists=True), nargs=-1, required=True)
|
|
17
|
+
@click.option(
|
|
18
|
+
"--line-length",
|
|
19
|
+
is_flag=False,
|
|
20
|
+
default=100,
|
|
21
|
+
help="A number indicating the maximum characters per line in the node SQL, lines will be splitted based on the SQL syntax and the number of characters passed as a parameter",
|
|
22
|
+
)
|
|
23
|
+
@click.option("--dry-run", is_flag=True, default=False, help="Don't ask to override the local file")
|
|
24
|
+
@click.option("--yes", is_flag=True, default=False, help="Do not ask for confirmation to overwrite the local file")
|
|
25
|
+
@click.option(
|
|
26
|
+
"--diff",
|
|
27
|
+
is_flag=True,
|
|
28
|
+
default=False,
|
|
29
|
+
help="Formats local file, prints the diff and exits 1 if different, 0 if equal",
|
|
30
|
+
)
|
|
31
|
+
@click.pass_context
|
|
32
|
+
@coro
|
|
33
|
+
async def fmt(
|
|
34
|
+
ctx: Context, filenames: List[str], line_length: int, dry_run: bool, yes: bool, diff: bool
|
|
35
|
+
) -> Optional[str]:
|
|
36
|
+
"""
|
|
37
|
+
Formats a .datasource, .pipe or .incl file
|
|
38
|
+
|
|
39
|
+
This command removes comments starting with # from the file, use DESCRIPTION instead.
|
|
40
|
+
|
|
41
|
+
The format command tries to parse the datafile so syntax errors might rise.
|
|
42
|
+
|
|
43
|
+
.incl files must contain a NODE definition
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
result = ""
|
|
47
|
+
failed = []
|
|
48
|
+
for filename in filenames:
|
|
49
|
+
if not diff:
|
|
50
|
+
click.echo(filename)
|
|
51
|
+
extensions = Path(filename).suffixes
|
|
52
|
+
if is_file_a_datasource(filename):
|
|
53
|
+
result = await format_datasource(filename, skip_eval=True)
|
|
54
|
+
elif (".pipe" in extensions) or (".incl" in extensions):
|
|
55
|
+
result = await format_pipe(filename, line_length, skip_eval=True)
|
|
56
|
+
else:
|
|
57
|
+
click.echo("Unsupported file type. Supported files types are: .pipe, .incl and .datasource")
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
if diff:
|
|
61
|
+
result = result.rstrip("\n")
|
|
62
|
+
lines_fmt = [f"{line}\n" for line in result.split("\n")]
|
|
63
|
+
with open(filename, "r") as file:
|
|
64
|
+
lines_file = file.readlines()
|
|
65
|
+
diff_result = difflib.unified_diff(
|
|
66
|
+
lines_file, lines_fmt, fromfile=f"{Path(filename).name} local", tofile="fmt datafile"
|
|
67
|
+
)
|
|
68
|
+
diff_result = color_diff(diff_result)
|
|
69
|
+
not_empty, diff_lines = peek(diff_result)
|
|
70
|
+
if not_empty:
|
|
71
|
+
sys.stdout.writelines(diff_lines)
|
|
72
|
+
failed.append(filename)
|
|
73
|
+
click.echo("")
|
|
74
|
+
else:
|
|
75
|
+
click.echo(result)
|
|
76
|
+
if dry_run:
|
|
77
|
+
return None
|
|
78
|
+
|
|
79
|
+
if yes or click.confirm(FeedbackManager.prompt_override_local_file(name=filename)):
|
|
80
|
+
with open(f"{filename}", "w") as file:
|
|
81
|
+
file.write(result)
|
|
82
|
+
|
|
83
|
+
click.echo(FeedbackManager.success_generated_local_file(file=filename))
|
|
84
|
+
|
|
85
|
+
if len(failed):
|
|
86
|
+
click.echo(FeedbackManager.error_failed_to_format_files(number=len(failed)))
|
|
87
|
+
for f in failed:
|
|
88
|
+
click.echo(f"tb fmt {f} --yes")
|
|
89
|
+
sys.exit(1)
|
|
90
|
+
return result
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
from click import Context
|
|
5
|
+
|
|
6
|
+
from tinybird.feedback_manager import FeedbackManager
|
|
7
|
+
from tinybird.tb_cli_modules.cli import cli
|
|
8
|
+
from tinybird.tb_cli_modules.common import (
|
|
9
|
+
coro,
|
|
10
|
+
echo_safe_humanfriendly_tables_format_smart_table,
|
|
11
|
+
get_current_main_workspace,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@cli.group()
|
|
16
|
+
@click.pass_context
|
|
17
|
+
def tag(ctx: Context) -> None:
|
|
18
|
+
"""Tag commands"""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@tag.command(name="ls")
|
|
22
|
+
@click.argument("tag_name", required=False)
|
|
23
|
+
@click.pass_context
|
|
24
|
+
@coro
|
|
25
|
+
async def tag_ls(ctx: Context, tag_name: Optional[str]) -> None:
|
|
26
|
+
"""List all the tags of the current Workspace or the resources associated to a specific tag."""
|
|
27
|
+
|
|
28
|
+
client = ctx.ensure_object(dict)["client"]
|
|
29
|
+
config = ctx.ensure_object(dict)["config"]
|
|
30
|
+
main_workspace = await get_current_main_workspace(client, config)
|
|
31
|
+
token = main_workspace.get("token") if main_workspace else None
|
|
32
|
+
|
|
33
|
+
response = await client.get_all_tags(token=token)
|
|
34
|
+
|
|
35
|
+
if tag_name:
|
|
36
|
+
the_tag = [tag for tag in response["tags"] if tag["name"] == tag_name]
|
|
37
|
+
|
|
38
|
+
columns = ["name", "id", "type"]
|
|
39
|
+
table = []
|
|
40
|
+
|
|
41
|
+
if len(the_tag) > 0:
|
|
42
|
+
for resource in the_tag[0]["resources"]:
|
|
43
|
+
table.append([resource["name"], resource["id"], resource["type"]])
|
|
44
|
+
|
|
45
|
+
click.echo(FeedbackManager.info_tag_resources(tag_name=tag_name))
|
|
46
|
+
echo_safe_humanfriendly_tables_format_smart_table(table, column_names=columns)
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
columns = ["tag", "resources"]
|
|
50
|
+
table = []
|
|
51
|
+
|
|
52
|
+
for tag in response["tags"]:
|
|
53
|
+
unique_resources = []
|
|
54
|
+
for resource in tag["resources"]:
|
|
55
|
+
if resource.get("name", "") not in unique_resources:
|
|
56
|
+
unique_resources.append(resource) # Reducing by name in case there are duplicates.
|
|
57
|
+
table.append([tag["name"], len(unique_resources)])
|
|
58
|
+
|
|
59
|
+
click.echo(FeedbackManager.info_tag_list())
|
|
60
|
+
echo_safe_humanfriendly_tables_format_smart_table(table, column_names=columns)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@tag.command(name="create")
|
|
64
|
+
@click.argument("tag_name")
|
|
65
|
+
@click.pass_context
|
|
66
|
+
@coro
|
|
67
|
+
async def tag_create(ctx: Context, tag_name: str) -> None:
|
|
68
|
+
"""Create a tag in the current Workspace."""
|
|
69
|
+
|
|
70
|
+
client = ctx.ensure_object(dict)["client"]
|
|
71
|
+
config = ctx.ensure_object(dict)["config"]
|
|
72
|
+
main_workspace = await get_current_main_workspace(client, config)
|
|
73
|
+
token = main_workspace.get("token") if main_workspace else None
|
|
74
|
+
|
|
75
|
+
await client.create_tag(name=tag_name, token=token)
|
|
76
|
+
|
|
77
|
+
click.echo(FeedbackManager.success_tag_created(tag_name=tag_name))
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@tag.command(name="rm")
|
|
81
|
+
@click.argument("tag_name")
|
|
82
|
+
@click.option("--yes", is_flag=True, default=False, help="Do not ask for confirmation to delete the tag.")
|
|
83
|
+
@click.pass_context
|
|
84
|
+
@coro
|
|
85
|
+
async def tag_rm(ctx: Context, tag_name: str, yes: bool) -> None:
|
|
86
|
+
"""Remove a tag from the current Workspace."""
|
|
87
|
+
|
|
88
|
+
client = ctx.ensure_object(dict)["client"]
|
|
89
|
+
config = ctx.ensure_object(dict)["config"]
|
|
90
|
+
main_workspace = await get_current_main_workspace(client, config)
|
|
91
|
+
token = main_workspace.get("token") if main_workspace else None
|
|
92
|
+
|
|
93
|
+
remove_tag = True
|
|
94
|
+
|
|
95
|
+
if not yes:
|
|
96
|
+
all_tags = await client.get_all_tags(token=token)
|
|
97
|
+
the_tag = [tag for tag in all_tags["tags"] if tag["name"] == tag_name]
|
|
98
|
+
if len(the_tag) > 0:
|
|
99
|
+
unique_resources = []
|
|
100
|
+
for resource in the_tag[0]["resources"]:
|
|
101
|
+
if resource.get("name", "") not in unique_resources:
|
|
102
|
+
unique_resources.append(resource) # Reducing by name in case there are duplicates.
|
|
103
|
+
|
|
104
|
+
if len(unique_resources) > 0:
|
|
105
|
+
remove_tag = click.confirm(
|
|
106
|
+
FeedbackManager.warning_tag_remove(tag_name=tag_name, resources_len=len(unique_resources))
|
|
107
|
+
)
|
|
108
|
+
else:
|
|
109
|
+
remove_tag = click.confirm(FeedbackManager.warning_tag_remove_no_resources(tag_name=tag_name))
|
|
110
|
+
else:
|
|
111
|
+
remove_tag = False
|
|
112
|
+
click.echo(FeedbackManager.error_tag_not_found(tag_name=tag_name))
|
|
113
|
+
|
|
114
|
+
if remove_tag:
|
|
115
|
+
await client.delete_tag(tag_name, token=token)
|
|
116
|
+
click.echo(FeedbackManager.success_tag_removed(tag_name=tag_name))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: tinybird-cli
|
|
3
|
-
Version: 5.8.0.
|
|
3
|
+
Version: 5.8.0.dev2
|
|
4
4
|
Summary: Tinybird Command Line Tool
|
|
5
5
|
Home-page: https://www.tinybird.co/docs/cli/introduction.html
|
|
6
6
|
Author: Tinybird
|
|
@@ -18,6 +18,16 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
|
|
|
18
18
|
Changelog
|
|
19
19
|
----------
|
|
20
20
|
|
|
21
|
+
5.8.0.dev2
|
|
22
|
+
***********
|
|
23
|
+
|
|
24
|
+
- `Added` support to `TAGS` in `tb fmt`.
|
|
25
|
+
|
|
26
|
+
5.8.0.dev1
|
|
27
|
+
***********
|
|
28
|
+
|
|
29
|
+
- `Added` new `tb tag` command.
|
|
30
|
+
|
|
21
31
|
5.8.0.dev0
|
|
22
32
|
***********
|
|
23
33
|
|
|
@@ -26,9 +26,11 @@ tinybird/tb_cli_modules/config.py
|
|
|
26
26
|
tinybird/tb_cli_modules/connection.py
|
|
27
27
|
tinybird/tb_cli_modules/datasource.py
|
|
28
28
|
tinybird/tb_cli_modules/exceptions.py
|
|
29
|
+
tinybird/tb_cli_modules/fmt.py
|
|
29
30
|
tinybird/tb_cli_modules/job.py
|
|
30
31
|
tinybird/tb_cli_modules/pipe.py
|
|
31
32
|
tinybird/tb_cli_modules/regions.py
|
|
33
|
+
tinybird/tb_cli_modules/tag.py
|
|
32
34
|
tinybird/tb_cli_modules/telemetry.py
|
|
33
35
|
tinybird/tb_cli_modules/test.py
|
|
34
36
|
tinybird/tb_cli_modules/token.py
|
|
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-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/tb_cli_modules/tinyunit/tinyunit.py
RENAMED
|
File without changes
|
{tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird/tb_cli_modules/workspace_members.py
RENAMED
|
File without changes
|
|
File without changes
|
{tinybird-cli-5.8.0.dev0 → tinybird-cli-5.8.0.dev2}/tinybird_cli.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|