tinybird 0.0.1.dev0__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.

Potentially problematic release.


This version of tinybird might be problematic. Click here for more details.

Files changed (45) hide show
  1. tinybird/__cli__.py +8 -0
  2. tinybird/ch_utils/constants.py +244 -0
  3. tinybird/ch_utils/engine.py +855 -0
  4. tinybird/check_pypi.py +25 -0
  5. tinybird/client.py +1281 -0
  6. tinybird/config.py +117 -0
  7. tinybird/connectors.py +428 -0
  8. tinybird/context.py +23 -0
  9. tinybird/datafile.py +5589 -0
  10. tinybird/datatypes.py +434 -0
  11. tinybird/feedback_manager.py +1022 -0
  12. tinybird/git_settings.py +145 -0
  13. tinybird/sql.py +865 -0
  14. tinybird/sql_template.py +2343 -0
  15. tinybird/sql_template_fmt.py +281 -0
  16. tinybird/sql_toolset.py +350 -0
  17. tinybird/syncasync.py +682 -0
  18. tinybird/tb_cli.py +25 -0
  19. tinybird/tb_cli_modules/auth.py +252 -0
  20. tinybird/tb_cli_modules/branch.py +1043 -0
  21. tinybird/tb_cli_modules/cicd.py +434 -0
  22. tinybird/tb_cli_modules/cli.py +1571 -0
  23. tinybird/tb_cli_modules/common.py +2082 -0
  24. tinybird/tb_cli_modules/config.py +344 -0
  25. tinybird/tb_cli_modules/connection.py +803 -0
  26. tinybird/tb_cli_modules/datasource.py +900 -0
  27. tinybird/tb_cli_modules/exceptions.py +91 -0
  28. tinybird/tb_cli_modules/fmt.py +91 -0
  29. tinybird/tb_cli_modules/job.py +85 -0
  30. tinybird/tb_cli_modules/pipe.py +858 -0
  31. tinybird/tb_cli_modules/regions.py +9 -0
  32. tinybird/tb_cli_modules/tag.py +100 -0
  33. tinybird/tb_cli_modules/telemetry.py +310 -0
  34. tinybird/tb_cli_modules/test.py +107 -0
  35. tinybird/tb_cli_modules/tinyunit/tinyunit.py +340 -0
  36. tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +71 -0
  37. tinybird/tb_cli_modules/token.py +349 -0
  38. tinybird/tb_cli_modules/workspace.py +269 -0
  39. tinybird/tb_cli_modules/workspace_members.py +212 -0
  40. tinybird/tornado_template.py +1194 -0
  41. tinybird-0.0.1.dev0.dist-info/METADATA +2815 -0
  42. tinybird-0.0.1.dev0.dist-info/RECORD +45 -0
  43. tinybird-0.0.1.dev0.dist-info/WHEEL +5 -0
  44. tinybird-0.0.1.dev0.dist-info/entry_points.txt +2 -0
  45. tinybird-0.0.1.dev0.dist-info/top_level.txt +4 -0
@@ -0,0 +1,434 @@
1
+ from enum import Enum
2
+ from os import getcwd
3
+ from pathlib import Path
4
+ from typing import Any, Dict, List, Optional, Type, Union
5
+
6
+ import click
7
+ from tornado.template import Template
8
+
9
+ from tinybird.client import TinyB
10
+ from tinybird.feedback_manager import FeedbackManager
11
+
12
+
13
+ class Provider(Enum):
14
+ GitHub = 0
15
+ GitLab = 1
16
+
17
+
18
+ WORKFLOW_VERSION = "v3.1.0"
19
+
20
+ DEFAULT_REQUIREMENTS_FILE = "tinybird-cli>=5,<6"
21
+
22
+ GITHUB_CI_YML = """
23
+ ##################################################
24
+ ### Visit https://github.com/tinybirdco/ci ###
25
+ ### for more details or custom CI/CD ###
26
+ ##################################################
27
+
28
+ name: Tinybird {{ workspace_name }} - CI Workflow
29
+
30
+ on:
31
+ workflow_dispatch:
32
+ pull_request:
33
+ branches:
34
+ - main
35
+ - master
36
+ types: [opened, reopened, labeled, unlabeled, synchronize, closed]{% if data_project_dir != '.' %}
37
+ paths:
38
+ - '{{ data_project_dir }}/**'{% end %}
39
+
40
+ concurrency: ${{! github.workflow }}-${{! github.event.pull_request.number }}
41
+
42
+ jobs:
43
+ ci: # ci using branches from workspace '{{ workspace_name }}'
44
+ uses: tinybirdco/ci/.github/workflows/ci.yml@{{ workflow_version }}
45
+ with:
46
+ data_project_dir: {{ data_project_dir }}
47
+ tb_format: false
48
+ secrets:
49
+ tb_admin_token: ${{! secrets.{{ tb_admin_token_name }} }} # set the Workspace admin token in GitHub secrets
50
+ tb_host: {{ tb_host }}
51
+ """
52
+
53
+ GITHUB_CD_YML = """
54
+ ##################################################
55
+ ### Visit https://github.com/tinybirdco/ci ###
56
+ ### for more details or custom CI/CD ###
57
+ ##################################################
58
+
59
+ name: Tinybird {{ workspace_name }} - CD Workflow
60
+
61
+ on:
62
+ workflow_dispatch:
63
+ push:
64
+ branches:
65
+ - main
66
+ - master{% if data_project_dir != '.' %}
67
+ paths:
68
+ - '{{ data_project_dir }}/**'{% end %}
69
+ jobs:
70
+ cd: # deploy changes to workspace '{{ workspace_name }}'
71
+ uses: tinybirdco/ci/.github/workflows/cd.yml@{{ workflow_version }}
72
+ with:
73
+ data_project_dir: {{ data_project_dir }}
74
+ secrets:
75
+ tb_admin_token: ${{! secrets.{{ tb_admin_token_name }} }} # set the Workspace admin token in GitHub secrets
76
+ tb_host: {{ tb_host }}
77
+ """
78
+
79
+ GITHUB_RELEASES_YML = """
80
+ ##################################################
81
+ ### Visit https://github.com/tinybirdco/ci ###
82
+ ### for more details or custom CI/CD ###
83
+ ##################################################
84
+
85
+ name: Tinybird {{ workspace_name }} - Releases Workflow
86
+
87
+ on:
88
+ workflow_dispatch:
89
+ inputs:
90
+ job_to_run:
91
+ description: 'Select the job to run manually'
92
+ required: true
93
+ default: 'promote'
94
+
95
+ jobs:
96
+ cd: # manage releases for workspace '{{ workspace_name }}'
97
+ uses: tinybirdco/ci/.github/workflows/release.yml@{{ workflow_version }}
98
+ with:
99
+ job_to_run: ${{! inputs.job_to_run }}
100
+ data_project_dir: {{ data_project_dir }}
101
+ secrets:
102
+ tb_admin_token: ${{! secrets.{{ tb_admin_token_name }} }} # set the Workspace admin token in GitHub secrets
103
+ tb_host: {{ tb_host }}
104
+ """
105
+
106
+
107
+ GITLAB_YML = """
108
+ ##################################################
109
+ ### Visit https://github.com/tinybirdco/ci ###
110
+ ### for more details or custom CI/CD ###
111
+ ##################################################
112
+
113
+ include: "https://raw.githubusercontent.com/tinybirdco/ci/{{ workflow_version }}/.gitlab/ci_cd.yaml"
114
+
115
+ .ci_config_rules:
116
+ - &ci_config_rule_deploy
117
+ if: $CI_PIPELINE_SOURCE == "merge_request_event"{% if data_project_dir != '.' %}
118
+ changes:
119
+ - {{ data_project_dir }}/*
120
+ - {{ data_project_dir }}/**/*{% end %}
121
+ - &ci_config_rule_test
122
+ if: $CI_PIPELINE_SOURCE == "merge_request_event"{% if data_project_dir != '.' %}
123
+ changes:
124
+ - {{ data_project_dir }}/*
125
+ - {{ data_project_dir }}/**/*{% end %}
126
+
127
+ - &ci_cleanup_rule
128
+ if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH{% if data_project_dir != '.' %}
129
+ changes:
130
+ - {{ data_project_dir }}/*
131
+ - {{ data_project_dir }}/**/*{% end %}
132
+
133
+ .cd_config_rules:
134
+ - &cd_config_rule
135
+ if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH{% if data_project_dir != '.' %}
136
+ changes:
137
+ - {{ data_project_dir }}/*
138
+ - {{ data_project_dir }}/**/*{% end %}
139
+
140
+ .cicd_variables:
141
+ variables: &cicd_variables
142
+ TB_HOST: "{{ tb_host }}"
143
+ TB_ADMIN_TOKEN: ${{ tb_admin_token_name }} # set the Workspace admin token in GitLab CI/CD Variables
144
+ DATA_PROJECT_DIR: "{{ data_project_dir }}"
145
+
146
+ deploy_ci: # ci using branches from workspace '{{ workspace_name }}'
147
+ extends: .tb_deploy_ci
148
+ rules:
149
+ - *ci_config_rule_deploy
150
+ variables:
151
+ <<: *cicd_variables
152
+
153
+ test_ci: # ci using branches from workspace '{{ workspace_name }}'
154
+ extends: .tb_test
155
+ needs: ["deploy_ci"]
156
+ rules:
157
+ - *ci_config_rule_test
158
+ variables:
159
+ <<: *cicd_variables
160
+
161
+ cleanup_ci_branch:
162
+ extends: .tb_cleanup_ci_branch
163
+ when: always
164
+ rules:
165
+ - *ci_cleanup_rule
166
+ variables:
167
+ <<: *cicd_variables
168
+
169
+ deploy_main: # deploy changes to workspace '{{ workspace_name }}'
170
+ extends: .tb_deploy_main
171
+ rules:
172
+ - *cd_config_rule
173
+ variables:
174
+ <<: *cicd_variables
175
+
176
+ run_promote:
177
+ extends: .release_promote
178
+ dependencies: []
179
+ when: manual
180
+ rules:
181
+ - *cd_config_rule
182
+ variables:
183
+ <<: *cicd_variables
184
+
185
+ dry_run_rm_oldest_rollback:
186
+ extends: .dry_run_release_rm_oldest_rollback
187
+ dependencies: []
188
+ when: manual
189
+ rules:
190
+ - *cd_config_rule
191
+ variables:
192
+ <<: *cicd_variables
193
+
194
+ run_rm_oldest_rollback:
195
+ extends: .release_rm_oldest_rollback
196
+ dependencies: []
197
+ when: manual
198
+ rules:
199
+ - *cd_config_rule
200
+ variables:
201
+ <<: *cicd_variables
202
+
203
+
204
+ """
205
+
206
+
207
+ EXEC_TEST_SH = """
208
+ #!/usr/bin/env bash
209
+ set -euxo pipefail
210
+
211
+ export TB_VERSION_WARNING=0
212
+
213
+ run_test() {
214
+ t=$1
215
+ echo "** Running $t **"
216
+ echo "** $(cat $t)"
217
+ tmpfile=$(mktemp)
218
+ retries=0
219
+ TOTAL_RETRIES=3
220
+
221
+ # When appending fixtures, we need to retry in case of the data is not replicated in time
222
+ while [ $retries -lt $TOTAL_RETRIES ]; do
223
+ # Run the test and store the output in a temporary file
224
+ bash $t $2 >$tmpfile
225
+ exit_code=$?
226
+ if [ "$exit_code" -eq 0 ]; then
227
+ # If the test passed, break the loop
228
+ if diff -B ${t}.result $tmpfile >/dev/null 2>&1; then
229
+ break
230
+ # If the test failed, increment the retries counter and try again
231
+ else
232
+ retries=$((retries+1))
233
+ fi
234
+ # If the bash command failed, print an error message and break the loop
235
+ else
236
+ break
237
+ fi
238
+ done
239
+
240
+ if diff -B ${t}.result $tmpfile >/dev/null 2>&1; then
241
+ echo "✅ Test $t passed"
242
+ rm $tmpfile
243
+ return 0
244
+ elif [ $retries -eq $TOTAL_RETRIES ]; then
245
+ echo "🚨 ERROR: Test $t failed, diff:";
246
+ diff -B ${t}.result $tmpfile
247
+ rm $tmpfile
248
+ return 1
249
+ else
250
+ echo "🚨 ERROR: Test $t failed with bash command exit code $?"
251
+ cat $tmpfile
252
+ rm $tmpfile
253
+ return 1
254
+ fi
255
+ echo ""
256
+ }
257
+ export -f run_test
258
+
259
+ fail=0
260
+ find ./tests -name "*.test" -print0 | xargs -0 -I {} -P 4 bash -c 'run_test "$@"' _ {} || fail=1
261
+
262
+ if [ $fail == 1 ]; then
263
+ exit -1;
264
+ fi
265
+ """
266
+
267
+ APPEND_FIXTURES_SH = """
268
+ #!/usr/bin/env bash
269
+ set -euxo pipefail
270
+
271
+ directory="datasources/fixtures"
272
+ extensions=("csv" "ndjson")
273
+
274
+ absolute_directory=$(realpath "$directory")
275
+
276
+ for extension in "${extensions[@]}"; do
277
+ file_list=$(find "$absolute_directory" -type f -name "*.$extension")
278
+
279
+ for file_path in $file_list; do
280
+ file_name=$(basename "$file_path")
281
+ file_name_without_extension="${file_name%.*}"
282
+
283
+ command="tb datasource append $file_name_without_extension datasources/fixtures/$file_name"
284
+ echo $command
285
+ $command
286
+ done
287
+ done
288
+ """
289
+
290
+
291
+ class CICDFile:
292
+ def __init__(
293
+ self,
294
+ template: str,
295
+ file_name_template: str,
296
+ dir_path: Optional[str] = None,
297
+ warning_message: Optional[str] = None,
298
+ ):
299
+ self.template = template
300
+ self.file_name_template = file_name_template
301
+ self.dir_path = dir_path
302
+ self.warning_message = warning_message
303
+ self.file_name = ""
304
+
305
+ def generate_file_name(self, params: Dict[str, Any]):
306
+ file_name_template = Template(self.file_name_template)
307
+ self.file_name = str(file_name_template.generate(**params).decode()).lower()
308
+
309
+ @property
310
+ def full_path(self) -> str:
311
+ return f"{self.dir_path}/{self.file_name}" if self.dir_path else self.file_name
312
+
313
+
314
+ class CICDGeneratorBase:
315
+ cicd_files: List[CICDFile] = []
316
+
317
+ def __call__(self, path: str, params: Dict[str, Any]):
318
+ for cicd_file in self.cicd_files:
319
+ cicd_file.generate_file_name(params)
320
+ if cicd_file.dir_path:
321
+ Path(f"{path}/{cicd_file.dir_path}").mkdir(parents=True, exist_ok=True)
322
+ content = Template(cicd_file.template).generate(**params)
323
+ with open(f"{path}/{cicd_file.full_path}", "wb") as f:
324
+ f.write(content)
325
+ click.echo(FeedbackManager.info_cicd_file_generated(file_path=cicd_file.full_path))
326
+ if cicd_file.warning_message is not None:
327
+ return FeedbackManager.warning_for_cicd_file(
328
+ file_name=cicd_file.file_name, warning_message=cicd_file.warning_message.format(**params)
329
+ )
330
+
331
+ def is_already_generated(self, path: str) -> bool:
332
+ for cicd_file in self.cicd_files:
333
+ if cicd_file.file_name and Path(f"{path}/{cicd_file.full_path}").exists():
334
+ return True
335
+ return False
336
+
337
+ @classmethod
338
+ def build_generator(cls, provider: str) -> Union["GitHubCICDGenerator", "GitLabCICDGenerator"]:
339
+ builder: Dict[str, Union[Type[GitHubCICDGenerator], Type[GitLabCICDGenerator]]] = {
340
+ Provider.GitHub.name: GitHubCICDGenerator,
341
+ Provider.GitLab.name: GitLabCICDGenerator,
342
+ }
343
+ return builder[provider]()
344
+
345
+
346
+ class GitHubCICDGenerator(CICDGeneratorBase):
347
+ cicd_files = [
348
+ CICDFile(
349
+ template=GITHUB_CI_YML,
350
+ file_name_template="tinybird_{{workspace_name}}_ci.yml",
351
+ dir_path=".github/workflows",
352
+ ),
353
+ CICDFile(
354
+ template=GITHUB_CD_YML,
355
+ file_name_template="tinybird_{{workspace_name}}_cd.yml",
356
+ dir_path=".github/workflows",
357
+ ),
358
+ CICDFile(
359
+ template=GITHUB_RELEASES_YML,
360
+ file_name_template="tinybird_{{workspace_name}}_release.yml",
361
+ dir_path=".github/workflows",
362
+ warning_message="Set {tb_admin_token_name} in GitHub secrets. Use the Workspace admin token. Hint: use `tb token copy {token_id}` to copy clipboard",
363
+ ),
364
+ ]
365
+
366
+
367
+ class GitLabCICDGenerator(CICDGeneratorBase):
368
+ cicd_files = [
369
+ CICDFile(
370
+ template=GITLAB_YML,
371
+ file_name_template=".gitlab-ci.yml",
372
+ warning_message="Set {tb_admin_token_name} in GitLab CI/CD Variables. Use the Workspace admin token. Hint: use `tb token copy {token_id}` to copy clipboard",
373
+ )
374
+ ]
375
+
376
+
377
+ def ask_provider_interactively():
378
+ provider_index = -1
379
+ while provider_index == -1:
380
+ click.echo(FeedbackManager.info_available_git_providers())
381
+ for index, provider in enumerate(Provider):
382
+ click.echo(f" [{index + 1}] {provider.name}")
383
+ click.echo(" [0] Cancel")
384
+
385
+ provider_index = click.prompt("\nUse provider", default=1)
386
+
387
+ if provider_index == 0:
388
+ click.echo(FeedbackManager.info_cicd_generation_cancelled_by_user())
389
+ return None
390
+
391
+ try:
392
+ return Provider(provider_index - 1).name
393
+ except Exception:
394
+ available_options = ", ".join(map(str, range(1, len(Provider) + 1)))
395
+ click.echo(
396
+ FeedbackManager.error_git_provider_index(host_index=provider_index, available_options=available_options)
397
+ )
398
+ provider_index = -1
399
+
400
+
401
+ async def init_cicd(
402
+ client: TinyB,
403
+ path: Optional[str] = None,
404
+ data_project_dir: Optional[str] = None,
405
+ ):
406
+ provider = ask_provider_interactively()
407
+ if provider:
408
+ path = path if path else getcwd()
409
+ data_project_dir = data_project_dir if data_project_dir else "."
410
+ generator = CICDGeneratorBase.build_generator(provider)
411
+ workspace_info = await client.workspace_info()
412
+ token = await client.get_token_by_name("admin token")
413
+ params = {
414
+ "tb_host": client.host,
415
+ "workspace_name": workspace_info["name"],
416
+ "token_name": token["name"],
417
+ "token_id": token["id"],
418
+ "data_project_dir": data_project_dir,
419
+ "workflow_version": WORKFLOW_VERSION,
420
+ "tb_admin_token_name": f"TB_{str(workspace_info['name']).upper()}_ADMIN_TOKEN",
421
+ }
422
+ warning_message = generator(path, params)
423
+ if warning_message:
424
+ click.echo(warning_message)
425
+ click.echo(FeedbackManager.info_generate_cicd_config(provider=provider))
426
+
427
+
428
+ async def check_cicd_exists(path: Optional[str] = None) -> Optional[Provider]:
429
+ path = path if path else getcwd()
430
+ for provider in Provider:
431
+ generator = CICDGeneratorBase.build_generator(provider.name)
432
+ if generator.is_already_generated(path):
433
+ return provider
434
+ return None