diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 9a6acff..1991e34 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -18,3 +18,9 @@ If problems persist, consult the OnionShare and Tor documentation for more advan ## Electron GUI If `npm start` fails with `spawn voxvera ENOENT`, the `voxvera` command is not in your `PATH`. Install it with `pipx install voxvera` or run `./install.sh` from the repository. + +## Missing dependencies +Run `voxvera check` to see which required tools are present. The command verifies +`node`, `javascript-obfuscator`, `html-minifier-terser`, `jq`, `qrencode`, +`onionshare-cli`, and other helpers, then prints a summary of any that are +missing so you can install them. diff --git a/tests/test_cli.py b/tests/test_cli.py index 727f4b7..4cddbd4 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -64,3 +64,20 @@ def test_import(tmp_path, monkeypatch): assert dest.is_dir() assert (dest / "index.html").exists() + +def test_check_all_present(capsys, monkeypatch): + monkeypatch.setattr(shutil, "which", lambda cmd: "/usr/bin/" + cmd) + cli.main(["check"]) + captured = capsys.readouterr() + assert "All required tools are installed." in captured.out + + +def test_check_missing(capsys, monkeypatch): + def fake_which(cmd): + return None if cmd == "node" else "/usr/bin/" + cmd + + monkeypatch.setattr(shutil, "which", fake_which) + cli.main(["check"]) + captured = capsys.readouterr() + assert "node: missing" in captured.out + diff --git a/voxvera/cli.py b/voxvera/cli.py index 05805e3..8b35d9a 100644 --- a/voxvera/cli.py +++ b/voxvera/cli.py @@ -31,6 +31,39 @@ def require_cmd(cmd: str): return True +def check_deps(): + console = Console() + tools = [ + "node", + "javascript-obfuscator", + "html-minifier-terser", + "jq", + "qrencode", + "onionshare-cli", + "convert", + "pdftotext", + ] + + found = [] + missing = [] + for t in tools: + if shutil.which(t): + found.append(t) + else: + missing.append(t) + + console.rule("Dependency Check") + for t in tools: + status = "found" if t in found else "missing" + color = "green" if t in found else "red" + console.print(f"{t}: [{color}]{status}[/{color}]") + + if missing: + console.print(f"[red]Missing tools:[/red] {', '.join(missing)}") + else: + console.print("[green]All required tools are installed.[/green]") + + def run(cmd, **kwargs): try: subprocess.run(cmd, check=True, **kwargs) @@ -327,6 +360,7 @@ def main(argv=None): sub.add_parser('import', help='Batch import JSON files from imports/') sub.add_parser('serve', help='Serve flyer over OnionShare using config') sub.add_parser('quickstart', help='Init, build and serve in sequence') + sub.add_parser('check', help='Check for required external dependencies') args = parser.parse_args(argv) config_path = Path(args.config).resolve() @@ -351,6 +385,8 @@ def main(argv=None): interactive_update(config_path) build_assets(config_path) serve(config_path) + elif args.command == 'check': + check_deps() else: parser.print_help()