Skip to content

Commit c617fbc

Browse files
committed
jq: bump up gojq, better query parse error, handle halt error gracefully
1 parent 288843f commit c617fbc

File tree

4 files changed

+102
-18
lines changed

4 files changed

+102
-18
lines changed

go.mod

+5-5
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ require (
1111
github.com/cli/shurcooL-graphql v0.0.4
1212
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
1313
github.com/henvic/httpretty v0.0.6
14-
github.com/itchyny/gojq v0.12.13
14+
github.com/itchyny/gojq v0.12.15
1515
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d
1616
github.com/muesli/reflow v0.3.0
1717
github.com/muesli/termenv v0.13.0
1818
github.com/stretchr/testify v1.7.0
1919
github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e
20-
golang.org/x/sys v0.13.0
20+
golang.org/x/sys v0.18.0
2121
golang.org/x/term v0.13.0
2222
golang.org/x/text v0.13.0
2323
gopkg.in/h2non/gock.v1 v1.1.2
@@ -37,12 +37,12 @@ require (
3737
github.com/kr/pretty v0.1.0 // indirect
3838
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
3939
github.com/mattn/go-colorable v0.1.13 // indirect
40-
github.com/mattn/go-isatty v0.0.19 // indirect
41-
github.com/mattn/go-runewidth v0.0.14 // indirect
40+
github.com/mattn/go-isatty v0.0.20 // indirect
41+
github.com/mattn/go-runewidth v0.0.15 // indirect
4242
github.com/microcosm-cc/bluemonday v1.0.26 // indirect
4343
github.com/olekukonko/tablewriter v0.0.5 // indirect
4444
github.com/pmezard/go-difflib v1.0.0 // indirect
45-
github.com/rivo/uniseg v0.4.4 // indirect
45+
github.com/rivo/uniseg v0.4.7 // indirect
4646
github.com/yuin/goldmark v1.5.2 // indirect
4747
github.com/yuin/goldmark-emoji v1.0.1 // indirect
4848
golang.org/x/net v0.17.0 // indirect

go.sum

+10-9
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ github.com/henvic/httpretty v0.0.6 h1:JdzGzKZBajBfnvlMALXXMVQWxWMF/ofTy8C3/OSUTx
3535
github.com/henvic/httpretty v0.0.6/go.mod h1:X38wLjWXHkXT7r2+uK8LjCMne9rsuNaBLJ+5cU2/Pmo=
3636
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
3737
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
38-
github.com/itchyny/gojq v0.12.13 h1:IxyYlHYIlspQHHTE0f3cJF0NKDMfajxViuhBLnHd/QU=
39-
github.com/itchyny/gojq v0.12.13/go.mod h1:JzwzAqenfhrPUuwbmEz3nu3JQmFLlQTQMUcOdnu/Sf4=
38+
github.com/itchyny/gojq v0.12.15 h1:WC1Nxbx4Ifw5U2oQWACYz32JK8G9qxNtHzrvW4KEcqI=
39+
github.com/itchyny/gojq v0.12.15/go.mod h1:uWAHCbCIla1jiNxmeT5/B5mOjSdfkCq6p8vxWg+BM10=
4040
github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE=
4141
github.com/itchyny/timefmt-go v0.1.5/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8=
4242
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
@@ -53,12 +53,13 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec
5353
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
5454
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
5555
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
56-
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
57-
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
56+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
57+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
5858
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
5959
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
60-
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
6160
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
61+
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
62+
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
6263
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
6364
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
6465
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
@@ -77,8 +78,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
7778
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
7879
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
7980
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
80-
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
81-
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
81+
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
82+
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
8283
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
8384
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
8485
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
@@ -112,8 +113,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
112113
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
113114
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
114115
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
115-
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
116-
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
116+
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
117+
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
117118
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
118119
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
119120
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=

pkg/jq/jq.go

+28
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ package jq
44
import (
55
"bytes"
66
"encoding/json"
7+
"errors"
78
"fmt"
89
"io"
910
"math"
1011
"os"
1112
"strconv"
13+
"strings"
1214

1315
"github.com/cli/go-gh/v2/pkg/jsonpretty"
1416
"github.com/itchyny/gojq"
@@ -29,6 +31,14 @@ func Evaluate(input io.Reader, output io.Writer, expr string) error {
2931
func EvaluateFormatted(input io.Reader, output io.Writer, expr string, indent string, colorize bool) error {
3032
query, err := gojq.Parse(expr)
3133
if err != nil {
34+
var e *gojq.ParseError
35+
if errors.As(err, &e) {
36+
str, line, column := getLineColumn(expr, e.Offset-len(e.Token))
37+
return fmt.Errorf(
38+
"failed to parse jq expression (line %d, column %d)\n %s\n %*c %w",
39+
line, column, str, column, '^', err,
40+
)
41+
}
3242
return err
3343
}
3444

@@ -65,6 +75,10 @@ func EvaluateFormatted(input io.Reader, output io.Writer, expr string, indent st
6575
break
6676
}
6777
if err, isErr := v.(error); isErr {
78+
var e *gojq.HaltError
79+
if errors.As(err, &e) && e.Value() == nil {
80+
break
81+
}
6882
return err
6983
}
7084
if text, e := jsonScalarToString(v); e == nil {
@@ -129,3 +143,17 @@ func (p prettyEncoder) Encode(v any) error {
129143
}
130144
return jsonpretty.Format(p.w, bytes.NewReader(b), p.indent, true)
131145
}
146+
147+
func getLineColumn(expr string, offset int) (string, int, int) {
148+
for line := 1; ; line++ {
149+
index := strings.Index(expr, "\n")
150+
if index < 0 {
151+
return expr, line, offset + 1
152+
}
153+
if index >= offset {
154+
return expr[:index], line, offset + 1
155+
}
156+
expr = expr[index+1:]
157+
offset -= index + 1
158+
}
159+
}

pkg/jq/jq_test.go

+59-4
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ func TestEvaluateFormatted(t *testing.T) {
1919
colorize bool
2020
}
2121
tests := []struct {
22-
name string
23-
args args
24-
wantW string
25-
wantErr bool
22+
name string
23+
args args
24+
wantW string
25+
wantErr bool
26+
wantErrMsg string
2627
}{
2728
{
2829
name: "simple",
@@ -171,13 +172,67 @@ func TestEvaluateFormatted(t *testing.T) {
171172
" \x1b[32m\"bar\"\x1b[m\n" +
172173
"\x1b[1;38m}\x1b[m\n",
173174
},
175+
{
176+
name: "halt function",
177+
args: args{
178+
json: strings.NewReader("{}"),
179+
expr: `1,halt,2`,
180+
},
181+
wantW: "1\n",
182+
},
183+
{
184+
name: "halt_error function",
185+
args: args{
186+
json: strings.NewReader("{}"),
187+
expr: `1,halt_error,2`,
188+
},
189+
wantW: "1\n",
190+
wantErr: true,
191+
wantErrMsg: "halt error: {}",
192+
},
193+
{
194+
name: "invalid one-line query",
195+
args: args{
196+
json: strings.NewReader("{}"),
197+
expr: `[1,2,,3]`,
198+
},
199+
wantErr: true,
200+
wantErrMsg: `failed to parse jq expression (line 1, column 6)
201+
[1,2,,3]
202+
^ unexpected token ","`,
203+
},
204+
{
205+
name: "invalid multi-line query",
206+
args: args{
207+
json: strings.NewReader("{}"),
208+
expr: `[
209+
1,,2
210+
,3]`,
211+
},
212+
wantErr: true,
213+
wantErrMsg: `failed to parse jq expression (line 2, column 5)
214+
1,,2
215+
^ unexpected token ","`,
216+
},
217+
{
218+
name: "invalid unterminated query",
219+
args: args{
220+
json: strings.NewReader("{}"),
221+
expr: `[1,`,
222+
},
223+
wantErr: true,
224+
wantErrMsg: `failed to parse jq expression (line 1, column 4)
225+
[1,
226+
^ unexpected EOF`,
227+
},
174228
}
175229
for _, tt := range tests {
176230
t.Run(tt.name, func(t *testing.T) {
177231
w := &bytes.Buffer{}
178232
err := EvaluateFormatted(tt.args.json, w, tt.args.expr, tt.args.indent, tt.args.colorize)
179233
if tt.wantErr {
180234
assert.Error(t, err)
235+
assert.EqualError(t, err, tt.wantErrMsg)
181236
return
182237
}
183238
assert.NoError(t, err)

0 commit comments

Comments
 (0)