Skip to content

Never prompt for sudo when UG_NONINTERACTIVE is set - #1253

Open
yaoharry wants to merge 3 commits into
databricks:mainfrom
yaoharry:harry/noninteractive-sudo
Open

yaoharry wants to merge 3 commits into
databricks:mainfrom
yaoharry:harry/noninteractive-sudo

Conversation

@yaoharry

Copy link
Copy Markdown
Contributor

Summary

A wrapper can start ug in a terminal nobody watches. One example is a coding-agent host that relaunches ug claude in tmux after the machine or host restarts, with the person away. stdin is a TTY there, so _sudo_may_prompt() returned true. When the OS-managed Claude Code settings needed an update, the launch printed:

Enter password to configure settings for Claude Code.
Password:

It then blocked on sudo until the wrapper gave up, and the agent never started. ug has no way to tell that terminal from a person's.

This adds an explicit opt-out, UG_NONINTERACTIVE=1. When it is set, _sudo_may_prompt() is false and the run takes ug's existing no-terminal path:

  • sudo runs with -n, so an existing file can still be repaired when credentials are cached;
  • a launch whose managed file can't be updated continues with the per-user settings when they are compatible (the existing "continuing with local settings" warning);
  • a stale managed file that conflicts still fails with the existing message, so a bare agent can't silently reach the wrong workspace.

Without the variable, behaviour is unchanged. The README documents the variable next to --user / UG_SKIP_OS_SETTINGS. Unlike those, it leaves the managed file in use and only removes the password prompt.

Wrappers have to opt in. A follow-up in the wrapper that relaunches ug unattended will set the variable.

Test plan

  • tests/test_managed_files.py:
    • test_noninteractive_env_never_lets_sudo_prompt (new): with a TTY attached and the variable set to 1 or true, _sudo_may_prompt() is false, new-file writes are refused, repairs are allowed, and _sudo_command adds -n.
    • test_noninteractive_env_unset_keeps_the_interactive_prompt (new).
  • uv run pytest tests/test_managed_files.py tests/test_agent_claude.py tests/test_agent_codex.py: 637 passed, 1 skipped.
  • uv run ruff check src tests, ruff format --check, ty check src/: clean.

This pull request and its description were written by Isaac.

Harry Yao and others added 2 commits October 22, 2026 21:28
A wrapper can start ug in a terminal nobody watches, e.g. a coding-agent host relaunching `ug claude` in tmux after a restart. stdin is a TTY there, so ug decided sudo may prompt and printed "Enter password to configure settings for Claude Code." followed by sudo's Password: prompt. The launch then blocked until the wrapper gave up.

UG_NONINTERACTIVE=1 now makes _sudo_may_prompt() false, so the run takes the existing no-terminal path: sudo -n repairs, a compatible launch continues with local settings, and a conflicting stale managed file still fails with the usual message.

Signed-off-by: Harry Yao <harry.yao@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Move the flag into ucode.env as unattended_terminal() so other prompts (the custom OAuth browser sign-in) can honor the same switch.

Signed-off-by: Harry Yao <harry.yao@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
README conflict only: keep both the --user note from main and the UG_NONINTERACTIVE paragraph.

Signed-off-by: Harry Yao <harry.yao@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant