Skip to content

Commit 8b2f44f

Browse files
committed
Add unified download command for dataset and SAST requirements
1 parent ecccd7e commit 8b2f44f

4 files changed

Lines changed: 62 additions & 52 deletions

File tree

codesectools/cli.py

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@
99
from typing import Optional
1010

1111
import typer
12+
from click import Choice
1213
from rich import print
1314
from rich.table import Table
1415
from typing_extensions import Annotated
1516

1617
from codesectools.datasets import DATASETS_ALL
17-
from codesectools.datasets.core.cli import cli as dataset_cli
18+
from codesectools.datasets.core.dataset import Dataset
1819
from codesectools.sasts import SASTS_ALL
20+
from codesectools.sasts.core.sast.requirements import DownloadableRequirement
1921

2022
cli = typer.Typer(name="cstools", no_args_is_help=True)
2123

@@ -63,19 +65,21 @@ def status(
6365
for sast_name, sast_data in SASTS_ALL.items():
6466
if sast_data["status"] == "full":
6567
table.add_row(
66-
sast_name, "Full ✅", f"See subcommand [b]{sast_name.lower()}[/b]"
68+
sast_name,
69+
"Full ✅",
70+
"[b]Analysis[/b] and [b]result parsing[/b] are available",
6771
)
6872
elif sast_data["status"] == "partial":
6973
table.add_row(
7074
sast_name,
7175
"Partial ⚠️",
72-
f"See subcommand [b]{sast_name.lower()}[/b]\nMissing: [b]{sast_data['missing']}[/b]",
76+
f"Only [b]result parsing[/b] is available\nMissing: [red]{sast_data['missing']}[/red]",
7377
)
7478
else:
7579
table.add_row(
7680
sast_name,
7781
"None ❌",
78-
f"Missing: [b]{sast_data['missing']}[/b]",
82+
f"[b]Nothing[/b] is available\nMissing: [red]{sast_data['missing']}[/red]",
7983
)
8084
print(table)
8185

@@ -98,12 +102,62 @@ def status(
98102
dataset_name,
99103
dataset.__bases__[0].__name__,
100104
"❌",
101-
f"[red]cstools dataset download {dataset_name}[/red]",
105+
f"Download with: [i red]cstools download {dataset_name}[/i red]",
102106
)
103107
print(table)
104108

105109

106-
cli.add_typer(dataset_cli)
110+
def get_downloadable() -> dict[str, DownloadableRequirement | Dataset]:
111+
"""Identify and collect all missing downloadable resources.
112+
113+
Collects unfulfilled `DownloadableRequirement` instances from all SASTs
114+
and un-cached `Dataset` instances.
115+
116+
Returns:
117+
A dictionary mapping the resource name to its downloadable object.
118+
119+
"""
120+
downloadable = {}
121+
122+
for _, sast_data in SASTS_ALL.items():
123+
sast = sast_data["sast"]
124+
for req in sast.requirements.all:
125+
if isinstance(req, DownloadableRequirement):
126+
if not req.is_fulfilled():
127+
downloadable[req.name] = req
128+
129+
for dataset_name, dataset in DATASETS_ALL.items():
130+
dataset_instance = dataset()
131+
if not dataset.is_cached():
132+
downloadable[dataset_name] = dataset_instance
133+
134+
return downloadable
135+
136+
137+
if DOWNLOADABLE := get_downloadable():
138+
139+
@cli.command()
140+
def download(
141+
name: Annotated[
142+
str,
143+
typer.Argument(
144+
click_type=Choice(["all"] + list(DOWNLOADABLE)),
145+
metavar="NAME",
146+
),
147+
],
148+
) -> None:
149+
"""Download missing resources."""
150+
if name == "all":
151+
targets = DOWNLOADABLE.values()
152+
else:
153+
targets = [DOWNLOADABLE[name]]
154+
155+
for downloadable in targets:
156+
if isinstance(downloadable, DownloadableRequirement):
157+
downloadable.download()
158+
else:
159+
downloadable.download_dataset()
160+
107161

108162
for _, sast_data in SASTS_ALL.items():
109163
cli.add_typer(sast_data["cli_factory"].build_cli())

codesectools/datasets/core/cli.py

Lines changed: 0 additions & 44 deletions
This file was deleted.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "CodeSecTools"
3-
version = "0.5.0"
3+
version = "0.6.0"
44
description = "A framework for code security that provides abstractions for static analysis tools and datasets to support their integration, testing, and evaluation."
55
readme = "README.md"
66
license = "AGPL-3.0-only"

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)