Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] advice: refuse to output if stderr not TTY #1776

Closed
wants to merge 7 commits into from

Conversation

derrickstolee
Copy link

@derrickstolee derrickstolee commented Aug 20, 2024

Advice is supposed to be for humans, not machines. Why do we output it when stderr is not a terminal? Let's stop doing that.

I'm labeling this as an RFC because I believe there is some risk with this change. In particular, this does change behavior to reduce the output that some scripts may depend upon. But this output is not intended to be locked in and we add or edit advice messages without considering this impact, so there is risk in the existing system already.

This series is motivated by an internal tool breaking due to the advice message added to Git 2.46.0 by 9479a31 (advice: warn when sparse index expands, 2024-07-08). This tool is assuming that any output to stderr is an error, and in this case is attempting to parse it to determine what kind of error (warning, error, or failure).

I've recommended that the tool author remove the advice message for now, but I'd like to help other tool authors avoid this surprise.

I read the thread for the --no-advice option [1] looking to see if this was presented as an option, but did not see it as part of that review. I hope that this is not considered a breaking change for users, but I could see the argument for that.

[1] https://lore.kernel.org/git/20240424035857.84583-1-james@jamesliu.io/t/#u

  • Patches 1-5 are preparation patches to make the test library work to test the advice system after the final patch. These are split by test file name to reduce the size of the patches, but could be squashed into a megapatch if necessary. This is usually a simple addition of the GIT_ADVICE=1 environment variable, but there were some changes made to those lines to be more correct as necessary.
  • Patch 6 highlights the fact that 'git status' uses advice_enabled() to determine if it should print certain parenthetical results. See format_tracking_info() in remote.c for an example. This output doesn't use the advise() method, but instead appends to a string buffer that is later sent to stdout. (If we think this part of the change is too risky, then we could move the isatty() out of advice_enabled() and into advise(), but that would not match the existing behavior of what is blocked by --no-advice.)
  • Patch 7 modifies advice_enabled() to disable when isatty(2) is false and GIT_ADVICE is unset.

Thanks, - Stolee

cc: gitster@pobox.com
cc: Johannes.Schindelin@gmx.de
cc: ps@pks.im
cc: james@jamesliu.io
cc: Jeff King peff@peff.net
cc: Gabor Gombas gombasgg@gmail.com

@derrickstolee derrickstolee self-assigned this Aug 20, 2024
@derrickstolee derrickstolee marked this pull request as ready for review August 21, 2024 02:58
Several tests validate the exact output of stderr, including when the stderr
file should be empty. In advance of modifying the advice system to only
output when stderr is a terminal, force the advice system to output in these
cases.

Signed-off-by: Derrick Stolee <derrickstolee@github.com>
Several tests validate the exact output of stderr, including when the stderr
file should be empty. In advance of modifying the advice system to only
output when stderr is a terminal, force the advice system to output in these
cases.

Signed-off-by: Derrick Stolee <derrickstolee@github.com>
Several tests validate the exact output of stderr, including when the stderr
file should be empty. In advance of modifying the advice system to only
output when stderr is a terminal, force the advice system to output in these
cases.

In particular, lib-https.sh must be updated in order for t5541 to succeed as
it calls test_http_push_nonff.

Signed-off-by: Derrick Stolee <derrickstolee@github.com>
Several tests validate the exact output of stderr, including when the stderr
file should be empty. In advance of modifying the advice system to only
output when stderr is a terminal, force the advice system to output in these
cases.

Signed-off-by: Derrick Stolee <derrickstolee@github.com>
Several tests validate the exact output of stderr, including when the stderr
file should be empty. In advance of modifying the advice system to only
output when stderr is a terminal, force the advice system to output in these
cases.

In addition, two more edits were made while in the neighborhood:

 1. In t7002, a redirected stderr was ignored and is now checked as empty.

 2. In t7060 and 7500, the output of "git status" has paranthetical messages
    that appear only when advice is enabled, even though it is sent to stdout.

 3. In t7400, a command was checked for failure with "!" but is now checked
    via test_must_fail.

Signed-off-by: Derrick Stolee <derrickstolee@github.com>
The output of 'git status' changes depending on the availability of advice,
even though the messages are to stdout. Since this test script is all about
testing the output of 'git status' including the existence (or lack of)
these messages, set the GIT_ADVICE environment globally across the script.

Signed-off-by: Derrick Stolee <derrickstolee@github.com>
The advice system is intended to help end users around corner cases or other
difficult spots when using the Git tool. As such, they are added without
considering the possibility that they could break scripts or external tools
that execute Git processes and then parse the output.

