diff --git a/scripts/gen_doc.py b/scripts/gen_doc.py index 6cb68b36e..1ec753efa 100755 --- a/scripts/gen_doc.py +++ b/scripts/gen_doc.py @@ -32,8 +32,8 @@ def generate_docfile(directory: Path) -> None: The file will begin with a header indicating the provided `directory` path. For each `.py` file in the current working directory the function writes a second-level Markdown header linking to the file, then writes the first - line of the module docstring if present, and finally writes the filename as - a separate entry. + line of the module docstring if present. Files with syntax errors retain + their headers but have no summary. """ with open("README.md", "w", encoding="utf-8", newline="\n") as indexfile: print( diff --git a/scripts/generate_openapi_schema.py b/scripts/generate_openapi_schema.py index d483400b7..db35bf410 100644 --- a/scripts/generate_openapi_schema.py +++ b/scripts/generate_openapi_schema.py @@ -61,7 +61,16 @@ def read_version_from_pyproject() -> str: def generate_openapi_schema(schema_file_name: str) -> None: - """Generate OpenAPI schema into file with provided name.""" + """Write the app's OpenAPI schema, then check its version against PDM. + + Overwrites the named JSON file before checking versions; the file remains + if the check fails. + + Raises: + AssertionError: If the schema and project versions differ. + subprocess.CalledProcessError: If ``pdm show --version`` fails. + OSError: If the file cannot be written or PDM cannot be started. + """ print("Service metadata:") print(app.title) print(app.description) diff --git a/scripts/latest_tag.py b/scripts/latest_tag.py index 72c97e08c..e63ba3906 100755 --- a/scripts/latest_tag.py +++ b/scripts/latest_tag.py @@ -8,7 +8,7 @@ def version_split(value: str) -> tuple[int, ...]: - """Split string into a tuple of ints.""" + """Parse dot-separated integers, returning ``(-1,)`` if any part is invalid.""" try: return tuple(int(n) for n in value.split(".")) except ValueError: @@ -16,14 +16,25 @@ def version_split(value: str) -> tuple[int, ...]: def is_prerelease(tag: str) -> bool: - """Determine if a tag is a pre-release version.""" + """Return whether a tag contains ``rc``, ``alpha``, ``beta``, or ``dev``. + + Matching is case-sensitive and does not otherwise validate the version. + """ omit = {"rc", "alpha", "beta", "dev"} return any(n in tag for n in omit) def get_latest_stable() -> Optional[str]: - """Return the latest stable tag.""" + """Return the highest local Git tag by dot-separated integer ordering. + + Excludes tags matched by ``is_prerelease``. Nonnumeric tags sort as + ``(-1,)``; returns None when no tags remain. Does not fetch remote tags. + + Raises: + subprocess.CalledProcessError: If ``git tag`` fails. + OSError: If Git cannot be started. + """ stdout = subprocess.check_output(["git", "tag"], text=True) tags = [tag for tag in stdout.splitlines() if not is_prerelease(tag)] tags.sort(key=version_split) @@ -32,7 +43,12 @@ def get_latest_stable() -> Optional[str]: def main() -> None: - """Entry point to this script, retrieves latest tag defined on GitHub.""" + """Entry point to this script, retrieves latest tag defined on GitHub. + + Append ``apply_latest=true`` or ``apply_latest=false`` when GITHUB_OUTPUT + is set. An unset or prerelease GIT_TAG produces false without querying Git. + Git command and output-file errors propagate to the caller. + """ if not (current_tag := os.environ.get("GIT_TAG")): reason = "GIT_TAG environment variable not set, skipping latest tag" apply_latest = "false" diff --git a/scripts/vulnerability_report.py b/scripts/vulnerability_report.py index 0645f2a02..1026a7ae1 100644 --- a/scripts/vulnerability_report.py +++ b/scripts/vulnerability_report.py @@ -368,8 +368,9 @@ def fill_in_days_statistic(source_data: DependabotAlerts) -> dict[str, Any]: Compute statistics on elapsed days from creation to fix for alerts in a fixed state. Returns: - dict[str, Any]: Dictionary with "days" (list of elapsed day values), + dict[str, Any]: Dictionary with "days" (list of fractional elapsed days), "avg" (average days to fix), and "median" (median days to fix). + With no fixed alerts, "days" is empty and both summary values are zero. """ fixed = filter_by(source_data, "state", "fixed") @@ -426,13 +427,18 @@ def process_dependabot_file(dependabot_file: str) -> dict[str, Any]: Parameters: dependabot_file (str): Path to the JSON file containing Dependabot alerts. - prefix (str): Unused; retained for interface compatibility. Returns: dict[str, Any]: A dictionary with keys: "count", "state", "severity", "severities", "days", "packages", and "dates", each containing the corresponding computed metrics. + Raises: + OSError: If the input file cannot be read. + ValueError: If JSON decoding, alert validation, or date parsing fails. + KeyError: If a required package or timestamp field is missing. + TypeError: If timestamp types or timezone awareness are incompatible. + """ source_data = load_dependabot_file(dependabot_file) @@ -605,6 +611,11 @@ def generate_page( ) -> None: """ Generate HTML page containing visualization graphs. + + Use the organization and repository in the title and include average and + median fix times from ``stat["days"]``. Reference ``{prefix}_*.svg`` charts + and ``stylesheet.css`` without generating them. + """ filename = f"vulnerabilities_{prefix}.html" output = f""" @@ -700,6 +711,13 @@ def retrieve_issues(organization: str, repository: str, dependabot_file: str) -> 1 if the GitHub token is missing. 2 if the organization or repository name is invalid. + Raises: + requests.exceptions.RequestException: If a request or HTTP status fails. + ValueError: If response JSON decoding fails. + OSError: If the output file cannot be written. + + Failures after opening the file can leave partial output; no rollback occurs. + """ if not is_valid_github_org_repo(organization): print("Organization name is not correct") @@ -764,12 +782,22 @@ def main() -> int: """ CLI entry point that retrieves Dependabot issues and produces Vulnerability report. + Retrieve Dependabot alerts or generate reports from the local alerts file. + Parses command-line arguments, retrieves Dependabot issues, analyses issues, generates graphs, and generates HTML page with Vulnerability report. Is is also possible to compare two repositories. Error diagnostics are printed to stderr. + Raises: + ValueError: If graph generation lacks an output format, or data + decoding, validation, or date parsing fails. + OSError: If an input or output file cannot be accessed. + requests.exceptions.RequestException: If alert retrieval fails. + + Other processing and plotting errors propagate to the caller. + Usage: Returns: