@@ -186,6 +186,10 @@ def task_args(self) -> List["Argument"]:
186186 indent_width = 4
187187 indent = " " * indent_width
188188 col_padding = 3
189+ root_warning = (
190+ "WARNING: Running Invoke as root may create root-owned files and "
191+ "cause later I/O or permission errors. Re-run as a non-root user."
192+ )
189193
190194 def __init__ (
191195 self ,
@@ -373,6 +377,7 @@ def run(self, argv: Optional[List[str]] = None, exit: bool = True) -> None:
373377 .. versionadded:: 1.0
374378 """
375379 try :
380+ self .warn_if_running_as_root (is_testing = not exit )
376381 # Create an initial config, which will hold defaults & values from
377382 # most config file locations (all but runtime.) Used to inform
378383 # loading & parsing behavior.
@@ -421,6 +426,22 @@ def run(self, argv: Optional[List[str]] = None, exit: bool = True) -> None:
421426 except KeyboardInterrupt :
422427 sys .exit (1 ) # Same behavior as Python itself outside of REPL
423428
429+ def warn_if_running_as_root (self , is_testing : bool = False ) -> None :
430+ """
431+ Emit a warning when Invoke is executed as the root user.
432+ """
433+ if is_testing or not self .running_as_root ():
434+ return
435+ print (self .root_warning , file = sys .stderr )
436+
437+ def running_as_root (self ) -> bool :
438+ """
439+ Return ``True`` when the current process is running as root.
440+ """
441+ if hasattr (os , "geteuid" ):
442+ return os .geteuid () == 0
443+ return getpass .getuser () == "root"
444+
424445 def parse_core (self , argv : Optional [List [str ]]) -> None :
425446 debug ("argv given to Program.run: {!r}" .format (argv ))
426447 self .normalize_argv (argv )
0 commit comments