I will not debate the merit of tools parsing stderr, but instead attempt to
be helpful to tool authors by avoiding these behavior changes across Git
versions.

In b79deeb (advice: add --no-advice global option, 2024-05-03), the
--no-advice option was presented as a way to help tool authors specify that
they do not want any advice messages. As part of this implementation, the
GIT_ADVICE environment variable is given as a way to communicate the desire
for advice (=1) or no advice (=0) and pass that along to all child
processes.

However, both the --no-advice option and the GIT_ADVICE environment variable
require the tool author to change how they interact with Git to gain this
protection.

If Git instead disables the advice system when stderr is not a terminal,
then tool authors benefit immediately.

It is important, though, to let interested users force advice to be enabled,
even when redirecting stderr to a non-terminal file. Be sure to test this by
ensuring GIT_ADVICE=1 forces advice to be written to non-terminals.

The changes leading up to this already set GIT_ADVICE=1 in all other test
scripts that care about the advice being output (or not).

Signed-off-by: Derrick Stolee <derrickstolee@github.com>
@derrickstolee derrickstolee changed the title advice: refuse to output if stderr not TTY [RFC] advice: refuse to output if stderr not TTY Aug 21, 2024
@derrickstolee
Copy link
Author

/submit

Copy link

gitgitgadget bot commented Aug 21, 2024

Submitted as pull.1776.git.1724238152.gitgitgadget@gmail.com

To fetch this version into FETCH_HEAD:

git fetch https://github.com/gitgitgadget/git/ pr-1776/derrickstolee/advice-tty-v1

To fetch this version to local tag pr-1776/derrickstolee/advice-tty-v1:

git fetch --no-tags https://github.com/gitgitgadget/git/ tag pr-1776/derrickstolee/advice-tty-v1

Copy link

gitgitgadget bot commented Aug 21, 2024

On the Git mailing list, Jeff King wrote (reply to this):

On Wed, Aug 21, 2024 at 11:02:25AM +0000, Derrick Stolee via GitGitGadget wrote:

> Advice is supposed to be for humans, not machines. Why do we output it when
> stderr is not a terminal? Let's stop doing that.
> 
> I'm labeling this as an RFC because I believe there is some risk with this
> change. In particular, this does change behavior to reduce the output that
> some scripts may depend upon. But this output is not intended to be locked
> in and we add or edit advice messages without considering this impact, so
> there is risk in the existing system already.

Playing devil's advocate for a moment: what about programs that read
stderr but intend to relay the output to the user?

For example, programs running on the server side of a push are spawned
by receive-pack with their stderr fed into a muxer that ships it to the
client, who then dumps it to the user's terminal. Would we ever want to
see their advice?

My guess is "conceivably yes", though I don't know of a specific example
(and in fact, I've seen the "your hook was ignored because it's not
executable" advice coming from a server, which was actually more of an
annoyance on the client side).

Ditto for upload-pack. Another possible place where it matters:
interfaces that wrap Git and collect the output to show to the user. I
don't use git-gui, but I'd imagine it does this in some places.

Looking over patch 7, I think the escape hatch for all of these cases
would be setting GIT_ADVICE=1. Which isn't too bad, but it does require
some action. I'm not sure if it is worth it (but then, I am not all that
sympathetic to the script you mentioned that was trying to be too clever
about parsing stderr).

-Peff

Copy link

gitgitgadget bot commented Aug 21, 2024

User Jeff King <peff@peff.net> has been added to the cc: list.

Copy link

gitgitgadget bot commented Aug 21, 2024

On the Git mailing list, Junio C Hamano wrote (reply to this):

"Derrick Stolee via GitGitGadget" <gitgitgadget@gmail.com> writes:

> Advice is supposed to be for humans, not machines. Why do we output it when
> stderr is not a terminal? Let's stop doing that.

Last night while skimming the series on my phone (read: not a real
review at all), I found it very annoying that GIT_ADVICE=1 had to be
sprinkled all over the place.  I wonder if we want to instead set
and export it in t/test-lib.sh and turn it off as needed?

The end-to-end tests we have are primarily to guarantee the
continuity of the end-user experience by humans, and ensuring that
an advice message is given when appropriate and it does not get
shown otherwise is very much inherent part of them.  An alternative
workaround to counteract the breakage this series causes of course
is to run everything under test_terminal and it probably is much
more kosher philosophically ;-), but compared to that, globally
disabling the "if (!isatty(2))" while running the tests, and
temporarily lifting that disabling during tests of the new feature
added by this series would be easier to reason about, I would
suspect.

> This series is motivated by an internal tool breaking due to the advice
> message added to Git 2.46.0 by 9479a31d603 (advice: warn when sparse index
> expands, 2024-07-08). This tool is assuming that any output to stderr is an
> error, and in this case is attempting to parse it to determine what kind of
> error (warning, error, or failure).

The "anything on stderr is an error" attitude needs to be fixed
regardless of where it comes from (tcl/tk scripts have, or at least
used to have, the tendency, which I found annoying), but regardless,
I thought we added a mechanism to squelch all advice messages for
this exact purpose at f0e21837 (Merge branch 'jl/git-no-advice',
2024-05-16).  Why isn't the tool using the mechanism that already
exists?

I would have supported the behaviour proposed by this series 100% if
it were on the table when we were introducing the advise mechanism,
but unfortunately nobody seemed have suggested it back then.  I am
willing to go with an "experiment" to change the behaviour,
deliberately breaking "backward compatibility", if we have a wide
support here during the review period.  FWIW, I think any scripts
that scrape the advice messages are already broken.

Copy link

gitgitgadget bot commented Aug 21, 2024

On the Git mailing list, Junio C Hamano wrote (reply to this):

Jeff King <peff@peff.net> writes:

> Playing devil's advocate for a moment: what about programs that read
> stderr but intend to relay the output to the user?
>
> For example, programs running on the server side of a push are spawned
> by receive-pack with their stderr fed into a muxer that ships it to the
> client, who then dumps it to the user's terminal. Would we ever want to
> see their advice?
>
> My guess is "conceivably yes", though I don't know of a specific example
> (and in fact, I've seen the "your hook was ignored because it's not
> executable" advice coming from a server, which was actually more of an
> annoyance on the client side).

Ah, I should have waited to think about the topic before reading
what you wrote.  Yes, this is a huge downside.

> Looking over patch 7, I think the escape hatch for all of these cases
> would be setting GIT_ADVICE=1. Which isn't too bad, but it does require
> some action. I'm not sure if it is worth it (but then, I am not all that
> sympathetic to the script you mentioned that was trying to be too clever
> about parsing stderr).

This too.

Copy link

gitgitgadget bot commented Aug 22, 2024

On the Git mailing list, Gabor Gombas wrote (reply to this):

Hi,

On Wed, Aug 21, 2024 at 11:02:25AM +0000, Derrick Stolee via GitGitGadget wrote:

> Advice is supposed to be for humans, not machines. Why do we output it when
> stderr is not a terminal? Let's stop doing that.

Really bad idea. "/some/script 2>&1 | tee /some/where | less" is a
common, generic debug construct (with countless variations of the exact
commands in the pipe - this is Unix, after all). If /some/script happens
to run git, then I _do_ want to see all the diagnostic messages it might
produce, both recorded at /some/where, and displayed by "less".

Regards,
Gabor

Copy link

gitgitgadget bot commented Aug 22, 2024

User Gabor Gombas <gombasgg@gmail.com> has been added to the cc: list.

Copy link

gitgitgadget bot commented Aug 22, 2024

On the Git mailing list, Patrick Steinhardt wrote (reply to this):

On Wed, Aug 21, 2024 at 09:36:56AM -0700, Junio C Hamano wrote:
> "Derrick Stolee via GitGitGadget" <gitgitgadget@gmail.com> writes:
> 
> > Advice is supposed to be for humans, not machines. Why do we output it when
> > stderr is not a terminal? Let's stop doing that.
> 
> Last night while skimming the series on my phone (read: not a real
> review at all), I found it very annoying that GIT_ADVICE=1 had to be
> sprinkled all over the place.  I wonder if we want to instead set
> and export it in t/test-lib.sh and turn it off as needed?
> 
> The end-to-end tests we have are primarily to guarantee the
> continuity of the end-user experience by humans, and ensuring that
> an advice message is given when appropriate and it does not get
> shown otherwise is very much inherent part of them.  An alternative
> workaround to counteract the breakage this series causes of course
> is to run everything under test_terminal and it probably is much
> more kosher philosophically ;-), but compared to that, globally
> disabling the "if (!isatty(2))" while running the tests, and
> temporarily lifting that disabling during tests of the new feature
> added by this series would be easier to reason about, I would
> suspect.
> 
> > This series is motivated by an internal tool breaking due to the advice
> > message added to Git 2.46.0 by 9479a31d603 (advice: warn when sparse index
> > expands, 2024-07-08). This tool is assuming that any output to stderr is an
> > error, and in this case is attempting to parse it to determine what kind of
> > error (warning, error, or failure).
> 
> The "anything on stderr is an error" attitude needs to be fixed
> regardless of where it comes from (tcl/tk scripts have, or at least
> used to have, the tendency, which I found annoying), but regardless,
> I thought we added a mechanism to squelch all advice messages for
> this exact purpose at f0e21837 (Merge branch 'jl/git-no-advice',
> 2024-05-16).  Why isn't the tool using the mechanism that already
> exists?
> 
> I would have supported the behaviour proposed by this series 100% if
> it were on the table when we were introducing the advise mechanism,
> but unfortunately nobody seemed have suggested it back then.  I am
> willing to go with an "experiment" to change the behaviour,
> deliberately breaking "backward compatibility", if we have a wide
> support here during the review period.  FWIW, I think any scripts
> that scrape the advice messages are already broken.

