summaryrefslogtreecommitdiff
path: root/lib/spack/spack/cmd/audit.py
blob: c8cef494ef749b28af4db2dce952504c2ea03993 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# Copyright 2013-2024 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import warnings

import llnl.util.tty as tty
import llnl.util.tty.colify
import llnl.util.tty.color as cl

import spack.audit
import spack.repo

description = "audit configuration files, packages, etc."
section = "system"
level = "short"


def setup_parser(subparser):
    # Top level flags, valid for every audit class
    sp = subparser.add_subparsers(metavar="SUBCOMMAND", dest="subcommand")

    # Audit configuration files
    sp.add_parser("configs", help="audit configuration files")

    # Audit package recipes
    external_parser = sp.add_parser("externals", help="check external detection in packages")
    external_parser.add_argument(
        "--list",
        action="store_true",
        dest="list_externals",
        help="if passed, list which packages have detection tests",
    )

    # Https and other linting
    https_parser = sp.add_parser("packages-https", help="check https in packages")
    https_parser.add_argument(
        "--all", action="store_true", default=False, dest="check_all", help="audit all packages"
    )

    # Audit package recipes
    pkg_parser = sp.add_parser("packages", help="audit package recipes")

    for group in [pkg_parser, https_parser, external_parser]:
        group.add_argument(
            "name",
            metavar="PKG",
            nargs="*",
            help="package to be analyzed (if none all packages will be processed)",
        )

    # List all checks
    sp.add_parser("list", help="list available checks and exits")


def configs(parser, args):
    with warnings.catch_warnings():
        warnings.simplefilter("ignore")
        reports = spack.audit.run_group(args.subcommand)
        _process_reports(reports)


def packages(parser, args):
    pkgs = args.name or spack.repo.PATH.all_package_names()
    reports = spack.audit.run_group(args.subcommand, pkgs=pkgs)
    _process_reports(reports)


def packages_https(parser, args):
    # Since packages takes a long time, --all is required without name
    if not args.check_all and not args.name:
        tty.die("Please specify one or more packages to audit, or --all.")

    pkgs = args.name or spack.repo.PATH.all_package_names()
    reports = spack.audit.run_group(args.subcommand, pkgs=pkgs)
    _process_reports(reports)


def externals(parser, args):
    if args.list_externals:
        msg = "@*{The following packages have detection tests:}"
        tty.msg(cl.colorize(msg))
        llnl.util.tty.colify.colify(spack.audit.packages_with_detection_tests(), indent=2)
        return

    pkgs = args.name or spack.repo.PATH.all_package_names()
    reports = spack.audit.run_group(args.subcommand, pkgs=pkgs)
    _process_reports(reports)


def list(parser, args):
    for subcommand, check_tags in spack.audit.GROUPS.items():
        print(cl.colorize("@*b{" + subcommand + "}:"))
        for tag in check_tags:
            audit_obj = spack.audit.CALLBACKS[tag]
            print("  " + audit_obj.description)
            if args.verbose:
                for idx, fn in enumerate(audit_obj.callbacks):
                    print("    {0}. ".format(idx + 1) + fn.__doc__)
                print()
        print()


def audit(parser, args):
    subcommands = {
        "configs": configs,
        "externals": externals,
        "packages": packages,
        "packages-https": packages_https,
        "list": list,
    }
    subcommands[args.subcommand](parser, args)


def _process_reports(reports):
    for check, errors in reports:
        if errors:
            msg = "{0}: {1} issue{2} found".format(
                check, len(errors), "" if len(errors) == 1 else "s"
            )
            header = "@*b{" + msg + "}"
            print(cl.colorize(header))
            for idx, error in enumerate(errors):
                print(str(idx + 1) + ". " + str(error))
            raise SystemExit(1)
        else:
            msg = "{0}: 0 issues found.".format(check)
            header = "@*b{" + msg + "}"
            print(cl.colorize(header))