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

nvidia-holoscan / holoscan-cli / 28852898255

07 Jul 2026 08:35AM UTC coverage: 77.916% (+0.7%) from 77.248%
28852898255

Pull #193

github

wyli
refactor: share the recursive local-build command builder

build and run --as-root assembled the same in-container build command
independently; extract make_local_build_command and cover its flags with
one exact-string assertion in the existing container-branch test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Wenqi Li <wenqil@nvidia.com>
Pull Request #193: fix: install CLI in one env and elevate only per-operation

140 of 182 new or added lines in 8 files covered. (76.92%)

4 existing lines in 2 files now uncovered.

3066 of 3935 relevant lines covered (77.92%)

0.78 hits per line

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

48.53
/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
    # A stale PYTHONHOME breaks whichever python/pip the prepended PATH resolves.
52
    env.pop("PYTHONHOME", None)
1✔
53
    if sys.prefix != sys.base_prefix:  # running inside a venv
1✔
54
        env["VIRTUAL_ENV"] = sys.prefix
1✔
55
    else:
NEW
56
        env.pop("VIRTUAL_ENV", None)
×
57
    return env
1✔
58

59

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

82

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

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

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

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

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

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

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

152
            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