I continue to believe that the biggest issue in this context is that
there is no proper interface between Git and its caller that would allow
the caller to learn about errors in a machine-parseable way. Matching
error messages against regular expressions is bad, and can easily be
broken by the output changing in whatever way. This may be because the
error message itself was changed, or it may be because we have started
to show advice messages. It's extremely fragile, and from my point of
view there is no good way to classify errors right now.

I won't argue that checking whether stderr is empty or not is good -- it
almost certainly feels wrong to me. But that's only one small part of a
more widespread issue. Having structured error handling in Git, e.g. via
a new structure that represents errors as discussed a couple of months
ago [1] would go a long way. I didn't quite like the approach chosen by
that patch series, but think that the idea certainly has merit.

The other question is why advice is being shown in the first place. In
theory, all one should ever use in scripted usecases are plumbing tools.
And as plumbing tools are explicitly not designed for users, they should
never show advice in the first place. I guess chances are high though
that the scripts in question used porcelain. That is also understandable
though: our plumbing tools are often not as powerful as the porcelain
ones, which has been lamented on the mailing list several times.

So I certainly get the sentiment of this patch series, but feel like we
continue to work around the underlying problems. Those are rooted rather
deep though, so fixing them is nothing we can do in a release or two,
but rather on the order of years. Meanwhile I guess we have to find
short-term solutions.

Patrick

[1]: https://lore.kernel.org/git/pull.1666.git.git.1708241612.gitgitgadget@gmail.com/

Copy link

gitgitgadget bot commented Aug 22, 2024

On the Git mailing list, Derrick Stolee wrote (reply to this):

On 8/21/24 7:02 AM, Derrick Stolee via GitGitGadget wrote:
> Advice is supposed to be for humans, not machines. Why do we output it when
> stderr is not a terminal? Let's stop doing that.
> > I'm labeling this as an RFC because I believe there is some risk with this
> change. Thanks, all, for the feedback about the risk of making such a change. I
agree that we should not pursue this direction.

The main issues are:

 1. Some tools create a wrapper around Git and may want to supply the
    advice to the user by parsing stderr.

 2. The advice system has been on for a long time and we cannot know
    where other dependencies could be for it.

I'll abandon this RFC, but plan on the following action items:

 * Document GIT_ADVICE in Documentation/git.exe.

 * Modify Documentation/config/advice.txt to mention GIT_ADVICE and
   recommend that automated tools calling Git commands set it to zero.

 * If we have a place to recommend best practices for automation
   executing Git commands, then I would add GIT_ADVICE=0 as a
   recommendation there. I couldn't find one myself. Do we have one?

Thanks!
-Stolee

@derrickstolee derrickstolee deleted the advice-tty branch August 22, 2024 15:06
Copy link

gitgitgadget bot commented Aug 22, 2024

On the Git mailing list, Junio C Hamano wrote (reply to this):

Derrick Stolee <stolee@gmail.com> writes:

> On 8/21/24 7:02 AM, Derrick Stolee via GitGitGadget wrote:
>> Advice is supposed to be for humans, not machines. Why do we output it when
>> stderr is not a terminal? Let's stop doing that.
>> I'm labeling this as an RFC because I believe there is some risk
>> with this
>> change. 
>
> Thanks, all, for the feedback about the risk of making such a change. I
> agree that we should not pursue this direction.
>
> The main issues are:
>
>  1. Some tools create a wrapper around Git and may want to supply the
>     advice to the user by parsing stderr.

Or they may just pass it through to the user without even parsing.

>  2. The advice system has been on for a long time and we cannot know
>     where other dependencies could be for it.
>
> I'll abandon this RFC, but plan on the following action items:
>
>  * Document GIT_ADVICE in Documentation/git.exe.
>
>  * Modify Documentation/config/advice.txt to mention GIT_ADVICE and
>    recommend that automated tools calling Git commands set it to zero.

FWIW, not documenting it was very much deliberate to discourage
folks placing it in their ~/.login file.  I am OK with the above as
long as "this is for tools" is stressed well enough.

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