• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nvidia-holoscan / holoscan-cli / 29010300065

09 Jul 2026 10:02AM UTC coverage: 78.598% (+0.7%) from 77.878%
29010300065

Pull #193

github

wyli
update based on comments

Signed-off-by: Wenqi Li <wenqil@nvidia.com>
Pull Request #193: fix: install CLI in one env and elevate only per-operation

158 of 201 new or added lines in 8 files covered. (78.61%)

4 existing lines in 2 files now uncovered.

3129 of 3981 relevant lines covered (78.6%)

0.79 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

47.76
/src/holoscan_cli/commands/setup_cmd.py
1
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
# SPDX-License-Identifier: Apache-2.0
3
#
4
# Licensed under the Apache License, Version 2.0 (the "License");
5
# you may not use this file except in compliance with the License.
6
# You may obtain a copy of the License at
7
#
8
# http://www.apache.org/licenses/LICENSE-2.0
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15

16
"""``holoscan setup`` — install host packages and named setup scripts.
17

18
The module is named ``setup_cmd.py`` (rather than the bare ``setup.py``
19
that would shadow the legacy setuptools build script) so reviewers and
20
IDEs do not confuse it for a packaging file.
21
"""
22

23
import argparse
1✔
24
import filecmp
1✔
25
import os
1✔
26
import sys
1✔
27
from pathlib import Path
1✔
28

29
from holoscan_cli.commands.registry import help_for
1✔
30
from holoscan_cli.utils.holohub import get_holohub_setup_scripts_dir
1✔
31
from holoscan_cli.utils.host_setup import (
1✔
32
    install_packages_if_missing,
33
    setup_cmake,
34
    setup_cuda_dependencies,
35
    setup_ngc_cli,
36
    setup_python_dev,
37
    setup_sccache,
38
)
39
from holoscan_cli.utils.io import Color, fatal, format_cmd, run_command
1✔
40

41

42
def _build_script_env() -> dict:
1✔
43
    """Environment for running a setup script.
44

45
    Puts the project's Python on ``PATH`` so a bare ``pip``/``python`` in the
46
    script resolves to it — the active venv when the user has one, otherwise the
47
    wrapper-managed venv (i.e. wherever the CLI itself is running).
48
    """
49
    env = os.environ.copy()
1✔
50
    env["PATH"] = os.path.dirname(sys.executable) + os.pathsep + env.get("PATH", "")
1✔
51
    if sys.prefix != sys.base_prefix:  # running inside a venv
1✔
52
        env["VIRTUAL_ENV"] = sys.prefix
1✔
53
    else:
NEW
54
        env.pop("VIRTUAL_ENV", None)
×
55
    return env
1✔
56

57

58
def register_setup_parser(cli, subparsers) -> argparse.ArgumentParser:
1✔
59
    """Register the ``setup`` subcommand."""
60
    parser = subparsers.add_parser("setup", help=help_for("setup"))
1✔
61
    parser.add_argument(
1✔
62
        "--dryrun", action="store_true", help="Print commands without executing them"
63
    )
64
    parser.add_argument(
1✔
65
        "--list-scripts",
66
        action="store_true",
67
        help="List all setup scripts found in the HOLOSCAN_CLI_SETUP_SCRIPTS_DIR directory. "
68
        + "Run scripts directly or with `holoscan setup --scripts <script_name>`.",
69
    )
70
    parser.add_argument(
1✔
71
        "--scripts",
72
        action="append",
73
        help="Named dependency installation scripts to run. Can be specified multiple times. "
74
        + "Searches in the directory path specified by the HOLOSCAN_CLI_SETUP_SCRIPTS_DIR environment variable. "
75
        + "Omit to install default recommended packages for Holoscan SDK development.",
76
    )
77
    parser.set_defaults(func=lambda args: handle_setup(cli, args))
1✔
78
    return parser
1✔
79

80

