Skip to content

Commit bf83c66

Browse files
authored
Merge pull request cli#5143 from cli/pager-epipe-ignore
Ignore EPIPE errors when writing to a closed pager
2 parents c9f44ff + 0a5e220 commit bf83c66

File tree

7 files changed

+51
-20
lines changed

7 files changed

+51
-20
lines changed

cmd/gh/main.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/cli/cli/v2/pkg/cmd/factory"
2525
"github.com/cli/cli/v2/pkg/cmd/root"
2626
"github.com/cli/cli/v2/pkg/cmdutil"
27+
"github.com/cli/cli/v2/pkg/iostreams"
2728
"github.com/cli/cli/v2/utils"
2829
"github.com/cli/safeexec"
2930
"github.com/mattn/go-colorable"
@@ -201,6 +202,7 @@ func mainRun() exitCode {
201202
rootCmd.SetArgs(expandedArgs)
202203

203204
if cmd, err := rootCmd.ExecuteC(); err != nil {
205+
var pagerPipeError *iostreams.ErrClosedPagerPipe
204206
if err == cmdutil.SilentError {
205207
return exitError
206208
} else if cmdutil.IsUserCancellation(err) {
@@ -211,6 +213,9 @@ func mainRun() exitCode {
211213
return exitCancel
212214
} else if errors.Is(err, authError) {
213215
return exitAuth
216+
} else if errors.As(err, &pagerPipeError) {
217+
// ignore the error raised when piping to a closed pager
218+
return exitOK
214219
}
215220

216221
printError(stderr, err, cmd, hasDebug)

pkg/cmd/api/api.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package api
33
import (
44
"bytes"
55
"encoding/json"
6-
"errors"
76
"fmt"
87
"io"
98
"io/ioutil"
@@ -13,7 +12,6 @@ import (
1312
"sort"
1413
"strconv"
1514
"strings"
16-
"syscall"
1715
"time"
1816

1917
"github.com/MakeNowJust/heredoc"
@@ -384,11 +382,7 @@ func processResponse(resp *http.Response, opts *ApiOptions, headersOutputStream
384382
_, err = io.Copy(opts.IO.Out, responseBody)
385383
}
386384
if err != nil {
387-
if errors.Is(err, syscall.EPIPE) {
388-
err = nil
389-
} else {
390-
return
391-
}
385+
return
392386
}
393387

394388
if serverError == "" && resp.StatusCode > 299 {

pkg/cmd/pr/diff/diff.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"io"
88
"net/http"
99
"strings"
10-
"syscall"
1110

1211
"github.com/MakeNowJust/heredoc"
1312
"github.com/cli/cli/v2/api"
@@ -112,9 +111,6 @@ func diffRun(opts *DiffOptions) error {
112111

113112
if !opts.UseColor {
114113
_, err = io.Copy(opts.IO.Out, diff)
115-
if errors.Is(err, syscall.EPIPE) {
116-
return nil
117-
}
118114
return err
119115
}
120116

pkg/cmd/repo/view/view.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"net/http"
77
"net/url"
88
"strings"
9-
"syscall"
109
"text/template"
1110

1211
"github.com/MakeNowJust/heredoc"
@@ -213,12 +212,7 @@ func viewRun(opts *ViewOptions) error {
213212
View: cs.Gray(fmt.Sprintf("View this repository on GitHub: %s", openURL)),
214213
}
215214

216-
err = tmpl.Execute(stdout, repoData)
217-
if err != nil && !errors.Is(err, syscall.EPIPE) {
218-
return err
219-
}
220-
221-
return nil
215+
return tmpl.Execute(stdout, repoData)
222216
}
223217

224218
func isMarkdownFile(filename string) bool {

pkg/iostreams/epipe_other.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//go:build !windows
2+
// +build !windows
3+
4+
package iostreams
5+
6+
import (
7+
"errors"
8+
"syscall"
9+
)
10+
11+
func isEpipeError(err error) bool {
12+
return errors.Is(err, syscall.EPIPE)
13+
}

pkg/iostreams/epipe_windows.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package iostreams
2+
3+
import (
4+
"errors"
5+
"syscall"
6+
)
7+
8+
func isEpipeError(err error) bool {
9+
// 232 is Windows error code ERROR_NO_DATA, "The pipe is being closed".
10+
return errors.Is(err, syscall.Errno(232))
11+
}

pkg/iostreams/iostreams.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ import (
2424

2525
const DefaultWidth = 80
2626

27+
// ErrClosedPagerPipe is the error returned when writing to a pager that has been closed.
28+
type ErrClosedPagerPipe struct {
29+
error
30+
}
31+
2732
type IOStreams struct {
2833
In io.ReadCloser
2934
Out io.Writer
@@ -197,7 +202,7 @@ func (s *IOStreams) StartPager() error {
197202
if err != nil {
198203
return err
199204
}
200-
s.Out = pagedOut
205+
s.Out = &pagerWriter{pagedOut}
201206
err = pagerCmd.Start()
202207
if err != nil {
203208
return err
@@ -211,7 +216,7 @@ func (s *IOStreams) StopPager() {
211216
return
212217
}
213218

214-
_ = s.Out.(io.ReadCloser).Close()
219+
_ = s.Out.(io.WriteCloser).Close()
215220
_, _ = s.pagerProcess.Wait()
216221
s.pagerProcess = nil
217222
}
@@ -430,3 +435,16 @@ func terminalSize(w io.Writer) (int, int, error) {
430435
}
431436
return 0, 0, fmt.Errorf("%v is not a file", w)
432437
}
438+
439+
// pagerWriter implements a WriteCloser that wraps all EPIPE errors in an ErrClosedPagerPipe type.
440+
type pagerWriter struct {
441+
io.WriteCloser
442+
}
443+
444+
func (w *pagerWriter) Write(d []byte) (int, error) {
445+
n, err := w.WriteCloser.Write(d)
446+
if err != nil && (errors.Is(err, io.ErrClosedPipe) || isEpipeError(err)) {
447+
return n, &ErrClosedPagerPipe{err}
448+
}
449+
return n, err
450+
}

0 commit comments

Comments
 (0)