Skip to content

Commit b70c703

Browse files
committed
feature: add/remove/edit existing commands
1 parent 322e80d commit b70c703

File tree

8 files changed

+210
-221
lines changed

8 files changed

+210
-221
lines changed

smart_tests/__main__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import os
55
from glob import glob
66
from os.path import basename, dirname, join
7-
from typing import Annotated, Optional
7+
from typing import Annotated
88

99
import typer
1010

@@ -89,7 +89,7 @@ def main(
8989
log_level: Annotated[str, typer.Option(
9090
help="Set logger's log level (CRITICAL, ERROR, WARNING, AUDIT, INFO, DEBUG)."
9191
)] = logger.LOG_LEVEL_DEFAULT_STR,
92-
plugin_dir: Annotated[Optional[str], typer.Option(
92+
plugin_dir: Annotated[str | None, typer.Option(
9393
"--plugin-dir", "--plugins",
9494
help="Directory to load plugins from"
9595
)] = None,
@@ -105,7 +105,7 @@ def main(
105105
"like CERTIFICATE_VERIFY_FAILED, at the expense of vulnerability against "
106106
"a possible man-in-the-middle attack. Use it as an escape hatch, but with caution."
107107
)] = False,
108-
version: Annotated[Optional[bool], typer.Option(
108+
version: Annotated[bool | None, typer.Option(
109109
"--version", help="Show version and exit", callback=version_callback, is_eager=True
110110
)] = None,
111111
):

smart_tests/commands/record/build.py

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import typer
77
from tabulate import tabulate
88

9-
from smart_tests.utils.link import CIRCLECI_KEY, GITHUB_ACTIONS_KEY, JENKINS_URL_KEY, LinkKind, capture_link
9+
from smart_tests.utils.link import CIRCLECI_KEY, GITHUB_ACTIONS_KEY, JENKINS_URL_KEY, capture_link
1010
from smart_tests.utils.tracking import Tracking, TrackingClient
1111

1212
from ...utils import subprocess
@@ -34,6 +34,15 @@ def build(
3434
help="build name",
3535
metavar="BUILD_NAME"
3636
)],
37+
branch: Annotated[str, typer.Option(
38+
"--branch",
39+
help="Branch name. A branch is a set of test sessions grouped and this option value will be used for a lineage name."
40+
)],
41+
repositories: Annotated[List[str], typer.Option(
42+
"--repository",
43+
help="Set repository name and branch name when you use --no-commit-collection option. "
44+
"Please use the same repository name with a commit option"
45+
)] = [],
3746
source: Annotated[List[str], typer.Option(
3847
help="path to local Git workspace, optionally prefixed by a label. "
3948
"like --source path/to/ws or --source main=path/to/ws",
@@ -51,23 +60,10 @@ def build(
5160
"possible. The commit data must be collected with a separate fully-cloned "
5261
"repository."
5362
)] = False,
54-
scrub_pii: Annotated[bool, typer.Option(
55-
help="Scrub emails and names",
56-
hidden=True
57-
)] = False,
5863
commits: Annotated[List[str], typer.Option(
5964
"--commit",
6065
help="set repository name and commit hash when you use --no-commit-collection option"
6166
)] = [],
62-
links: Annotated[List[str], typer.Option(
63-
"--link",
64-
help="Set external link of title and url"
65-
)] = [],
66-
branches: Annotated[List[str], typer.Option(
67-
"--branch",
68-
help="Set repository name and branch name when you use --no-commit-collection option. "
69-
"Please use the same repository name with a commit option"
70-
)] = [],
7167
lineage: Annotated[Optional[str], typer.Option(
7268
help="hidden option to directly specify the lineage name without relying on branches",
7369
hidden=True
@@ -79,9 +75,8 @@ def build(
7975
):
8076
app = ctx.obj
8177

82-
# Parse key-value pairs for commits and links
78+
# Parse key-value pairs for commits
8379
parsed_commits = [validate_key_value(c) for c in commits]
84-
parsed_links = [validate_key_value(link) for link in links]
8580

8681
# Parse timestamp if provided
8782
parsed_timestamp = None
@@ -229,22 +224,23 @@ def list_submodules(workspaces: List[Workspace]) -> List[Workspace]:
229224
def compute_hash_and_branch(ws: List[Workspace]):
230225
ws_by_name = {w.name: w for w in ws}
231226

227+
# Process repository options to create branch name mappings
232228
branch_name_map = dict()
233-
if len(branches) == 1 and len(ws) == 1 and not ('=' in branches[0]):
234-
# if there's only one repo and the short form "--branch NAME" is used, then we assign that to the first repo
235-
branch_name_map[ws[0].name] = branches[0]
229+
if len(repositories) == 1 and len(ws) == 1 and not ('=' in repositories[0]):
230+
# if there's only one repo and the short form "--repository NAME" is used, then we assign that to the first repo
231+
branch_name_map[ws[0].name] = repositories[0]
236232
else:
237-
for b in branches:
238-
kv = b.split('=')
233+
for r in repositories:
234+
kv = r.split('=')
239235
if len(kv) != 2:
240236
typer.secho(
241-
"Expected --branch REPO=BRANCHNAME but got {}".format(kv),
237+
"Expected --repository REPO=BRANCHNAME but got {}".format(kv),
242238
fg=typer.colors.YELLOW, err=True)
243239
raise typer.Exit(1)
244240

245241
if not ws_by_name.get(kv[0]):
246242
typer.secho(
247-
"Invalid repository name {} in a --branch option. ".format(kv[0]),
243+
"Invalid repository name {} in a --repository option. ".format(kv[0]),
248244
fg=typer.colors.YELLOW, err=True)
249245
# TODO: is there any reason this is not an error? for now erring on caution
250246
# sys.exit(1)
@@ -289,20 +285,14 @@ def send(ws: List[Workspace]) -> Optional[str]:
289285
# figure out all the CI links to capture
290286
def compute_links():
291287
_links = capture_link(os.environ)
292-
for k, v in parsed_links:
293-
_links.append({
294-
"title": k,
295-
"url": v,
296-
"kind": LinkKind.CUSTOM_LINK.name,
297-
})
298288
return _links
299289

300290
tracking_client = TrackingClient(Tracking.Command.RECORD_BUILD, app=app)
301291
client = LaunchableClient(app=app, tracking_client=tracking_client)
302292
try:
303293
payload = {
304294
"buildNumber": build_name,
305-
"lineage": lineage or ws[0].branch,
295+
"lineage": lineage or branch,
306296
"commitHashes": [{
307297
'repositoryName': w.name,
308298
'commitHash': w.commit_hash,

smart_tests/commands/record/session.py

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import re
33
import sys
44
from http import HTTPStatus
5-
from typing import Annotated, List, Optional
5+
from typing import Annotated, List
66

77
import typer
88

@@ -36,6 +36,11 @@ def session(
3636
"--session",
3737
help="test session name"
3838
)],
39+
test_suite: Annotated[str, typer.Option(
40+
"--test-suite",
41+
help="Set test suite name. A test suite is a collection of test sessions. Setting a test suite allows you to "
42+
"manage data over test sessions and lineages."
43+
)],
3944
print_session: bool = True,
4045
flavor: Annotated[List[str], typer.Option(
4146
"--flavor",
@@ -54,16 +59,7 @@ def session(
5459
"--no-build",
5560
help="If you want to only send test reports, please use this option"
5661
)] = False,
57-
lineage: Annotated[Optional[str], typer.Option(
58-
help="Set lineage name. A lineage is a set of test sessions grouped and this option value will be used for a "
59-
"lineage name."
60-
)] = None,
61-
test_suite: Annotated[Optional[str], typer.Option(
62-
"--test-suite",
63-
help="Set test suite name. A test suite is a collection of test sessions. Setting a test suite allows you to "
64-
"manage data over test sessions and lineages."
65-
)] = None,
66-
timestamp: Annotated[Optional[str], typer.Option(
62+
timestamp: Annotated[str | None, typer.Option(
6763
help="Used to overwrite the session time when importing historical data. Note: Format must be "
6864
"`YYYY-MM-DDThh:mm:ssTZD` or `YYYY-MM-DDThh:mm:ss` (local timezone applied)"
6965
)] = None,
@@ -155,7 +151,6 @@ def session(
155151
"flavors": flavor_dict,
156152
"isObservation": is_observation,
157153
"noBuild": is_no_build,
158-
"lineage": lineage,
159154
"testSuite": test_suite,
160155
"timestamp": parsed_timestamp.isoformat() if parsed_timestamp else None,
161156
}

smart_tests/commands/record/tests.py

Lines changed: 9 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
from ...utils.launchable_client import LaunchableClient
2424
from ...utils.logger import Logger
2525
from ...utils.no_build import NO_BUILD_BUILD_NAME, NO_BUILD_TEST_SESSION_ID
26-
from ...utils.typer_types import validate_datetime_with_tz
2726
from ..helper import get_session_id, parse_session
2827
from .case_event import CaseEvent, CaseEventType
2928

@@ -56,19 +55,18 @@ def tests_main(
5655
"--session",
5756
help="test session name"
5857
)],
59-
base_path: Annotated[Optional[Path], typer.Option(
58+
build_name: Annotated[str | None, typer.Option(
59+
"--build",
60+
help="build name"
61+
)] = None,
62+
base_path: Annotated[Path | None, typer.Option(
6063
"--base",
6164
help="(Advanced) base directory to make test names portable",
6265
exists=True,
6366
file_okay=False,
6467
dir_okay=True,
6568
resolve_path=True
6669
)] = None,
67-
build_name: Annotated[Optional[str], typer.Option(
68-
"--build",
69-
help="build name",
70-
hidden=True
71-
)] = None,
7270
post_chunk: Annotated[int, typer.Option(
7371
"--post-chunk",
7472
help="Post chunk"
@@ -92,33 +90,18 @@ def tests_main(
9290
"Use with --dry-run",
9391
hidden=True
9492
)] = False,
95-
group: Annotated[Optional[str], typer.Option(
93+
group: Annotated[str | None, typer.Option(
9694
help="Grouping name for test results"
9795
)] = "",
9896
is_allow_test_before_build: Annotated[bool, typer.Option(
9997
"--allow-test-before-build",
10098
help="",
10199
hidden=True
102100
)] = False,
103-
links: Annotated[List[str], typer.Option(
104-
"--link",
105-
help="Set external link of title and url"
106-
)] = [],
107101
is_no_build: Annotated[bool, typer.Option(
108102
"--no-build",
109103
help="If you want to only send test reports, please use this option"
110104
)] = False,
111-
lineage: Annotated[Optional[str], typer.Option(
112-
help="Set lineage name. This option value will be passed to the record session command if a session isn't created yet."
113-
)] = None,
114-
test_suite: Annotated[Optional[str], typer.Option(
115-
"--test-suite",
116-
help="Set test suite name. This option value will be passed to the record session command if a session isn't created yet."
117-
)] = "",
118-
timestamp: Annotated[Optional[str], typer.Option(
119-
help="Used to overwrite the test executed times when importing historical data. Note: Format must be "
120-
"`YYYY-MM-DDThh:mm:ssTZD` or `YYYY-MM-DDThh:mm:ss` (local timezone applied)"
121-
)] = None,
122105
):
123106
logger = Logger()
124107

@@ -133,7 +116,7 @@ def tests_main(
133116
# In NestedCommand, the test runner name should be available from the command structure
134117
# For now, temporarily extract from command chain
135118
command_chain = []
136-
current_ctx: Optional[typer.Context] = ctx
119+
current_ctx: typer.Context | None = ctx
137120
while current_ctx:
138121
if current_ctx.info_name:
139122
command_chain.append(current_ctx.info_name)
@@ -157,28 +140,12 @@ def tests_main(
157140
else:
158141
raise typer.BadParameter(f"Expected a key-value pair formatted as --option key=value, but got '{kv}'")
159142

160-
links_tuples = []
161-
for kv in links:
162-
if '=' in kv:
163-
parts = kv.split('=', 1)
164-
links_tuples.append((parts[0].strip(), parts[1].strip()))
165-
elif ':' in kv:
166-
parts = kv.split(':', 1)
167-
links_tuples.append((parts[0].strip(), parts[1].strip()))
168-
else:
169-
raise typer.BadParameter(f"Expected a key-value pair formatted as --option key=value, but got '{kv}'")
170-
171143
# Validate group if provided and ensure it's never None
172144
if group is None:
173145
group = ""
174146
elif group:
175147
group = _validate_group(group)
176148

177-
# Validate and convert timestamp if provided
178-
parsed_timestamp = None
179-
if timestamp:
180-
parsed_timestamp = validate_datetime_with_tz(timestamp)
181-
182149
app_instance = ctx.obj
183150
tracking_client = TrackingClient(Tracking.Command.RECORD_TESTS, app=app_instance)
184151
client = LaunchableClient(test_runner=test_runner, app=app_instance, tracking_client=tracking_client)
@@ -359,7 +326,6 @@ def report(self, junit_report_file: str):
359326
if (
360327
not self.is_allow_test_before_build # nlqa: W503
361328
and not self.is_no_build # noqa: W503
362-
and parsed_timestamp is None # noqa: W503
363329
and self.check_timestamp # noqa: W503
364330
and ctime.timestamp() < record_start_at.timestamp() # noqa: W503
365331
):
@@ -394,9 +360,7 @@ def testcases(reports: List[str]) -> Generator[CaseEventType, None, None]:
394360
if len(tc.get('testPath', [])) == 0:
395361
continue
396362

397-
# Set specific time for importing historical data
398-
if parsed_timestamp is not None:
399-
tc["createdAt"] = parsed_timestamp.isoformat()
363+
# Timestamp option has been removed
400364

401365
yield tc
402366

@@ -516,7 +480,7 @@ def recorded_result() -> Tuple[int, int, int, float]:
516480
cases=chunk,
517481
test_runner=test_runner,
518482
group=group,
519-
test_suite_name=test_suite if test_suite else "",
483+
test_suite_name="", # test_suite option was removed
520484
flavors=dict(flavor_tuples),
521485
)
522486

smart_tests/commands/subset.py

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ def subset(
3636
help="test session name",
3737
metavar="SESSION_NAME"
3838
)],
39+
build: Annotated[str | None, typer.Option(
40+
"--build",
41+
help="build name",
42+
metavar="BUILD_NAME"
43+
)] = None,
3944
target: Annotated[str | None, typer.Option(
4045
help="subsetting target from 0% to 100%"
4146
)] = None,
@@ -52,11 +57,6 @@ def subset(
5257
help="(Advanced) base directory to make test names portable",
5358
metavar="DIR"
5459
)] = None,
55-
build: Annotated[str | None, typer.Option(
56-
help="build name",
57-
metavar="BUILD_NAME",
58-
hidden=True
59-
)] = None,
6060
rest: Annotated[str | None, typer.Option(
6161
help="Output the subset remainder to a file, e.g. `--rest=remainder.txt`"
6262
)] = None,
@@ -100,17 +100,10 @@ def subset(
100100
help="Ignore flaky tests above the value set by this option. You can confirm flaky scores in WebApp",
101101
min=0.0, max=1.0
102102
)] = None,
103-
link: Annotated[list[str], typer.Option(
104-
help="Set external link of title and url"
105-
)] = [],
106103
no_build: Annotated[bool, typer.Option(
107104
"--no-build",
108105
help="If you want to only send test reports, please use this option"
109106
)] = False,
110-
lineage: Annotated[str | None, typer.Option(
111-
help="Set lineage name. This option value will be passed to the record session command if a session isn't created yet.",
112-
metavar="LINEAGE"
113-
)] = None,
114107
prioritize_tests_failed_within_hours: Annotated[int | None, typer.Option(
115108
help="Prioritize tests that failed within the specified hours; maximum 720 hours (= 24 hours * 30 days)",
116109
min=0, max=24 * 30
@@ -120,11 +113,6 @@ def subset(
120113
help="Prioritize tests based on test mapping file",
121114
mode="r"
122115
)] = None,
123-
test_suite: Annotated[str | None, typer.Option(
124-
help="Set test suite name. This option value will be passed to the record session command if a session "
125-
"isn't created yet.",
126-
metavar="TEST_SUITE"
127-
)] = None,
128116
):
129117
app = ctx.obj
130118

0 commit comments

Comments
 (0)