81
def handle_setup(cli, args: argparse.Namespace) -> None:
1✔
82
    """Handle setup command"""
83

84
    if args.list_scripts:
1✔
85
        setup_scripts_dir = get_holohub_setup_scripts_dir()
×
86
        print(format_cmd(f"Listing setup scripts available in {setup_scripts_dir}"))
×
87
        print(Color.green("Use with `holoscan setup --scripts <script_name>`"))
×
88
        for script in setup_scripts_dir.glob("*.sh"):
×
89
            print(f"  {script.stem}")
×
90
        sys.exit(0)
×
91

92
    if args.scripts:
1✔
93
        # Scripts run as the invoking user, in the project's Python environment,
94
        # with sudo available — authors write plain `sudo apt-get ...` / `pip install ...`.
95
        # A script that needs sudo prompts on its first `sudo`, and sudo caches the
96
        # credential; pip-only scripts never prompt at all.
97
        script_env = _build_script_env()
1✔
98
        for script in args.scripts:
1✔
99
            if any(sep in script for sep in ("/", "\\")):
1✔
100
                fatal(f"Invalid script name '{script}': path separators are not allowed")
×
101
            script_path = get_holohub_setup_scripts_dir().resolve() / f"{script}.sh"
1✔
102
            if not script_path.exists():
1✔
103
                fatal(f"Script {script}.sh not found in {get_holohub_setup_scripts_dir()}")
×
104
            run_command(["bash", str(script_path)], dry_run=args.dryrun, env=script_env)
1✔
105
        sys.exit(0)
1✔
106

107
    if not args.scripts:
×
108
        # The first `as_root` command prompts for sudo; sudo caches the credential
109
        # for the rest of the run. Nothing prompts when everything is installed.
UNCOV
110
        install_packages_if_missing(
×
111
            ["wget", "xvfb", "git", "unzip", "ffmpeg", "ninja-build", "libv4l-dev"],
112
            dry_run=args.dryrun,
113
        )
114

115
        setup_cuda_dependencies(dry_run=args.dryrun)
×
116
        setup_cmake(dry_run=args.dryrun)
×
117
        setup_python_dev(dry_run=args.dryrun)
×
118
        setup_ngc_cli(dry_run=args.dryrun)
×
119
        setup_sccache(dry_run=args.dryrun)
×
120

121
        # Wrappers that ship a bash completion script under
122
        # utilities/<wrapper>_autocomplete (HoloHub, Isaac OS, …) get it
123
        # installed into /etc/bash_completion.d/ so users can `source` it.
124
        # Skip silently when the source isn't present — the consolidated
125
        # CLI doesn't ship its own completion script.
126
        utilities_dir = Path(cli.HOLOHUB_ROOT) / "utilities"
×
127
        autocomplete_sources = (
×
128
            sorted(utilities_dir.glob("*_autocomplete")) if utilities_dir.is_dir() else []
129
        )
130
        dest_folder = Path("/etc/bash_completion.d")
×
131
        installed: list[Path] = []
×
132
        if autocomplete_sources and dest_folder.exists():
×
133
            for source in autocomplete_sources:
×
134
                dest = dest_folder / source.name
×
135
                if dest.exists() and filecmp.cmp(source, dest, shallow=False):
×
136
                    continue
×
NEW
137
                run_command(
×
138
                    ["cp", str(source), str(dest_folder)], dry_run=args.dryrun, as_root=True
139
                )
UNCOV
140
                installed.append(dest)
×
141

142
        if not args.dryrun:
×
143
            for dest in installed:
×
144
                print(Color.blue(f"\nTo enable {dest.name} in your current shell session:"))
×
145
                print(f"  source {dest}")
×
146
                print("Or add it to your shell profile:")
×
147
                print(f"  echo '. {dest}' >> ~/.bashrc")
×
148
                print("  source ~/.bashrc")
×
149

150
            print(Color.green(f"Setup via `{cli.script_name} setup` is ready."))
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc