repo: handle bad programs a bit better

If programs emit non-UTF-8 output, we currently throw a fatal error.
We largely only care about the exit status of programs, and even the
output we do parse is a bit minimal.  Lets make it into a warning and
mangle the invalid bytes into U+FFFD.  This should complain enough to
annoy but not to break when it's not necessary.

Bug: https://crbug.com/gerrit/12337#c2
Change-Id: Idbc94f19ff4d84d2e47e01960dd17d5b492d4a8a
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/255272
Reviewed-by: David Pursehouse <dpursehouse@collab.net>
Tested-by: Mike Frysinger <vapier@google.com>
diff --git a/repo b/repo
index 30bce52..7771c2d 100755
--- a/repo
+++ b/repo
@@ -363,15 +363,23 @@
     kwargs.setdefault('stderr', subprocess.PIPE)
   cmd_input = kwargs.pop('input', None)
 
+  def decode(output):
+    """Decode |output| to text."""
+    if output is None:
+      return output
+    try:
+      return output.decode('utf-8')
+    except UnicodeError:
+      print('repo: warning: Invalid UTF-8 output:\ncmd: %r\n%r' % (cmd, output),
+            file=sys.stderr)
+      # TODO(vapier): Once we require Python 3, use 'backslashreplace'.
+      return output.decode('utf-8', 'replace')
+
   # Run & package the results.
   proc = subprocess.Popen(cmd, **kwargs)
   (stdout, stderr) = proc.communicate(input=cmd_input)
-  if stdout is not None:
-    stdout = stdout.decode('utf-8')
-  if stderr is not None:
-    stderr = stderr.decode('utf-8')
   trace.print(':', ' '.join(cmd))
-  ret = RunResult(proc.returncode, stdout, stderr)
+  ret = RunResult(proc.returncode, decode(stdout), decode(stderr))
 
   # If things failed, print useful debugging output.
   if check and ret.returncode: