← Back to Blog

pyrpc dev --yes: non-interactive setup for CI and scripts

·7 min read

Interactive wizards are great for humans and terrible for machines. A CI job, a Docker build, or a setup script cannot answer prompts, and a tool that blocks on input in one of those contexts is broken. That is why pyrpc dev ships a fully non-interactive mode behind a single flag: --yes.

Fully explicit: --yes --module --client

When you supply both values on the command line, nothing is detected and nothing is asked:

if yes and module and client:
 # Fully non-interactive: all values supplied on the command line.
 cfg = {"module": module, "framework": "Other", "client": client}
 if cfg_path is None:
 cfg_path = _write_config(cfg)
 console.print(f" [green]✓[/green] pyrpc.json created")

This is the deterministic form for CI: pyrpc dev --yes --module main --client ../frontend. The same command run a hundred times produces the same config. It writespyrpc.json only if one does not already exist, a pre-existing config is respected and read instead.

Auto-detected: --yes alone

With --yes but no explicit flags, the CLI auto-detects everything it can:

  • Module, first match of main.py, server.py, app.py, app/main.py.
  • Client + framework, via _find_frontend_projects. Exactly one project → it is used. Several projects → hard error listing them and pointing at --client. None → no client configured, framework "Other".

The multiple-projects case deserves emphasis. Rather than silently pick the first frontend it finds, a guess that would generate types into the wrong app, the CLI refuses and tells you exactly how to disambiguate:

elif len(detected_projects) > 1:
 console.print("[red]✗ Multiple TypeScript projects found.[/red]\n")
 for p, _ in detected_projects:
 console.print(f" • {p}")
 console.print("\n[dim]Specify which client to use:[/dim]\n")
 console.print(" [cyan]pyrpc dev --client <path>[/cyan]\n")
 raise typer.Exit(1)

Non-interactive mode still refuses to guess in ambiguous situations, it just reports the ambiguity in a machine-readable way (a non-zero exit) instead of asking a human.

The result is a summary line, not a dialog

After detection, --yes prints a compact line that captures the decision:

if resolved_client is not None:
 console.print(f" [dim]module={cfg['module']} client={cfg['client']}[/dim]")
else:
 console.print(f" [dim]module={cfg['module']} (no client configured)[/dim]")

With no client configured, type generation is skipped at startup and the dev console shows○ no clients configured, skipping type generation. This is a legitimate state: a pure-API server, or a frontend that will be configured later.

Where non-interactive mode shines

  • CI, regenerate types in a build step with --yes --module main --client ./frontend, deterministic every run.
  • Container image builds, a Dockerfile layer that pre-generates types cannot hang on a prompt; --yes guarantees it cannot.
  • Scripted onboarding, repo setup scripts, devcontainer post-create hooks, and Makefile targets can configure pyRPC without user input.

--yes rounds out the setup matrix: the wizard for humans, explicit flags for the deterministic case, and auto-detection for the “I just want it to work” case. Each path ends in the same pyrpc.json.

Read the full changelogfor the complete list of changes.