Skip to content

Commit

Permalink
Merge pull request #882 from koordinates/possible-clone-tty-fix
Browse files Browse the repository at this point in the history
Don't pipe stderr on windows - fixes #852
  • Loading branch information
olsen232 authored Jun 28, 2023
2 parents 1f192b8 + aeb8bbd commit 6f82173
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 20 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ Please note that compatibility for 0.x releases (software or repositories) isn't

_When adding new entries to the changelog, please include issue/PR numbers wherever possible._

## 0.14.1 (UNRELEASED)

- Fixes a bug where Git subprocesses (such as git clone) don't prompt the user for credentials or to resolve SSH issues on Windows. [#852](https://github.com/koordinates/kart/issues/852)

## 0.14.0

### Major changes
Expand Down
8 changes: 5 additions & 3 deletions kart/repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,10 +371,12 @@ def clone_repository(

@classmethod
def _create_with_git_command(cls, cmd, gitdir_path, temp_workdir_path=None):
returncode, stdout, stderr = subprocess.run_and_tee_output(cmd)
if returncode != 0:
proc = subprocess.run_and_tee_output(cmd, tee_stderr=not is_windows)
if proc.returncode != 0:
raise SubprocessError(
f"Error calling {cmd[0]} {cmd[1]}", exit_code=returncode, stderr=stderr
f"Error calling {cmd[0]} {cmd[1]}",
exit_code=proc.returncode,
stderr=b"" if is_windows else proc.stderr,
)

result = KartRepo(gitdir_path, validate=False)
Expand Down
53 changes: 36 additions & 17 deletions kart/subprocess_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,30 +127,41 @@ async def read_stream_and_display(stream, display):
return b"".join(output)


async def read_and_display(cmd, **kwargs):
"""Capture cmd's stdout and stderr while displaying them as they arrive (line by line)."""
# start process
process = await asyncio.create_subprocess_exec(
*cmd, stdout=PIPE, stderr=PIPE, **kwargs
)
async def read_and_display(cmd, tee_stdout=False, tee_stderr=False, **kwargs):
"""Capture cmd's stdout and/or stderr while displaying them as they arrive (line by line)."""
if tee_stdout:
kwargs["stdout"] = PIPE
if tee_stderr:
kwargs["stderr"] = PIPE
process = await asyncio.create_subprocess_exec(*cmd, **kwargs)

def display(stream, output):
stream.buffer.write(output)
stream.flush()

# Read child's stdout/stderr concurrently (capture and display)
try:
stdout, stderr = await asyncio.gather(
read_stream_and_display(process.stdout, partial(display, sys.stdout)),
read_stream_and_display(process.stderr, partial(display, sys.stderr)),
)
stream_coroutines = []
if tee_stdout:
stream_coroutines.append(
read_stream_and_display(process.stdout, partial(display, sys.stdout))
)
if tee_stderr:
stream_coroutines.append(
read_stream_and_display(process.stderr, partial(display, sys.stderr))
)
outputs = list(await asyncio.gather(*stream_coroutines))
except Exception:
process.kill()
raise
finally:
# Wait for the process to exit
return_code = await process.wait()
return return_code, stdout, stderr
await process.wait()
if tee_stdout:
process.stdout = outputs.pop(0)
if tee_stderr:
process.stderr = outputs.pop(0)
return process


async def read_universal_line(stream):
Expand Down Expand Up @@ -230,16 +241,24 @@ async def read_until_any_of(stream, separators=b"\n"):
return bytes(chunk)


def run_and_tee_output(cmd, **kwargs):
def run_and_tee_output(cmd, tee_stdout=False, tee_stderr=False, **kwargs):
"""
Run a subprocess and *don't* capture its output - let stdout and stderr display as per usual -
- but also *do* capture its output so that we can inspect it.
Returns a tuple of (exit-code, stdout output string, stderr output string).
"""
if "env" not in kwargs:
kwargs.setdefault("env", tool_environment())
return_code, stdout, stderr = asyncio.run(read_and_display(cmd, **kwargs))
return return_code, stdout, stderr
if "_KART_RUN_WITH_CAPTURE" in os.environ:
tee_stdout = True
tee_stderr = True
proc = asyncio.run(
read_and_display(
cmd,
tee_stdout=tee_stdout,
tee_stderr=tee_stderr,
**add_default_kwargs(kwargs),
)
)
return proc


def run_then_exit(cmd):
Expand Down

0 comments on commit 6f82173

Please sign in to comment.