tinybird 4.5.11.dev0__tar.gz → 4.5.12.dev0__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-4.5.11.dev0 → tinybird-4.5.12.dev0}/PKG-INFO +14 -2
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/__cli__.py +2 -2
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/client.py +17 -3
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/branch.py +14 -5
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/cli.py +32 -3
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/common.py +47 -14
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/connection.py +23 -2
- tinybird-4.5.12.dev0/tinybird/tb/modules/connection_kafka.py +804 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/create.py +34 -11
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/datafile/build.py +32 -5
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/feedback_manager.py +3 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/info.py +35 -11
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/preview.py +3 -2
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb_cli_modules/connection.py +80 -9
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird.egg-info/PKG-INFO +14 -2
- tinybird-4.5.11.dev0/tinybird/tb/modules/connection_kafka.py +0 -433
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/setup.cfg +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/__cli__.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/ch_utils/constants.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/ch_utils/engine.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/check_pypi.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/client.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/config.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/context.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/datafile/common.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/datafile/exceptions.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/datafile/parse_connection.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/datafile/parse_datasource.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/datafile/parse_pipe.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/datatypes.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/feedback_manager.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/git_settings.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/prompts.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/service_datasources.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/sql.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/sql_template.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/sql_template_fmt.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/sql_toolset.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/syncasync.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/check_pypi.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/cli.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/config.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/build.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/build_common.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/cicd.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/config.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/connection_s3.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/copy.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/datafile/build_common.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/datafile/build_pipe.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/datafile/diff.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/datafile/fixture.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/datafile/format_common.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/datafile/format_connection.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/datafile/format_pipe.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/datafile/pipe_checker.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/datafile/playground.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/datafile/pull.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/datasource.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/deployment.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/deployment_common.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/deprecations.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/endpoint.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/exceptions.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/fmt.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/infra.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/job.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/job_common.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/llm.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/llm_utils.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/local.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/local_common.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/local_logs.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/login.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/login_common.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/logout.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/logs.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/materialization.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/open.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/pipe.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/project.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/project_commands.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/py_project.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/query_output.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/regions.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/secret.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/secret_common.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/sink.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/table.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/telemetry.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/test.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/test_common.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/tinyunit/tinyunit.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/tinyunit/tinyunit_lib.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/token.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/ts_project.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/watch.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/workspace.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb/modules/workspace_members.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb_cli.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb_cli_modules/auth.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb_cli_modules/branch.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb_cli_modules/cicd.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb_cli_modules/cli.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb_cli_modules/common.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb_cli_modules/config.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb_cli_modules/datasource.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb_cli_modules/exceptions.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb_cli_modules/fmt.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb_cli_modules/job.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb_cli_modules/pipe.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb_cli_modules/regions.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb_cli_modules/tag.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb_cli_modules/telemetry.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb_cli_modules/test.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb_cli_modules/workspace.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tb_cli_modules/workspace_members.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird/tornado_template.py +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird.egg-info/SOURCES.txt +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird.egg-info/dependency_links.txt +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird.egg-info/entry_points.txt +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird.egg-info/requires.txt +0 -0
- {tinybird-4.5.11.dev0 → tinybird-4.5.12.dev0}/tinybird.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: tinybird
|
|
3
|
-
Version: 4.5.
|
|
3
|
+
Version: 4.5.12.dev0
|
|
4
4
|
Summary: Tinybird Command Line Tool
|
|
5
5
|
Home-page: https://www.tinybird.co/docs/forward/commands
|
|
6
6
|
Author: Tinybird
|
|
@@ -52,10 +52,22 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
|
|
|
52
52
|
Changelog
|
|
53
53
|
----------
|
|
54
54
|
|
|
55
|
-
4.5.
|
|
55
|
+
4.5.12
|
|
56
56
|
*******
|
|
57
57
|
|
|
58
|
+
- `Fixed` `tb info` now lists branches and marks the current branch correctly when running on a branch.
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
4.5.11
|
|
62
|
+
*******
|
|
63
|
+
|
|
64
|
+
- `Added` `tb connection create kafka` now supports SASL ``OAUTHBEARER`` for Amazon MSK clusters with IAM-based authentication. The wizard asks up-front whether the user already has an IAM role for the cluster — if yes, it collects region + role ARN + external ID directly; if no, it walks the user through creating the IAM access policy and trust policy in the AWS Console, copying them to the clipboard.
|
|
65
|
+
- `Added` ``--oauthbearer-aws-external-id`` flag (and matching interactive prompt) so users can supply a pre-shared external ID instead of the one Tinybird auto-generates for the connection.
|
|
66
|
+
- `Added` ``PLAINTEXT`` security protocol now skips the SASL mechanism prompt entirely.
|
|
67
|
+
- `Added` ``.connection`` files generated by the wizard now emit ``KAFKA_SASL_OAUTHBEARER_METHOD``, ``KAFKA_SASL_OAUTHBEARER_AWS_REGION``, ``KAFKA_SASL_OAUTHBEARER_AWS_ROLE_ARN``, and ``KAFKA_SASL_OAUTHBEARER_AWS_EXTERNAL_ID``, with the role ARN stored as a ``tb_secret``.
|
|
68
|
+
- `Fixed` ``tb connection create kafka`` now accepts comma-separated bootstrap-server lists (e.g. ``b-1.kafka:9092,b-2.kafka:9092``) instead of rejecting them as malformed. Validation passes as soon as any one broker is reachable, matching Kafka bootstrap semantics.
|
|
58
69
|
- `Fixed` `tb init` now persists `folder` in `tinybird.config.json`, so subsequent resource creation targets the configured project folder instead of the repository root.
|
|
70
|
+
- `Fixed` Branch names derived from Git refs are now sanitized in `tb preview --name` and `tb branch create/rm/clear`, preventing CI/CD failures with invalid workspace names.
|
|
59
71
|
|
|
60
72
|
4.5.9
|
|
61
73
|
*******
|
|
@@ -4,5 +4,5 @@ __description__ = 'Tinybird Command Line Tool'
|
|
|
4
4
|
__url__ = 'https://www.tinybird.co/docs/forward/commands'
|
|
5
5
|
__author__ = 'Tinybird'
|
|
6
6
|
__author_email__ = 'support@tinybird.co'
|
|
7
|
-
__version__ = '4.5.
|
|
8
|
-
__revision__ = '
|
|
7
|
+
__version__ = '4.5.12.dev0'
|
|
8
|
+
__revision__ = '6a70694'
|
|
@@ -1010,16 +1010,29 @@ class TinyB:
|
|
|
1010
1010
|
kafka_sasl_mechanism="PLAIN",
|
|
1011
1011
|
kafka_security_protocol="SASL_SSL",
|
|
1012
1012
|
kafka_ssl_ca_pem=None,
|
|
1013
|
+
kafka_sasl_oauthbearer_method=None,
|
|
1014
|
+
kafka_sasl_oauthbearer_aws_region=None,
|
|
1015
|
+
kafka_sasl_oauthbearer_aws_role_arn=None,
|
|
1016
|
+
kafka_sasl_oauthbearer_aws_external_id=None,
|
|
1013
1017
|
):
|
|
1018
|
+
is_oauthbearer = kafka_sasl_mechanism == "OAUTHBEARER"
|
|
1014
1019
|
params = {
|
|
1015
1020
|
"service": "kafka",
|
|
1016
1021
|
"kafka_security_protocol": kafka_security_protocol,
|
|
1017
1022
|
"kafka_sasl_mechanism": kafka_sasl_mechanism,
|
|
1018
1023
|
"kafka_bootstrap_servers": kafka_bootstrap_servers,
|
|
1019
|
-
"kafka_sasl_plain_username": kafka_key,
|
|
1020
|
-
"kafka_sasl_plain_password": kafka_secret,
|
|
1021
1024
|
"name": kafka_connection_name,
|
|
1022
1025
|
}
|
|
1026
|
+
if is_oauthbearer:
|
|
1027
|
+
params["kafka_sasl_oauthbearer_method"] = kafka_sasl_oauthbearer_method
|
|
1028
|
+
params["kafka_sasl_oauthbearer_aws_region"] = kafka_sasl_oauthbearer_aws_region
|
|
1029
|
+
params["kafka_sasl_oauthbearer_aws_role_arn"] = kafka_sasl_oauthbearer_aws_role_arn
|
|
1030
|
+
# external_id is optional; the server fills it in from the workspace if absent.
|
|
1031
|
+
if kafka_sasl_oauthbearer_aws_external_id:
|
|
1032
|
+
params["kafka_sasl_oauthbearer_aws_external_id"] = kafka_sasl_oauthbearer_aws_external_id
|
|
1033
|
+
else:
|
|
1034
|
+
params["kafka_sasl_plain_username"] = kafka_key
|
|
1035
|
+
params["kafka_sasl_plain_password"] = kafka_secret
|
|
1023
1036
|
|
|
1024
1037
|
if kafka_schema_registry_url:
|
|
1025
1038
|
params["kafka_schema_registry_url"] = kafka_schema_registry_url
|
|
@@ -1185,7 +1198,8 @@ class TinyB:
|
|
|
1185
1198
|
def get_access_read_policy(self, service: str, bucket: Optional[str] = None) -> Dict[str, Any]:
|
|
1186
1199
|
params = {}
|
|
1187
1200
|
if bucket:
|
|
1188
|
-
|
|
1201
|
+
# The Kafka endpoint scopes the policy via `msk_cluster_arn`, not `bucket`.
|
|
1202
|
+
params["msk_cluster_arn" if service == "kafka" else "bucket"] = bucket
|
|
1189
1203
|
return self._req(f"/v0/integrations/{service}/policies/read-access-policy?{urlencode(params)}")
|
|
1190
1204
|
|
|
1191
1205
|
def sql_get_format(self, sql: str, with_clickhouse_format: bool = False) -> str:
|
|
@@ -7,7 +7,7 @@ from typing import Any, Dict, List, Optional, Tuple
|
|
|
7
7
|
|
|
8
8
|
import click
|
|
9
9
|
|
|
10
|
-
from tinybird.tb.modules.cli import cli
|
|
10
|
+
from tinybird.tb.modules.cli import _looks_like_uuid, cli, ensure_valid_workspace_name
|
|
11
11
|
from tinybird.tb.modules.common import (
|
|
12
12
|
MAIN_BRANCH,
|
|
13
13
|
create_workspace_branch,
|
|
@@ -93,7 +93,8 @@ def branch_ls(sort: bool) -> None:
|
|
|
93
93
|
help="Wait for data branch jobs to finish, showing a progress bar. Disabled by default.",
|
|
94
94
|
)
|
|
95
95
|
def create_branch(branch_name: Optional[str], last_partition: bool, ignore_datasources: List[str], wait: bool) -> None:
|
|
96
|
-
|
|
96
|
+
normalized_branch_name = ensure_valid_workspace_name(branch_name) if branch_name else branch_name
|
|
97
|
+
create_workspace_branch(normalized_branch_name, last_partition, False, list(ignore_datasources), wait)
|
|
97
98
|
|
|
98
99
|
|
|
99
100
|
@branch.command(name="rm", short_help="Removes an branch from the workspace. It can't be recovered.")
|
|
@@ -194,11 +195,14 @@ def clear_branch(
|
|
|
194
195
|
if branch_name_or_id:
|
|
195
196
|
if branch_name_or_id == MAIN_BRANCH:
|
|
196
197
|
raise CLIException(FeedbackManager.error_not_allowed_in_main_branch())
|
|
198
|
+
lookup_branch_name_or_id = (
|
|
199
|
+
branch_name_or_id if _looks_like_uuid(branch_name_or_id) else ensure_valid_workspace_name(branch_name_or_id)
|
|
200
|
+
)
|
|
197
201
|
workspace_to_clear = next(
|
|
198
202
|
(
|
|
199
203
|
workspace
|
|
200
204
|
for workspace in workspace_branches
|
|
201
|
-
if workspace["name"] ==
|
|
205
|
+
if workspace["name"] == lookup_branch_name_or_id or workspace["id"] == lookup_branch_name_or_id
|
|
202
206
|
),
|
|
203
207
|
None,
|
|
204
208
|
)
|
|
@@ -210,11 +214,16 @@ def clear_branch(
|
|
|
210
214
|
raise CLIBranchException(FeedbackManager.error_not_a_branch(cli=get_cli_name()))
|
|
211
215
|
|
|
212
216
|
if not workspace_to_clear:
|
|
213
|
-
raise CLIBranchException(
|
|
217
|
+
raise CLIBranchException(
|
|
218
|
+
FeedbackManager.error_branch(
|
|
219
|
+
branch=lookup_branch_name_or_id if branch_name_or_id else "",
|
|
220
|
+
cli=get_cli_name(),
|
|
221
|
+
)
|
|
222
|
+
)
|
|
214
223
|
|
|
215
224
|
branch_name = workspace_to_clear["name"]
|
|
216
225
|
if yes or click.confirm(FeedbackManager.warning_confirm_clear_workspace()):
|
|
217
|
-
was_current_branch = workspace_to_clear["id"] == config
|
|
226
|
+
was_current_branch = workspace_to_clear["id"] == config["id"]
|
|
218
227
|
client = config.get_client(token=current_main_workspace.get("token"))
|
|
219
228
|
try:
|
|
220
229
|
client.delete_branch(workspace_to_clear["id"])
|
|
@@ -68,6 +68,7 @@ PROJECT_TYPE_TYPESCRIPT = "ts-sdk"
|
|
|
68
68
|
PROJECT_TYPE_PYTHON = "python-sdk"
|
|
69
69
|
PROJECT_TYPE_CLI = "cli"
|
|
70
70
|
PROJECT_TYPES = {PROJECT_TYPE_TYPESCRIPT, PROJECT_TYPE_PYTHON, PROJECT_TYPE_CLI}
|
|
71
|
+
UUID_PATTERN = re.compile(r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", re.IGNORECASE)
|
|
71
72
|
|
|
72
73
|
|
|
73
74
|
CLI_PROJECT_MARKERS = (
|
|
@@ -163,16 +164,44 @@ def is_main_git_branch(branch_name: Optional[str]) -> bool:
|
|
|
163
164
|
return branch_name in {"main", "master"}
|
|
164
165
|
|
|
165
166
|
|
|
166
|
-
def sanitize_branch_name(branch_name: str) -> str:
|
|
167
|
+
def sanitize_branch_name(branch_name: str, *, enforce_workspace_prefix_rules: bool = True) -> str:
|
|
167
168
|
sanitized = re.sub(r"[^a-zA-Z0-9_]", "_", branch_name)
|
|
168
169
|
sanitized = re.sub(r"_+", "_", sanitized)
|
|
169
|
-
|
|
170
|
+
sanitized = sanitized.strip("_")
|
|
171
|
+
if enforce_workspace_prefix_rules:
|
|
172
|
+
if sanitized and sanitized[0].isdigit():
|
|
173
|
+
sanitized = f"branch_{sanitized}"
|
|
174
|
+
if sanitized.startswith("d_"):
|
|
175
|
+
sanitized = f"branch_{sanitized}"
|
|
176
|
+
return sanitized
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _looks_like_uuid(value: str) -> bool:
|
|
180
|
+
return bool(UUID_PATTERN.fullmatch(value))
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def ensure_valid_workspace_name(name: str, *, context: str = "Branch") -> str:
|
|
184
|
+
sanitized = sanitize_branch_name(name)
|
|
185
|
+
if not sanitized:
|
|
186
|
+
raise CLIException(
|
|
187
|
+
FeedbackManager.error(
|
|
188
|
+
message=(
|
|
189
|
+
f"{context} name '{name}' is not valid. "
|
|
190
|
+
"Name must start with a letter and contain only letters, numbers, and underscores."
|
|
191
|
+
)
|
|
192
|
+
)
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
if sanitized != name:
|
|
196
|
+
click.echo(FeedbackManager.warning_branch_name_sanitized(context=context, original=name, sanitized=sanitized))
|
|
197
|
+
|
|
198
|
+
return sanitized
|
|
170
199
|
|
|
171
200
|
|
|
172
201
|
def get_tinybird_branch_name_from_git_branch(branch_name: Optional[str]) -> Optional[str]:
|
|
173
202
|
if not branch_name:
|
|
174
203
|
return None
|
|
175
|
-
sanitized = sanitize_branch_name(branch_name)
|
|
204
|
+
sanitized = sanitize_branch_name(branch_name, enforce_workspace_prefix_rules=False)
|
|
176
205
|
return sanitized or None
|
|
177
206
|
|
|
178
207
|
|
|
@@ -1105,26 +1105,57 @@ def is_url_valid(url):
|
|
|
1105
1105
|
return False
|
|
1106
1106
|
|
|
1107
1107
|
|
|
1108
|
-
def
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1108
|
+
def _parse_kafka_host_port(entry: str) -> tuple[str, int]:
|
|
1109
|
+
"""Parse a single Kafka bootstrap entry of the form 'host' or 'host:port'.
|
|
1110
|
+
|
|
1111
|
+
Raises CLIException on malformed input. Defaults the port to 9092 when omitted.
|
|
1112
|
+
"""
|
|
1113
|
+
parts = entry.split(":")
|
|
1112
1114
|
if len(parts) > 2:
|
|
1113
1115
|
raise CLIException(FeedbackManager.error_kafka_bootstrap_server())
|
|
1114
1116
|
host = parts[0]
|
|
1115
1117
|
port_str = parts[1] if len(parts) == 2 else "9092"
|
|
1116
1118
|
try:
|
|
1117
|
-
|
|
1119
|
+
return host, int(port_str)
|
|
1118
1120
|
except Exception:
|
|
1119
1121
|
raise CLIException(FeedbackManager.error_kafka_bootstrap_server())
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1122
|
+
|
|
1123
|
+
|
|
1124
|
+
def validate_kafka_bootstrap_servers(host_and_port):
|
|
1125
|
+
"""Validate a Kafka bootstrap-servers string (single entry or comma-separated list).
|
|
1126
|
+
|
|
1127
|
+
Format is checked for every entry. Connectivity is then probed per entry — Kafka
|
|
1128
|
+
only needs one reachable bootstrap broker to discover the rest of the cluster,
|
|
1129
|
+
so the validator passes as long as at least one host accepts a TCP connection.
|
|
1130
|
+
The last connection error is re-raised when every host is unreachable.
|
|
1131
|
+
"""
|
|
1132
|
+
if not isinstance(host_and_port, str):
|
|
1133
|
+
raise CLIException(FeedbackManager.error_kafka_bootstrap_server())
|
|
1134
|
+
|
|
1135
|
+
entries = [e.strip() for e in host_and_port.split(",") if e.strip()]
|
|
1136
|
+
if not entries:
|
|
1137
|
+
raise CLIException(FeedbackManager.error_kafka_bootstrap_server())
|
|
1138
|
+
|
|
1139
|
+
# Format-check every entry up front so the user hears about all bad ones at
|
|
1140
|
+
# once rather than discovering them sequentially.
|
|
1141
|
+
hosts = [_parse_kafka_host_port(e) for e in entries]
|
|
1142
|
+
|
|
1143
|
+
last_error: Optional[CLIException] = None
|
|
1144
|
+
for host, port in hosts:
|
|
1145
|
+
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
|
|
1146
|
+
try:
|
|
1147
|
+
sock.settimeout(3)
|
|
1148
|
+
sock.connect((host, port))
|
|
1149
|
+
return # at least one reachable broker is enough
|
|
1150
|
+
except TimeoutError:
|
|
1151
|
+
last_error = CLIException(
|
|
1152
|
+
FeedbackManager.error_kafka_bootstrap_server_conn_timeout(host=host, port=port)
|
|
1153
|
+
)
|
|
1154
|
+
except Exception:
|
|
1155
|
+
last_error = CLIException(FeedbackManager.error_kafka_bootstrap_server_conn(host=host, port=port))
|
|
1156
|
+
|
|
1157
|
+
if last_error is not None:
|
|
1158
|
+
raise last_error
|
|
1128
1159
|
|
|
1129
1160
|
|
|
1130
1161
|
def validate_kafka_key(s):
|
|
@@ -1137,9 +1168,11 @@ def validate_kafka_secret(s):
|
|
|
1137
1168
|
raise CLIException("Password format is not correct, it must be a string")
|
|
1138
1169
|
|
|
1139
1170
|
|
|
1140
|
-
def validate_string_connector_param(param, s):
|
|
1171
|
+
def validate_string_connector_param(param: str, s: str) -> None:
|
|
1141
1172
|
if not isinstance(s, str):
|
|
1142
1173
|
raise CLIConnectionException(param + " format is not correct, it must be a string")
|
|
1174
|
+
if not s or not s.strip():
|
|
1175
|
+
raise CLIConnectionException(param + " cannot be empty")
|
|
1143
1176
|
|
|
1144
1177
|
|
|
1145
1178
|
def validate_connection_name(client, connection_name, service):
|
|
@@ -40,6 +40,18 @@ from tinybird.tb.modules.feedback_manager import FeedbackManager, get_cli_name
|
|
|
40
40
|
from tinybird.tb.modules.project import Project
|
|
41
41
|
from tinybird.tb.modules.secret import save_secret_to_env_file
|
|
42
42
|
|
|
43
|
+
|
|
44
|
+
def _upper_or_none(_ctx: click.Context, _param: click.Parameter, value: Optional[str]) -> Optional[str]:
|
|
45
|
+
"""Click callback that uppercases the option value if present.
|
|
46
|
+
|
|
47
|
+
Kafka SASL mechanisms and security protocols are matched case-sensitively
|
|
48
|
+
downstream (CLI, client, server), so we normalize at the flag layer to
|
|
49
|
+
accept user input like `--sasl-mechanism oauthbearer` without silently
|
|
50
|
+
falling through the case-sensitive branches and breaking the request.
|
|
51
|
+
"""
|
|
52
|
+
return value.upper() if value else value
|
|
53
|
+
|
|
54
|
+
|
|
43
55
|
DATA_CONNECTOR_SETTINGS: Dict[DataConnectorType, List[str]] = {
|
|
44
56
|
DataConnectorType.KAFKA: [
|
|
45
57
|
"kafka_bootstrap_servers",
|
|
@@ -247,15 +259,22 @@ def connection_create_gcs(ctx: Context) -> None:
|
|
|
247
259
|
@click.option("--schema-registry-url", default=None, help="Avro Confluent Schema Registry URL")
|
|
248
260
|
@click.option(
|
|
249
261
|
"--sasl-mechanism",
|
|
250
|
-
default=
|
|
262
|
+
default=None,
|
|
263
|
+
callback=_upper_or_none,
|
|
251
264
|
help="Authentication method for connection-based protocols. Defaults to 'PLAIN'",
|
|
252
265
|
)
|
|
253
266
|
@click.option(
|
|
254
267
|
"--security-protocol",
|
|
255
|
-
default=
|
|
268
|
+
default=None,
|
|
269
|
+
callback=_upper_or_none,
|
|
256
270
|
help="Security protocol for connection-based protocols. Defaults to 'SASL_SSL'",
|
|
257
271
|
)
|
|
258
272
|
@click.option("--ssl-ca-pem", default=None, help="Path or content of the CA Certificate file in PEM format")
|
|
273
|
+
@click.option(
|
|
274
|
+
"--oauthbearer-aws-external-id",
|
|
275
|
+
default=None,
|
|
276
|
+
help="Pre-shared external_id for the AWS assume-role call (OAUTHBEARER+AWS only). If omitted, the wizard prompts for one and falls back to a Tinybird-generated value.",
|
|
277
|
+
)
|
|
259
278
|
@click.pass_context
|
|
260
279
|
def connection_create_kafka_cmd(
|
|
261
280
|
ctx: Context,
|
|
@@ -268,6 +287,7 @@ def connection_create_kafka_cmd(
|
|
|
268
287
|
sasl_mechanism: Optional[str],
|
|
269
288
|
security_protocol: Optional[str],
|
|
270
289
|
ssl_ca_pem: Optional[str],
|
|
290
|
+
oauthbearer_aws_external_id: Optional[str],
|
|
271
291
|
) -> None:
|
|
272
292
|
env: str = ctx.ensure_object(dict)["env"]
|
|
273
293
|
project: Project = ctx.ensure_object(dict)["project"]
|
|
@@ -295,6 +315,7 @@ def connection_create_kafka_cmd(
|
|
|
295
315
|
sasl_mechanism=sasl_mechanism,
|
|
296
316
|
security_protocol=security_protocol,
|
|
297
317
|
ssl_ca_pem=ssl_ca_pem,
|
|
318
|
+
oauthbearer_aws_external_id=oauthbearer_aws_external_id,
|
|
298
319
|
)
|
|
299
320
|
|
|
300
321
|
if not result["error"]:
|