tinybird 0.0.1.dev15__py3-none-any.whl → 0.0.1.dev17__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.
- tinybird/client.py +3 -1
- tinybird/feedback_manager.py +12 -1
- tinybird/{tb/modules/prompts.py → prompts.py} +26 -64
- tinybird/tb/cli.py +2 -1
- tinybird/tb/modules/build.py +37 -153
- tinybird/tb/modules/build_shell.py +149 -0
- tinybird/tb/modules/cli.py +9 -87
- tinybird/tb/modules/common.py +7 -98
- tinybird/tb/modules/config.py +0 -10
- tinybird/tb/modules/create.py +82 -8
- tinybird/tb/modules/datafile/build_pipe.py +1 -1
- tinybird/tb/modules/datasource.py +0 -10
- tinybird/tb/modules/llm.py +35 -3
- tinybird/tb/modules/local.py +2 -46
- tinybird/tb/modules/local_common.py +54 -0
- tinybird/tb/modules/mock.py +1 -1
- tinybird/tb/modules/pipe.py +0 -4
- tinybird/tb/modules/test.py +23 -12
- {tinybird-0.0.1.dev15.dist-info → tinybird-0.0.1.dev17.dist-info}/METADATA +1 -1
- {tinybird-0.0.1.dev15.dist-info → tinybird-0.0.1.dev17.dist-info}/RECORD +23 -22
- tinybird/tb/modules/branch.py +0 -1023
- {tinybird-0.0.1.dev15.dist-info → tinybird-0.0.1.dev17.dist-info}/WHEEL +0 -0
- {tinybird-0.0.1.dev15.dist-info → tinybird-0.0.1.dev17.dist-info}/entry_points.txt +0 -0
- {tinybird-0.0.1.dev15.dist-info → tinybird-0.0.1.dev17.dist-info}/top_level.txt +0 -0
tinybird/tb/modules/branch.py
DELETED
|
@@ -1,1023 +0,0 @@
|
|
|
1
|
-
# This is a command file for our CLI. Please keep it clean.
|
|
2
|
-
#
|
|
3
|
-
# - If it makes sense and only when strictly necessary, you can create utility functions in this file.
|
|
4
|
-
# - But please, **do not** interleave utility functions and command definitions.
|
|
5
|
-
|
|
6
|
-
import os
|
|
7
|
-
from typing import List, Optional, Tuple
|
|
8
|
-
|
|
9
|
-
import aiofiles
|
|
10
|
-
import click
|
|
11
|
-
import yaml
|
|
12
|
-
|
|
13
|
-
from tinybird.feedback_manager import FeedbackManager
|
|
14
|
-
from tinybird.tb.modules.cli import cli
|
|
15
|
-
from tinybird.tb.modules.common import (
|
|
16
|
-
MAIN_BRANCH,
|
|
17
|
-
OLDEST_ROLLBACK,
|
|
18
|
-
coro,
|
|
19
|
-
create_workspace_branch,
|
|
20
|
-
echo_safe_humanfriendly_tables_format_smart_table,
|
|
21
|
-
get_current_main_workspace,
|
|
22
|
-
get_current_workspace,
|
|
23
|
-
get_current_workspace_branches,
|
|
24
|
-
get_oldest_rollback,
|
|
25
|
-
get_workspace_member_email,
|
|
26
|
-
getenv_bool,
|
|
27
|
-
print_branch_regression_tests_summary,
|
|
28
|
-
print_current_branch,
|
|
29
|
-
print_current_workspace,
|
|
30
|
-
print_data_branch_summary,
|
|
31
|
-
print_release_summary,
|
|
32
|
-
remove_release,
|
|
33
|
-
switch_to_workspace_by_user_workspace_data,
|
|
34
|
-
switch_workspace,
|
|
35
|
-
try_update_config_with_remote,
|
|
36
|
-
wait_job,
|
|
37
|
-
)
|
|
38
|
-
from tinybird.tb.modules.config import CLIConfig
|
|
39
|
-
from tinybird.tb.modules.exceptions import CLIBranchException, CLIException, CLIReleaseException
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
@cli.group(hidden=True)
|
|
43
|
-
def release() -> None:
|
|
44
|
-
"""Release commands"""
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
@release.command(name="ls", short_help="Lists Releases for the current Workspace")
|
|
48
|
-
@coro
|
|
49
|
-
async def release_ls() -> None:
|
|
50
|
-
"""List current available Releases in the Workspace"""
|
|
51
|
-
config = CLIConfig.get_project_config()
|
|
52
|
-
_ = await try_update_config_with_remote(config, only_if_needed=True)
|
|
53
|
-
|
|
54
|
-
await print_releases(config)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
async def print_releases(config: CLIConfig):
|
|
58
|
-
response = await config.get_client().releases(config["id"])
|
|
59
|
-
|
|
60
|
-
table: List[Tuple[str, str, str, str, str]] = []
|
|
61
|
-
for release in response["releases"]:
|
|
62
|
-
table.append(
|
|
63
|
-
(release["created_at"], release["semver"], release["status"], release["commit"], release["rollback"])
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
columns = ["created_at", "semver", "status", "commit", "rollback release"]
|
|
67
|
-
click.echo(FeedbackManager.info_releases())
|
|
68
|
-
echo_safe_humanfriendly_tables_format_smart_table(table, column_names=columns)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
@release.command(name="generate", short_help="Generates a custom deployment for a Release")
|
|
72
|
-
@click.option(
|
|
73
|
-
"--semver",
|
|
74
|
-
is_flag=False,
|
|
75
|
-
required=True,
|
|
76
|
-
type=str,
|
|
77
|
-
help="Semver of the new Release. Example: 1.0.0",
|
|
78
|
-
)
|
|
79
|
-
@coro
|
|
80
|
-
async def release_generate(semver: str) -> None:
|
|
81
|
-
click.echo(FeedbackManager.warning_deprecated_releases())
|
|
82
|
-
if os.path.exists(".tinyenv"):
|
|
83
|
-
async with aiofiles.open(".tinyenv", "r") as env_file:
|
|
84
|
-
lines = await env_file.readlines()
|
|
85
|
-
|
|
86
|
-
updated_lines = []
|
|
87
|
-
for line in lines:
|
|
88
|
-
if line.startswith("VERSION="):
|
|
89
|
-
updated_lines.append(f"VERSION={semver}\n")
|
|
90
|
-
else:
|
|
91
|
-
updated_lines.append(line)
|
|
92
|
-
|
|
93
|
-
async with aiofiles.open(".tinyenv", "w") as env_file:
|
|
94
|
-
await env_file.writelines(updated_lines)
|
|
95
|
-
else:
|
|
96
|
-
async with aiofiles.open(".tinyenv", "w") as env_file:
|
|
97
|
-
await env_file.write(f"VERSION={semver}\n")
|
|
98
|
-
|
|
99
|
-
deploy_dir = os.path.join("deploy", semver)
|
|
100
|
-
os.makedirs(deploy_dir, exist_ok=True)
|
|
101
|
-
|
|
102
|
-
deploy_file = os.path.join(deploy_dir, "deploy.sh")
|
|
103
|
-
async with aiofiles.open(deploy_file, "w") as deploy:
|
|
104
|
-
await deploy.write(
|
|
105
|
-
"""\
|
|
106
|
-
#!/bin/bash
|
|
107
|
-
set -euxo pipefail
|
|
108
|
-
|
|
109
|
-
# tb --semver $VERSION deploy
|
|
110
|
-
"""
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
os.chmod(deploy_file, 0o755)
|
|
114
|
-
|
|
115
|
-
post_deploy_file = os.path.join(deploy_dir, "postdeploy.sh")
|
|
116
|
-
async with aiofiles.open(post_deploy_file, "w") as post_deploy:
|
|
117
|
-
await post_deploy.write(
|
|
118
|
-
"""\
|
|
119
|
-
#!/bin/bash
|
|
120
|
-
set -euxo pipefail
|
|
121
|
-
|
|
122
|
-
# tb --semver $VERSION pipe populate <pipe_name> --node <node_name> --sql-condition <sql> --wait
|
|
123
|
-
"""
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
os.chmod(post_deploy_file, 0o755)
|
|
127
|
-
|
|
128
|
-
click.echo(FeedbackManager.info_release_generated(semver=semver))
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
@release.command(name="promote", short_help="Promotes to live status a preview Release")
|
|
132
|
-
@click.option(
|
|
133
|
-
"--semver", required=True, type=str, help="Semver of a preview Release to promote to live. Example: 1.0.0"
|
|
134
|
-
)
|
|
135
|
-
@coro
|
|
136
|
-
async def release_promote(semver: str) -> None:
|
|
137
|
-
"""
|
|
138
|
-
The oldest rollback Release will be automatically removed if no usage, otherwise export TB_FORCE_REMOVE_OLDEST_ROLLBACK="1" to force deletion
|
|
139
|
-
"""
|
|
140
|
-
click.echo(FeedbackManager.warning_deprecated_releases())
|
|
141
|
-
config = CLIConfig.get_project_config()
|
|
142
|
-
_ = await try_update_config_with_remote(config, only_if_needed=True)
|
|
143
|
-
|
|
144
|
-
client = config.get_client()
|
|
145
|
-
|
|
146
|
-
try:
|
|
147
|
-
force_remove = getenv_bool("TB_FORCE_REMOVE_OLDEST_ROLLBACK", False)
|
|
148
|
-
click.echo(FeedbackManager.warning_remove_oldest_rollback(semver=semver))
|
|
149
|
-
try:
|
|
150
|
-
await remove_release(False, config, OLDEST_ROLLBACK, client, force=force_remove)
|
|
151
|
-
except Exception as e:
|
|
152
|
-
click.echo(FeedbackManager.error_remove_oldest_rollback(error=str(e), semver=semver))
|
|
153
|
-
release = await client.release_promote(config["id"], semver)
|
|
154
|
-
click.echo(FeedbackManager.success_release_promote(semver=semver))
|
|
155
|
-
click.echo(FeedbackManager.success_git_release(release_commit=release["commit"]))
|
|
156
|
-
except Exception as e:
|
|
157
|
-
raise CLIReleaseException(FeedbackManager.error_exception(error=str(e)))
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
@release.command(name="preview", short_help="Updates the status of a deploying Release to preview")
|
|
161
|
-
@click.option(
|
|
162
|
-
"--semver", is_flag=False, required=True, type=str, help="Semver of a preview Release to preview. Example: 1.0.0"
|
|
163
|
-
)
|
|
164
|
-
@coro
|
|
165
|
-
async def release_preview(semver: str) -> None:
|
|
166
|
-
click.echo(FeedbackManager.warning_deprecated_releases())
|
|
167
|
-
config = CLIConfig.get_project_config()
|
|
168
|
-
_ = await try_update_config_with_remote(config, only_if_needed=True)
|
|
169
|
-
|
|
170
|
-
client = config.get_client()
|
|
171
|
-
|
|
172
|
-
try:
|
|
173
|
-
await client.release_preview(config["id"], semver)
|
|
174
|
-
click.echo(FeedbackManager.success_release_preview(semver=semver))
|
|
175
|
-
except Exception as e:
|
|
176
|
-
raise CLIReleaseException(FeedbackManager.error_exception(error=str(e)))
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
@release.command(name="rollback", short_help="Rollbacks to a previous Release")
|
|
180
|
-
@click.option("--yes", is_flag=True, default=False, help="Do not ask for confirmation")
|
|
181
|
-
@coro
|
|
182
|
-
async def release_rollback(yes: bool) -> None:
|
|
183
|
-
click.echo(FeedbackManager.warning_deprecated_releases())
|
|
184
|
-
config = CLIConfig.get_project_config()
|
|
185
|
-
_ = await try_update_config_with_remote(config, only_if_needed=False)
|
|
186
|
-
|
|
187
|
-
client = config.get_client()
|
|
188
|
-
|
|
189
|
-
releases_response = await config.get_client().releases(config["id"])
|
|
190
|
-
|
|
191
|
-
try:
|
|
192
|
-
release_to_rollback = next(
|
|
193
|
-
(release for release in releases_response["releases"] if release["status"] == "live"), None
|
|
194
|
-
)
|
|
195
|
-
if not release_to_rollback:
|
|
196
|
-
raise CLIBranchException(FeedbackManager.error_release_rollback_live_not_found())
|
|
197
|
-
|
|
198
|
-
await print_releases(config)
|
|
199
|
-
semver = release_to_rollback.get("semver")
|
|
200
|
-
rollback_version = release_to_rollback.get("rollback")
|
|
201
|
-
await print_release_summary(config, rollback_version)
|
|
202
|
-
if yes or click.confirm(
|
|
203
|
-
FeedbackManager.warning_confirm_rollback_release(semver=semver, rollback=rollback_version)
|
|
204
|
-
):
|
|
205
|
-
release = await client.release_rollback(config["id"], semver=semver)
|
|
206
|
-
click.echo(FeedbackManager.success_release_rollback(semver=release["semver"]))
|
|
207
|
-
except Exception as e:
|
|
208
|
-
raise CLIReleaseException(FeedbackManager.error_exception(error=str(e)))
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
@release.command(name="rm", short_help="Removes a preview or failed Release. This action is irreversible")
|
|
212
|
-
@click.option(
|
|
213
|
-
"--semver", required=False, type=str, help="Semver of a preview or failed Release to delete. Example: 1.0.0"
|
|
214
|
-
)
|
|
215
|
-
@click.option(
|
|
216
|
-
"--oldest-rollback", is_flag=True, default=False, help="Removes the oldest rollback Release by creation date"
|
|
217
|
-
)
|
|
218
|
-
@click.option(
|
|
219
|
-
"--force", is_flag=True, default=False, help="USE WITH CAUTION! Allows to delete a Release that is currently in use"
|
|
220
|
-
)
|
|
221
|
-
@click.option("--yes", is_flag=True, default=False, help="Do not ask for confirmation")
|
|
222
|
-
@click.option(
|
|
223
|
-
"--dry-run", is_flag=True, default=False, help="Checks the Release could be deleted without actually deleting it"
|
|
224
|
-
)
|
|
225
|
-
@coro
|
|
226
|
-
async def release_rm(semver: str, oldest_rollback: bool, force: bool, yes: bool, dry_run: bool) -> None:
|
|
227
|
-
click.echo(FeedbackManager.warning_deprecated_releases())
|
|
228
|
-
if (not semver and not oldest_rollback) or (semver and oldest_rollback):
|
|
229
|
-
raise CLIException(FeedbackManager.error_release_rm_param())
|
|
230
|
-
|
|
231
|
-
config = CLIConfig.get_project_config()
|
|
232
|
-
client = config.get_client()
|
|
233
|
-
_ = await try_update_config_with_remote(config)
|
|
234
|
-
|
|
235
|
-
if oldest_rollback:
|
|
236
|
-
oldest_rollback_semver = await get_oldest_rollback(config, client)
|
|
237
|
-
if not oldest_rollback_semver:
|
|
238
|
-
click.echo(FeedbackManager.info_release_no_rollback())
|
|
239
|
-
return
|
|
240
|
-
else:
|
|
241
|
-
semver = oldest_rollback_semver
|
|
242
|
-
await print_release_summary(config, semver, info=True, dry_run=dry_run)
|
|
243
|
-
if dry_run or yes or click.confirm(FeedbackManager.warning_confirm_delete_release(semver=semver)):
|
|
244
|
-
try:
|
|
245
|
-
await remove_release(dry_run, config, semver, client, force, show_print=False)
|
|
246
|
-
except Exception as e:
|
|
247
|
-
raise CLIReleaseException(FeedbackManager.simple_error_exception(error=str(e)))
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
@cli.group()
|
|
251
|
-
def branch() -> None:
|
|
252
|
-
"""Branch commands. Branches are an experimental feature only available in beta. Running branch commands without activation will return an error"""
|
|
253
|
-
pass
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
@branch.command(name="ls")
|
|
257
|
-
@click.option("--sort/--no-sort", default=False, help="Sort the table rows by name")
|
|
258
|
-
@coro
|
|
259
|
-
async def branch_ls(sort: bool) -> None:
|
|
260
|
-
"""List all the branches available using the workspace token"""
|
|
261
|
-
|
|
262
|
-
config = CLIConfig.get_project_config()
|
|
263
|
-
_ = await try_update_config_with_remote(config, only_if_needed=True)
|
|
264
|
-
|
|
265
|
-
client = config.get_client()
|
|
266
|
-
|
|
267
|
-
current_main_workspace = await get_current_main_workspace(config)
|
|
268
|
-
assert isinstance(current_main_workspace, dict)
|
|
269
|
-
|
|
270
|
-
if current_main_workspace["id"] != config["id"]:
|
|
271
|
-
client = config.get_client(token=current_main_workspace["token"])
|
|
272
|
-
|
|
273
|
-
current_main_owner_email = get_workspace_member_email(current_main_workspace, current_main_workspace["owner"])
|
|
274
|
-
|
|
275
|
-
response = await client.branches()
|
|
276
|
-
|
|
277
|
-
columns = ["name", "id", "created_at", "owner", "current"]
|
|
278
|
-
|
|
279
|
-
table: List[Tuple[str, str, str, str, bool]] = [
|
|
280
|
-
(
|
|
281
|
-
MAIN_BRANCH,
|
|
282
|
-
current_main_workspace["id"],
|
|
283
|
-
current_main_workspace["created_at"],
|
|
284
|
-
current_main_owner_email,
|
|
285
|
-
config["id"] == current_main_workspace["id"],
|
|
286
|
-
)
|
|
287
|
-
]
|
|
288
|
-
|
|
289
|
-
for branch in response["environments"]:
|
|
290
|
-
branch_owner_email = get_workspace_member_email(branch, branch["owner"])
|
|
291
|
-
|
|
292
|
-
table.append(
|
|
293
|
-
(branch["name"], branch["id"], branch["created_at"], branch_owner_email, config["id"] == branch["id"])
|
|
294
|
-
)
|
|
295
|
-
|
|
296
|
-
current_branch = [row for row in table if row[4]]
|
|
297
|
-
other_branches = [row for row in table if not row[4]]
|
|
298
|
-
|
|
299
|
-
if sort:
|
|
300
|
-
other_branches.sort(key=lambda x: x[0])
|
|
301
|
-
|
|
302
|
-
sorted_table = current_branch + other_branches
|
|
303
|
-
|
|
304
|
-
await print_current_workspace(config)
|
|
305
|
-
|
|
306
|
-
click.echo(FeedbackManager.info_branches())
|
|
307
|
-
echo_safe_humanfriendly_tables_format_smart_table(sorted_table, column_names=columns)
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
@branch.command(name="use")
|
|
311
|
-
@click.argument("branch_name_or_id")
|
|
312
|
-
@coro
|
|
313
|
-
async def branch_use(branch_name_or_id: str) -> None:
|
|
314
|
-
"""Switch to another Branch (requires an admin token associated with a user). Use 'tb branch ls' to list the Branches you can access"""
|
|
315
|
-
|
|
316
|
-
config = CLIConfig.get_project_config()
|
|
317
|
-
_ = await try_update_config_with_remote(config, only_if_needed=True)
|
|
318
|
-
|
|
319
|
-
if branch_name_or_id == MAIN_BRANCH:
|
|
320
|
-
current_main_workspace = await get_current_main_workspace(config)
|
|
321
|
-
assert isinstance(current_main_workspace, dict)
|
|
322
|
-
await switch_to_workspace_by_user_workspace_data(config, current_main_workspace)
|
|
323
|
-
else:
|
|
324
|
-
await switch_workspace(config, branch_name_or_id, only_environments=True)
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
@branch.command(name="current")
|
|
328
|
-
@coro
|
|
329
|
-
async def branch_current() -> None:
|
|
330
|
-
"""Show the Branch you're currently authenticated to"""
|
|
331
|
-
config = CLIConfig.get_project_config()
|
|
332
|
-
await print_current_branch(config)
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
@branch.command(name="create", short_help="Create a new Branch in the current 'main' Workspace")
|
|
336
|
-
@click.argument("branch_name", required=False)
|
|
337
|
-
@click.option(
|
|
338
|
-
"--last-partition",
|
|
339
|
-
is_flag=True,
|
|
340
|
-
default=False,
|
|
341
|
-
help="Attach the last modified partition from 'main' to the new Branch",
|
|
342
|
-
)
|
|
343
|
-
@click.option(
|
|
344
|
-
"--all",
|
|
345
|
-
is_flag=True,
|
|
346
|
-
default=False,
|
|
347
|
-
help="Attach all data from 'main' to the new Branch. Use only if you actually need all the data in the Branch",
|
|
348
|
-
hidden=True,
|
|
349
|
-
)
|
|
350
|
-
@click.option(
|
|
351
|
-
"-i",
|
|
352
|
-
"--ignore-datasource",
|
|
353
|
-
"ignore_datasources",
|
|
354
|
-
type=str,
|
|
355
|
-
multiple=True,
|
|
356
|
-
help="Ignore specified data source partitions",
|
|
357
|
-
)
|
|
358
|
-
@click.option(
|
|
359
|
-
"--wait/--no-wait",
|
|
360
|
-
is_flag=True,
|
|
361
|
-
default=True,
|
|
362
|
-
help="Wait for data branch jobs to finish, showing a progress bar. Disabled by default.",
|
|
363
|
-
)
|
|
364
|
-
@coro
|
|
365
|
-
async def create_branch(
|
|
366
|
-
branch_name: Optional[str], last_partition: bool, all: bool, ignore_datasources: List[str], wait: bool
|
|
367
|
-
) -> None:
|
|
368
|
-
if last_partition and all:
|
|
369
|
-
raise CLIException(FeedbackManager.error_exception(error="Use --last-partition or --all but not both"))
|
|
370
|
-
await create_workspace_branch(branch_name, last_partition, all, list(ignore_datasources), wait)
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
@branch.command(name="rm", short_help="Removes a Branch from the Workspace. It can't be recovered.")
|
|
374
|
-
@click.argument("branch_name_or_id")
|
|
375
|
-
@click.option("--yes", is_flag=True, default=False, help="Do not ask for confirmation")
|
|
376
|
-
@coro
|
|
377
|
-
async def delete_branch(branch_name_or_id: str, yes: bool) -> None:
|
|
378
|
-
"""Remove an Branch (not Main)"""
|
|
379
|
-
|
|
380
|
-
config = CLIConfig.get_project_config()
|
|
381
|
-
_ = await try_update_config_with_remote(config)
|
|
382
|
-
|
|
383
|
-
client = config.get_client()
|
|
384
|
-
|
|
385
|
-
if branch_name_or_id == MAIN_BRANCH:
|
|
386
|
-
raise CLIException(FeedbackManager.error_not_allowed_in_main_branch())
|
|
387
|
-
|
|
388
|
-
try:
|
|
389
|
-
workspace_branches = await get_current_workspace_branches(config)
|
|
390
|
-
workspace_to_delete = next(
|
|
391
|
-
(
|
|
392
|
-
workspace
|
|
393
|
-
for workspace in workspace_branches
|
|
394
|
-
if workspace["name"] == branch_name_or_id or workspace["id"] == branch_name_or_id
|
|
395
|
-
),
|
|
396
|
-
None,
|
|
397
|
-
)
|
|
398
|
-
except Exception as e:
|
|
399
|
-
raise CLIBranchException(FeedbackManager.error_exception(error=str(e)))
|
|
400
|
-
|
|
401
|
-
if not workspace_to_delete:
|
|
402
|
-
raise CLIBranchException(FeedbackManager.error_branch(branch=branch_name_or_id))
|
|
403
|
-
|
|
404
|
-
if yes or click.confirm(FeedbackManager.warning_confirm_delete_branch(branch=workspace_to_delete["name"])):
|
|
405
|
-
need_to_switch_to_main = workspace_to_delete.get("main") and config["id"] == workspace_to_delete["id"]
|
|
406
|
-
# get origin workspace if deleting current branch
|
|
407
|
-
if need_to_switch_to_main:
|
|
408
|
-
try:
|
|
409
|
-
workspaces = (await client.user_workspaces()).get("workspaces", [])
|
|
410
|
-
workspace_main = next(
|
|
411
|
-
(workspace for workspace in workspaces if workspace["id"] == workspace_to_delete["main"]), None
|
|
412
|
-
)
|
|
413
|
-
except Exception:
|
|
414
|
-
workspace_main = None
|
|
415
|
-
try:
|
|
416
|
-
await client.delete_branch(workspace_to_delete["id"])
|
|
417
|
-
click.echo(FeedbackManager.success_branch_deleted(branch_name=workspace_to_delete["name"]))
|
|
418
|
-
except Exception as e:
|
|
419
|
-
raise CLIBranchException(FeedbackManager.error_exception(error=str(e)))
|
|
420
|
-
else:
|
|
421
|
-
if need_to_switch_to_main:
|
|
422
|
-
if workspace_main:
|
|
423
|
-
await switch_to_workspace_by_user_workspace_data(config, workspace_main)
|
|
424
|
-
else:
|
|
425
|
-
raise CLIException(FeedbackManager.error_switching_to_main())
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
@branch.command(
|
|
429
|
-
name="data",
|
|
430
|
-
short_help="Perform a data branch operation to bring data into the current Branch. Check flags for details",
|
|
431
|
-
)
|
|
432
|
-
@click.option(
|
|
433
|
-
"--last-partition",
|
|
434
|
-
is_flag=True,
|
|
435
|
-
default=False,
|
|
436
|
-
help="Attach the last modified partition from 'main' to the new Branch",
|
|
437
|
-
)
|
|
438
|
-
@click.option(
|
|
439
|
-
"--all",
|
|
440
|
-
is_flag=True,
|
|
441
|
-
default=False,
|
|
442
|
-
help="Attach all data from 'main' to the new Branch. Use only if you actually need all the data in the Branch",
|
|
443
|
-
hidden=True,
|
|
444
|
-
)
|
|
445
|
-
@click.option(
|
|
446
|
-
"-i",
|
|
447
|
-
"--ignore-datasource",
|
|
448
|
-
"ignore_datasources",
|
|
449
|
-
type=str,
|
|
450
|
-
multiple=True,
|
|
451
|
-
help="Ignore specified data source partitions",
|
|
452
|
-
)
|
|
453
|
-
@click.option(
|
|
454
|
-
"--wait",
|
|
455
|
-
is_flag=True,
|
|
456
|
-
default=False,
|
|
457
|
-
help="Wait for data branch jobs to finish, showing a progress bar. Disabled by default.",
|
|
458
|
-
)
|
|
459
|
-
@coro
|
|
460
|
-
async def data_branch(last_partition: bool, all: bool, ignore_datasources: List[str], wait: bool) -> None:
|
|
461
|
-
if last_partition and all:
|
|
462
|
-
raise CLIException(FeedbackManager.error_exception(error="Use --last-partition or --all but not both"))
|
|
463
|
-
|
|
464
|
-
if not last_partition and not all:
|
|
465
|
-
raise CLIException(FeedbackManager.error_exception(error="Use --last-partition or --all"))
|
|
466
|
-
|
|
467
|
-
config = CLIConfig.get_project_config()
|
|
468
|
-
client = config.get_client()
|
|
469
|
-
|
|
470
|
-
current_main_workspace = await get_current_main_workspace(config)
|
|
471
|
-
assert isinstance(current_main_workspace, dict)
|
|
472
|
-
|
|
473
|
-
if current_main_workspace["id"] == config["id"]:
|
|
474
|
-
raise CLIException(FeedbackManager.error_not_allowed_in_main_branch())
|
|
475
|
-
|
|
476
|
-
try:
|
|
477
|
-
response = await client.branch_workspace_data(config["id"], last_partition, all, ignore_datasources)
|
|
478
|
-
|
|
479
|
-
is_job: bool = "job" in response
|
|
480
|
-
is_summary: bool = "partitions" in response
|
|
481
|
-
|
|
482
|
-
if not is_job and not is_summary:
|
|
483
|
-
raise CLIBranchException(str(response))
|
|
484
|
-
|
|
485
|
-
if all and not is_job:
|
|
486
|
-
raise CLIBranchException(str(response))
|
|
487
|
-
|
|
488
|
-
if wait and is_job:
|
|
489
|
-
job_id = response["job"]["job_id"]
|
|
490
|
-
job_url = response["job"]["job_url"]
|
|
491
|
-
click.echo(FeedbackManager.info_data_branch_job_url(url=job_url))
|
|
492
|
-
job_response = await wait_job(client, job_id, job_url, "Branch creation")
|
|
493
|
-
response = job_response["result"]
|
|
494
|
-
is_job = False
|
|
495
|
-
is_summary = "partitions" in response
|
|
496
|
-
|
|
497
|
-
if is_job:
|
|
498
|
-
click.echo(FeedbackManager.success_workspace_data_branch_in_progress(job_url=response["job"]["job_url"]))
|
|
499
|
-
else:
|
|
500
|
-
if not is_job and not is_summary:
|
|
501
|
-
FeedbackManager.warning_unknown_response(response=response)
|
|
502
|
-
elif is_summary and (bool(last_partition) or bool(all)):
|
|
503
|
-
await print_data_branch_summary(client, None, response)
|
|
504
|
-
click.echo(FeedbackManager.success_workspace_data_branch())
|
|
505
|
-
|
|
506
|
-
except Exception as e:
|
|
507
|
-
raise CLIBranchException(FeedbackManager.error_exception(error=str(e)))
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
@branch.group("regression-tests", invoke_without_command=True)
|
|
511
|
-
@click.option(
|
|
512
|
-
"-f",
|
|
513
|
-
"--filename",
|
|
514
|
-
type=click.Path(exists=True),
|
|
515
|
-
required=False,
|
|
516
|
-
help="The yaml file with the regression-tests definition",
|
|
517
|
-
)
|
|
518
|
-
@click.option(
|
|
519
|
-
"--wait",
|
|
520
|
-
is_flag=True,
|
|
521
|
-
default=False,
|
|
522
|
-
help="Wait for regression job to finish, showing a progress bar. Disabled by default.",
|
|
523
|
-
)
|
|
524
|
-
@click.option(
|
|
525
|
-
"--skip-regression-tests/--no-skip-regression-tests",
|
|
526
|
-
envvar="TB_SKIP_REGRESSION",
|
|
527
|
-
default=False,
|
|
528
|
-
help="Flag to skip execution of regression tests. This is handy for CI branches where regression might be flaky",
|
|
529
|
-
)
|
|
530
|
-
@click.option(
|
|
531
|
-
"--main",
|
|
532
|
-
is_flag=True,
|
|
533
|
-
default=False,
|
|
534
|
-
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.",
|
|
535
|
-
)
|
|
536
|
-
@click.pass_context
|
|
537
|
-
@coro
|
|
538
|
-
async def regression_tests(
|
|
539
|
-
ctx, filename: str, wait: bool, skip_regression_tests: Optional[bool] = False, main: Optional[bool] = False
|
|
540
|
-
):
|
|
541
|
-
"""Regression test commands for Branches"""
|
|
542
|
-
if skip_regression_tests:
|
|
543
|
-
click.echo(FeedbackManager.warning_regression_skipped())
|
|
544
|
-
return
|
|
545
|
-
|
|
546
|
-
if filename:
|
|
547
|
-
try:
|
|
548
|
-
with open(filename, "r") as file: # noqa: ASYNC230
|
|
549
|
-
regression_tests_commands = yaml.safe_load(file)
|
|
550
|
-
except Exception as exc:
|
|
551
|
-
raise CLIBranchException(FeedbackManager.error_regression_yaml_not_valid(filename=filename, error=exc))
|
|
552
|
-
if not isinstance(regression_tests_commands, List):
|
|
553
|
-
raise CLIBranchException(
|
|
554
|
-
FeedbackManager.error_regression_yaml_not_valid(filename=filename, error="not a list of pipes")
|
|
555
|
-
)
|
|
556
|
-
|
|
557
|
-
config = CLIConfig.get_project_config()
|
|
558
|
-
client = config.get_client()
|
|
559
|
-
|
|
560
|
-
current_main_workspace = await get_current_main_workspace(config)
|
|
561
|
-
assert isinstance(current_main_workspace, dict)
|
|
562
|
-
|
|
563
|
-
if current_main_workspace["id"] == config["id"]:
|
|
564
|
-
raise CLIException(FeedbackManager.error_not_allowed_in_main_branch())
|
|
565
|
-
return
|
|
566
|
-
try:
|
|
567
|
-
response = await client.branch_regression_tests_file(
|
|
568
|
-
config["id"], regression_tests_commands, run_in_main=main
|
|
569
|
-
)
|
|
570
|
-
if "job" not in response:
|
|
571
|
-
raise CLIBranchException(str(response))
|
|
572
|
-
job_id = response["job"]["job_id"]
|
|
573
|
-
job_url = response["job"]["job_url"]
|
|
574
|
-
click.echo(FeedbackManager.info_regression_tests_branch_job_url(url=job_url))
|
|
575
|
-
if wait:
|
|
576
|
-
await wait_job(client, job_id, job_url, "Regression tests")
|
|
577
|
-
await print_branch_regression_tests_summary(client, job_id, config["host"])
|
|
578
|
-
except Exception as e:
|
|
579
|
-
raise CLIBranchException(FeedbackManager.error_exception(error=str(e)))
|
|
580
|
-
else:
|
|
581
|
-
if not ctx.invoked_subcommand:
|
|
582
|
-
await _run_regression(type="coverage", wait=wait, run_in_main=main)
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
async def _run_regression(
|
|
586
|
-
type: str,
|
|
587
|
-
pipe_name: Optional[str] = None,
|
|
588
|
-
assert_result: Optional[bool] = True,
|
|
589
|
-
assert_result_no_error: Optional[bool] = True,
|
|
590
|
-
assert_result_rows_count: Optional[bool] = True,
|
|
591
|
-
assert_result_ignore_order: Optional[bool] = False,
|
|
592
|
-
assert_time_increase_percentage: Optional[int] = 25,
|
|
593
|
-
assert_bytes_read_increase_percentage: Optional[int] = 25,
|
|
594
|
-
assert_max_time: Optional[float] = 0.3,
|
|
595
|
-
failfast: Optional[bool] = False,
|
|
596
|
-
wait: Optional[bool] = False,
|
|
597
|
-
skip: Optional[bool] = False,
|
|
598
|
-
run_in_main: Optional[bool] = False,
|
|
599
|
-
**kwargs,
|
|
600
|
-
):
|
|
601
|
-
if skip:
|
|
602
|
-
click.echo(FeedbackManager.warning_regression_skipped())
|
|
603
|
-
return
|
|
604
|
-
|
|
605
|
-
config = CLIConfig.get_project_config()
|
|
606
|
-
client = config.get_client()
|
|
607
|
-
|
|
608
|
-
current_main_workspace = await get_current_main_workspace(config)
|
|
609
|
-
assert isinstance(current_main_workspace, dict)
|
|
610
|
-
|
|
611
|
-
if current_main_workspace["id"] == config["id"]:
|
|
612
|
-
raise CLIException(FeedbackManager.error_not_allowed_in_main_branch())
|
|
613
|
-
try:
|
|
614
|
-
response = await client.branch_regression_tests(
|
|
615
|
-
config["id"],
|
|
616
|
-
pipe_name,
|
|
617
|
-
type,
|
|
618
|
-
failfast=failfast,
|
|
619
|
-
assert_result=assert_result,
|
|
620
|
-
assert_result_no_error=assert_result_no_error,
|
|
621
|
-
assert_result_rows_count=assert_result_rows_count,
|
|
622
|
-
assert_result_ignore_order=assert_result_ignore_order,
|
|
623
|
-
assert_time_increase_percentage=assert_time_increase_percentage,
|
|
624
|
-
assert_bytes_read_increase_percentage=assert_bytes_read_increase_percentage,
|
|
625
|
-
assert_max_time=assert_max_time,
|
|
626
|
-
run_in_main=run_in_main,
|
|
627
|
-
**kwargs,
|
|
628
|
-
)
|
|
629
|
-
if "job" not in response:
|
|
630
|
-
raise CLIBranchException(str(response))
|
|
631
|
-
job_id = response["job"]["job_id"]
|
|
632
|
-
job_url = response["job"]["job_url"]
|
|
633
|
-
click.echo(FeedbackManager.info_regression_tests_branch_job_url(url=job_url))
|
|
634
|
-
if wait:
|
|
635
|
-
await wait_job(client, job_id, job_url, "Regression tests")
|
|
636
|
-
await print_branch_regression_tests_summary(client, job_id, config["host"])
|
|
637
|
-
except Exception as e:
|
|
638
|
-
raise CLIBranchException(FeedbackManager.error_exception(error=str(e)))
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
@regression_tests.command(
|
|
642
|
-
name="coverage",
|
|
643
|
-
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",
|
|
644
|
-
)
|
|
645
|
-
@click.argument("pipe_name", required=False)
|
|
646
|
-
@click.option(
|
|
647
|
-
"--assert-result/--no-assert-result",
|
|
648
|
-
is_flag=True,
|
|
649
|
-
default=True,
|
|
650
|
-
help="Whether to perform an assertion on the results returned by the endpoint. Enabled by default. Use --no-assert-result if you expect the endpoint output is different from current version",
|
|
651
|
-
)
|
|
652
|
-
@click.option(
|
|
653
|
-
"--assert-result-no-error/--no-assert-result-no-error",
|
|
654
|
-
is_flag=True,
|
|
655
|
-
default=True,
|
|
656
|
-
help="Whether to verify that the endpoint does not return errors. Enabled by default. Use --no-assert-result-no-error if you expect errors from the endpoint",
|
|
657
|
-
)
|
|
658
|
-
@click.option(
|
|
659
|
-
"--assert-result-rows-count/--no-assert-result-rows-count",
|
|
660
|
-
is_flag=True,
|
|
661
|
-
default=True,
|
|
662
|
-
help="Whether to verify that the correct number of elements are returned in the results. Enabled by default. Use --no-assert-result-rows-count if you expect the numbers of elements in the endpoint output is different from current version",
|
|
663
|
-
)
|
|
664
|
-
@click.option(
|
|
665
|
-
"--assert-result-ignore-order/--no-assert-result-ignore-order",
|
|
666
|
-
is_flag=True,
|
|
667
|
-
default=False,
|
|
668
|
-
help="Whether to ignore the order of the elements in the results. Disabled by default. Use --assert-result-ignore-order if you expect the endpoint output is returning same elements but in different order",
|
|
669
|
-
)
|
|
670
|
-
@click.option(
|
|
671
|
-
"--assert-time-increase-percentage",
|
|
672
|
-
type=int,
|
|
673
|
-
required=False,
|
|
674
|
-
default=25,
|
|
675
|
-
help="Allowed percentage increase in endpoint response time. Default value is 25%. Use -1 to disable assert.",
|
|
676
|
-
)
|
|
677
|
-
@click.option(
|
|
678
|
-
"--assert-bytes-read-increase-percentage",
|
|
679
|
-
type=int,
|
|
680
|
-
required=False,
|
|
681
|
-
default=25,
|
|
682
|
-
help="Allowed percentage increase in the amount of bytes read by the endpoint. Default value is 25%. Use -1 to disable assert",
|
|
683
|
-
)
|
|
684
|
-
@click.option(
|
|
685
|
-
"--assert-max-time",
|
|
686
|
-
type=float,
|
|
687
|
-
required=False,
|
|
688
|
-
default=0.3,
|
|
689
|
-
help="Max time allowed for the endpoint response time. If the response time is lower than this value then the --assert-time-increase-percentage is not taken into account.",
|
|
690
|
-
)
|
|
691
|
-
@click.option(
|
|
692
|
-
"-ff", "--failfast", is_flag=True, default=False, help="When set, the checker will exit as soon one test fails"
|
|
693
|
-
)
|
|
694
|
-
@click.option(
|
|
695
|
-
"--wait",
|
|
696
|
-
is_flag=True,
|
|
697
|
-
default=False,
|
|
698
|
-
help="Waits for regression job to finish, showing a progress bar. Disabled by default.",
|
|
699
|
-
)
|
|
700
|
-
@click.option(
|
|
701
|
-
"--skip-regression-tests/--no-skip-regression-tests",
|
|
702
|
-
envvar="TB_SKIP_REGRESSION",
|
|
703
|
-
default=False,
|
|
704
|
-
help="Flag to skip execution of regression tests. This is handy for CI branches where regression might be flaky",
|
|
705
|
-
)
|
|
706
|
-
@click.option(
|
|
707
|
-
"--main",
|
|
708
|
-
is_flag=True,
|
|
709
|
-
default=False,
|
|
710
|
-
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.",
|
|
711
|
-
)
|
|
712
|
-
@coro
|
|
713
|
-
async def coverage(
|
|
714
|
-
pipe_name: str,
|
|
715
|
-
assert_result: bool,
|
|
716
|
-
assert_result_no_error: bool,
|
|
717
|
-
assert_result_rows_count: bool,
|
|
718
|
-
assert_result_ignore_order: bool,
|
|
719
|
-
assert_time_increase_percentage: int,
|
|
720
|
-
assert_bytes_read_increase_percentage: int,
|
|
721
|
-
assert_max_time: float,
|
|
722
|
-
failfast: bool,
|
|
723
|
-
wait: bool,
|
|
724
|
-
skip_regression_tests: Optional[bool] = False,
|
|
725
|
-
main: Optional[bool] = False,
|
|
726
|
-
):
|
|
727
|
-
await _run_regression(
|
|
728
|
-
"coverage",
|
|
729
|
-
pipe_name,
|
|
730
|
-
assert_result,
|
|
731
|
-
assert_result_no_error,
|
|
732
|
-
assert_result_rows_count,
|
|
733
|
-
assert_result_ignore_order,
|
|
734
|
-
assert_time_increase_percentage,
|
|
735
|
-
assert_bytes_read_increase_percentage,
|
|
736
|
-
assert_max_time,
|
|
737
|
-
failfast,
|
|
738
|
-
wait,
|
|
739
|
-
skip_regression_tests,
|
|
740
|
-
run_in_main=main,
|
|
741
|
-
)
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
@regression_tests.command(
|
|
745
|
-
name="last",
|
|
746
|
-
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",
|
|
747
|
-
)
|
|
748
|
-
@click.argument("pipe_name", required=False)
|
|
749
|
-
@click.option(
|
|
750
|
-
"-l",
|
|
751
|
-
"--limit",
|
|
752
|
-
type=click.IntRange(1, 100),
|
|
753
|
-
default=10,
|
|
754
|
-
required=False,
|
|
755
|
-
help="Number of requests to validate. Default is 10",
|
|
756
|
-
)
|
|
757
|
-
@click.option(
|
|
758
|
-
"--assert-result/--no-assert-result",
|
|
759
|
-
is_flag=True,
|
|
760
|
-
default=True,
|
|
761
|
-
help="Whether to perform an assertion on the results returned by the endpoint. Enabled by default. Use --no-assert-result if you expect the endpoint output is different from current version",
|
|
762
|
-
)
|
|
763
|
-
@click.option(
|
|
764
|
-
"--assert-result-no-error/--no-assert-result-no-error",
|
|
765
|
-
is_flag=True,
|
|
766
|
-
default=True,
|
|
767
|
-
help="Whether to verify that the endpoint does not return errors. Enabled by default. Use --no-assert-result-no-error if you expect errors from the endpoint",
|
|
768
|
-
)
|
|
769
|
-
@click.option(
|
|
770
|
-
"--assert-result-rows-count/--no-assert-result-rows-count",
|
|
771
|
-
is_flag=True,
|
|
772
|
-
default=True,
|
|
773
|
-
help="Whether to verify that the correct number of elements are returned in the results. Enabled by default. Use --no-assert-result-rows-count if you expect the numbers of elements in the endpoint output is different from current version",
|
|
774
|
-
)
|
|
775
|
-
@click.option(
|
|
776
|
-
"--assert-result-ignore-order/--no-assert-result-ignore-order",
|
|
777
|
-
is_flag=True,
|
|
778
|
-
default=False,
|
|
779
|
-
help="Whether to ignore the order of the elements in the results. Disabled by default. Use --assert-result-ignore-order if you expect the endpoint output is returning same elements but in different order",
|
|
780
|
-
)
|
|
781
|
-
@click.option(
|
|
782
|
-
"--assert-time-increase-percentage",
|
|
783
|
-
type=int,
|
|
784
|
-
required=False,
|
|
785
|
-
default=25,
|
|
786
|
-
help="Allowed percentage increase in endpoint response time. Default value is 25%. Use -1 to disable assert.",
|
|
787
|
-
)
|
|
788
|
-
@click.option(
|
|
789
|
-
"--assert-bytes-read-increase-percentage",
|
|
790
|
-
type=int,
|
|
791
|
-
required=False,
|
|
792
|
-
default=25,
|
|
793
|
-
help="Allowed percentage increase in the amount of bytes read by the endpoint. Default value is 25%. Use -1 to disable assert",
|
|
794
|
-
)
|
|
795
|
-
@click.option(
|
|
796
|
-
"--assert-max-time",
|
|
797
|
-
type=float,
|
|
798
|
-
required=False,
|
|
799
|
-
default=0.3,
|
|
800
|
-
help="Max time allowed for the endpoint response time. If the response time is lower than this value then the --assert-time-increase-percentage is not taken into account.",
|
|
801
|
-
)
|
|
802
|
-
@click.option(
|
|
803
|
-
"-ff", "--failfast", is_flag=True, default=False, help="When set, the checker will exit as soon one test fails"
|
|
804
|
-
)
|
|
805
|
-
@click.option(
|
|
806
|
-
"--wait",
|
|
807
|
-
is_flag=True,
|
|
808
|
-
default=False,
|
|
809
|
-
help="Waits for regression job to finish, showing a progress bar. Disabled by default.",
|
|
810
|
-
)
|
|
811
|
-
@click.option(
|
|
812
|
-
"--skip-regression-tests/--no-skip-regression-tests",
|
|
813
|
-
envvar="TB_SKIP_REGRESSION",
|
|
814
|
-
default=False,
|
|
815
|
-
help="Flag to skip execution of regression tests. This is handy for CI branches where regression might be flaky",
|
|
816
|
-
)
|
|
817
|
-
@coro
|
|
818
|
-
async def last(
|
|
819
|
-
pipe_name: str,
|
|
820
|
-
limit: int,
|
|
821
|
-
assert_result: bool,
|
|
822
|
-
assert_result_no_error: bool,
|
|
823
|
-
assert_result_rows_count: bool,
|
|
824
|
-
assert_result_ignore_order: bool,
|
|
825
|
-
assert_time_increase_percentage: int,
|
|
826
|
-
assert_bytes_read_increase_percentage: int,
|
|
827
|
-
assert_max_time: float,
|
|
828
|
-
failfast: bool,
|
|
829
|
-
wait: bool,
|
|
830
|
-
skip_regression_tests: Optional[bool] = False,
|
|
831
|
-
):
|
|
832
|
-
await _run_regression(
|
|
833
|
-
"last",
|
|
834
|
-
pipe_name,
|
|
835
|
-
assert_result,
|
|
836
|
-
assert_result_no_error,
|
|
837
|
-
assert_result_rows_count,
|
|
838
|
-
assert_result_ignore_order,
|
|
839
|
-
assert_time_increase_percentage,
|
|
840
|
-
assert_bytes_read_increase_percentage,
|
|
841
|
-
assert_max_time,
|
|
842
|
-
failfast,
|
|
843
|
-
wait,
|
|
844
|
-
skip_regression_tests,
|
|
845
|
-
limit=limit,
|
|
846
|
-
)
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
@regression_tests.command(
|
|
850
|
-
name="manual",
|
|
851
|
-
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",
|
|
852
|
-
context_settings=dict(allow_extra_args=True, ignore_unknown_options=True),
|
|
853
|
-
)
|
|
854
|
-
@click.argument("pipe_name", required=False)
|
|
855
|
-
@click.option(
|
|
856
|
-
"--assert-result/--no-assert-result",
|
|
857
|
-
is_flag=True,
|
|
858
|
-
default=True,
|
|
859
|
-
help="Whether to perform an assertion on the results returned by the endpoint. Enabled by default. Use --no-assert-result if you expect the endpoint output is different from current version",
|
|
860
|
-
)
|
|
861
|
-
@click.option(
|
|
862
|
-
"--assert-result-no-error/--no-assert-result-no-error",
|
|
863
|
-
is_flag=True,
|
|
864
|
-
default=True,
|
|
865
|
-
help="Whether to verify that the endpoint does not return errors. Enabled by default. Use --no-assert-result-no-error if you expect errors from the endpoint",
|
|
866
|
-
)
|
|
867
|
-
@click.option(
|
|
868
|
-
"--assert-result-rows-count/--no-assert-result-rows-count",
|
|
869
|
-
is_flag=True,
|
|
870
|
-
default=True,
|
|
871
|
-
help="Whether to verify that the correct number of elements are returned in the results. Enabled by default. Use --no-assert-result-rows-count if you expect the numbers of elements in the endpoint output is different from current version",
|
|
872
|
-
)
|
|
873
|
-
@click.option(
|
|
874
|
-
"--assert-result-ignore-order/--no-assert-result-ignore-order",
|
|
875
|
-
is_flag=True,
|
|
876
|
-
default=False,
|
|
877
|
-
help="Whether to ignore the order of the elements in the results. Disabled by default. Use --assert-result-ignore-order if you expect the endpoint output is returning same elements but in different order",
|
|
878
|
-
)
|
|
879
|
-
@click.option(
|
|
880
|
-
"--assert-time-increase-percentage",
|
|
881
|
-
type=int,
|
|
882
|
-
required=False,
|
|
883
|
-
default=25,
|
|
884
|
-
help="Allowed percentage increase in endpoint response time. Default value is 25%. Use -1 to disable assert.",
|
|
885
|
-
)
|
|
886
|
-
@click.option(
|
|
887
|
-
"--assert-bytes-read-increase-percentage",
|
|
888
|
-
type=int,
|
|
889
|
-
required=False,
|
|
890
|
-
default=25,
|
|
891
|
-
help="Allowed percentage increase in the amount of bytes read by the endpoint. Default value is 25%. Use -1 to disable assert",
|
|
892
|
-
)
|
|
893
|
-
@click.option(
|
|
894
|
-
"--assert-max-time",
|
|
895
|
-
type=float,
|
|
896
|
-
required=False,
|
|
897
|
-
default=0.3,
|
|
898
|
-
help="Max time allowed for the endpoint response time. If the response time is lower than this value then the --assert-time-increase-percentage is not taken into account.",
|
|
899
|
-
)
|
|
900
|
-
@click.option(
|
|
901
|
-
"-ff", "--failfast", is_flag=True, default=False, help="When set, the checker will exit as soon one test fails"
|
|
902
|
-
)
|
|
903
|
-
@click.option(
|
|
904
|
-
"--wait",
|
|
905
|
-
is_flag=True,
|
|
906
|
-
default=False,
|
|
907
|
-
help="Waits for regression job to finish, showing a progress bar. Disabled by default.",
|
|
908
|
-
)
|
|
909
|
-
@click.option(
|
|
910
|
-
"--skip-regression-tests/--no-skip-regression-tests",
|
|
911
|
-
envvar="TB_SKIP_REGRESSION",
|
|
912
|
-
default=False,
|
|
913
|
-
help="Flag to skip execution of regression tests. This is handy for CI branches where regression might be flaky",
|
|
914
|
-
)
|
|
915
|
-
@click.pass_context
|
|
916
|
-
@coro
|
|
917
|
-
async def manual(
|
|
918
|
-
ctx: click.Context,
|
|
919
|
-
pipe_name: str,
|
|
920
|
-
assert_result: bool,
|
|
921
|
-
assert_result_no_error: bool,
|
|
922
|
-
assert_result_rows_count: bool,
|
|
923
|
-
assert_result_ignore_order: bool,
|
|
924
|
-
assert_time_increase_percentage: int,
|
|
925
|
-
assert_bytes_read_increase_percentage: int,
|
|
926
|
-
assert_max_time: float,
|
|
927
|
-
failfast: bool,
|
|
928
|
-
wait: bool,
|
|
929
|
-
skip_regression_tests: Optional[bool] = False,
|
|
930
|
-
):
|
|
931
|
-
params = [{ctx.args[i][2:]: ctx.args[i + 1] for i in range(0, len(ctx.args), 2)}]
|
|
932
|
-
await _run_regression(
|
|
933
|
-
"manual",
|
|
934
|
-
pipe_name,
|
|
935
|
-
assert_result,
|
|
936
|
-
assert_result_no_error,
|
|
937
|
-
assert_result_rows_count,
|
|
938
|
-
assert_result_ignore_order,
|
|
939
|
-
assert_time_increase_percentage,
|
|
940
|
-
assert_bytes_read_increase_percentage,
|
|
941
|
-
assert_max_time,
|
|
942
|
-
failfast,
|
|
943
|
-
wait,
|
|
944
|
-
skip_regression_tests,
|
|
945
|
-
params=params,
|
|
946
|
-
)
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
@branch.group()
|
|
950
|
-
def datasource() -> None:
|
|
951
|
-
"""Branch data source commands."""
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
@datasource.command(name="copy")
|
|
955
|
-
@click.argument("datasource_name")
|
|
956
|
-
@click.option(
|
|
957
|
-
"--sql",
|
|
958
|
-
default=None,
|
|
959
|
-
help="Freeform SQL query to select what is copied from Main into the Branch Data Source",
|
|
960
|
-
required=False,
|
|
961
|
-
)
|
|
962
|
-
@click.option(
|
|
963
|
-
"--sql-from-main",
|
|
964
|
-
is_flag=True,
|
|
965
|
-
default=False,
|
|
966
|
-
help="SQL query selecting * from the same Data Source in Main",
|
|
967
|
-
required=False,
|
|
968
|
-
)
|
|
969
|
-
@click.option("--wait", is_flag=True, default=False, help="Wait for copy job to finish, disabled by default")
|
|
970
|
-
@coro
|
|
971
|
-
async def datasource_copy_from_main(datasource_name: str, sql: str, sql_from_main: bool, wait: bool) -> None:
|
|
972
|
-
"""Copy data source from Main."""
|
|
973
|
-
|
|
974
|
-
if sql and sql_from_main:
|
|
975
|
-
raise CLIException(FeedbackManager.error_exception(error="Use --sql or --sql-from-main but not both"))
|
|
976
|
-
|
|
977
|
-
if not sql and not sql_from_main:
|
|
978
|
-
raise CLIException(FeedbackManager.error_exception(error="Use --sql or --sql-from-main"))
|
|
979
|
-
|
|
980
|
-
config = CLIConfig.get_project_config()
|
|
981
|
-
|
|
982
|
-
current_main_workspace = await get_current_main_workspace(config)
|
|
983
|
-
assert isinstance(current_main_workspace, dict)
|
|
984
|
-
|
|
985
|
-
if current_main_workspace["id"] == config["id"] and sql_from_main:
|
|
986
|
-
raise CLIException(FeedbackManager.error_not_allowed_in_main_branch())
|
|
987
|
-
|
|
988
|
-
client = config.get_client()
|
|
989
|
-
|
|
990
|
-
response = await client.datasource_query_copy(
|
|
991
|
-
datasource_name, sql if sql else f"SELECT * FROM main.{datasource_name}"
|
|
992
|
-
)
|
|
993
|
-
if "job" not in response:
|
|
994
|
-
raise CLIBranchException(response)
|
|
995
|
-
job_id = response["job"]["job_id"]
|
|
996
|
-
job_url = response["job"]["job_url"]
|
|
997
|
-
if sql:
|
|
998
|
-
click.echo(FeedbackManager.info_copy_with_sql_job_url(sql=sql, datasource_name=datasource_name, url=job_url))
|
|
999
|
-
else:
|
|
1000
|
-
click.echo(FeedbackManager.info_copy_from_main_job_url(datasource_name=datasource_name, url=job_url))
|
|
1001
|
-
if wait:
|
|
1002
|
-
base_msg = "Copy from Main Workspace" if sql_from_main else f"Copy from {sql}"
|
|
1003
|
-
await wait_job(client, job_id, job_url, f"{base_msg} to {datasource_name}")
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
async def warn_if_in_live(semver: str) -> None:
|
|
1007
|
-
if not semver:
|
|
1008
|
-
return
|
|
1009
|
-
|
|
1010
|
-
config = CLIConfig.get_project_config()
|
|
1011
|
-
current_workspace = await get_current_workspace(config)
|
|
1012
|
-
assert isinstance(current_workspace, dict)
|
|
1013
|
-
|
|
1014
|
-
is_branch = current_workspace.get("is_branch", False)
|
|
1015
|
-
release = current_workspace.get("release", {})
|
|
1016
|
-
live_semver = release and release.get("semver", None)
|
|
1017
|
-
|
|
1018
|
-
if is_branch:
|
|
1019
|
-
return
|
|
1020
|
-
|
|
1021
|
-
if live_semver and live_semver == semver:
|
|
1022
|
-
click.echo(FeedbackManager.warning_release_risky_operation_in_live())
|
|
1023
|
-
click.echo("")
|