diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 017bd002c32e39a3f620f98c21812b8e06d26754..3a27231ed492836d8db27e7ae3c05365a608c07a 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -11,7 +11,7 @@ jobs: with-go: strategy: matrix: - go-version: [1.24.2] + go-version: [1.25.x] platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} steps: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 90104e21bfc7025b78ca284f8b5a0f4d72fdd088..fc960b64b4eec1da6946c7f5217c82411e3ddbca 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -30,7 +30,7 @@ jobs: - name: Set up Go uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 with: - go-version: 1.24.2 + go-version: 1.25.x - name: Build run: make diff --git a/.github/workflows/trunk.yml b/.github/workflows/trunk.yml index 7b57dcf806037b0b70f061d68df5915e650a978d..e1f411f36c4a42a1ea9a13a23660d92af73150d5 100644 --- a/.github/workflows/trunk.yml +++ b/.github/workflows/trunk.yml @@ -33,7 +33,7 @@ jobs: - uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 with: - go-version: 1.24.2 + go-version: 1.25.x - name: Run benchmark run: go test -v ./... -bench=. -run=xxx -benchmem | tee output.txt diff --git a/api/graphql/handler.go b/api/graphql/handler.go index 201bea8c62e10d06da4d76a56c1583774d9fefbc..2410453c1a376b68972bce11af30c6266a71a0e9 100644 --- a/api/graphql/handler.go +++ b/api/graphql/handler.go @@ -20,13 +20,7 @@ import ( "github.com/git-bug/git-bug/cache" ) -// Handler is the root GraphQL http handler -type Handler struct { - http.Handler - io.Closer -} - -func NewHandler(mrc *cache.MultiRepoCache, errorOut io.Writer) Handler { +func NewHandler(mrc *cache.MultiRepoCache, errorOut io.Writer) http.Handler { rootResolver := resolvers.NewRootResolver(mrc) config := graph.Config{Resolvers: rootResolver} @@ -54,8 +48,5 @@ func NewHandler(mrc *cache.MultiRepoCache, errorOut io.Writer) Handler { h.Use(&Tracer{Out: errorOut}) } - return Handler{ - Handler: h, - Closer: rootResolver, - } + return h } diff --git a/commands/webui.go b/commands/webui.go index 3e2488a99a6eda3cfe877c7c8590cd0ac9fafba2..7f8db1b00e9660abf90279adf0d9942b5e1ccae7 100644 --- a/commands/webui.go +++ b/commands/webui.go @@ -30,7 +30,7 @@ import ( const webUIOpenConfigKey = "git-bug.webui.open" type webUIOptions struct { - host string + bind string port int open bool noOpen bool @@ -59,10 +59,10 @@ Available git config: flags := cmd.Flags() flags.SortFlags = false - flags.StringVar(&options.host, "host", "127.0.0.1", "Network address or hostname to listen to (default to 127.0.0.1)") + flags.StringVar(&options.bind, "bind", "127.0.0.1", "Network address to bind to (default to 127.0.0.1)") + flags.IntVarP(&options.port, "port", "p", 0, "Port to listen on (default to random available port)") flags.BoolVar(&options.open, "open", false, "Automatically open the web UI in the default browser") flags.BoolVar(&options.noOpen, "no-open", false, "Prevent the automatic opening of the web UI in the default browser") - flags.IntVarP(&options.port, "port", "p", 0, "Port to listen to (default to random available port)") flags.BoolVar(&options.readOnly, "read-only", false, "Whether to run the web UI in read-only mode") flags.BoolVar(&options.logErrors, "log-errors", false, "Whether to log errors") flags.StringVarP(&options.query, "query", "q", "", "The query to open in the web UI bug list") @@ -70,24 +70,8 @@ Available git config: return cmd } -func runWebUI(env *execenv.Env, opts webUIOptions) error { - if opts.port == 0 { - var err error - opts.port, err = freeport.GetFreePort() - if err != nil { - return err - } - } - - addr := net.JoinHostPort(opts.host, strconv.Itoa(opts.port)) - webUiAddr := fmt.Sprintf("http://%s", addr) - toOpen := webUiAddr - - if len(opts.query) > 0 { - // Explicitly set the query parameter instead of going with a default one. - toOpen = fmt.Sprintf("%s/?q=%s", webUiAddr, url.QueryEscape(opts.query)) - } - +// setupRoutes builds the router and registers all API and UI routes. +func setupRoutes(env *execenv.Env, opts webUIOptions) (*mux.Router, func() error, error) { router := mux.NewRouter() // If the webUI is not read-only, use an authentication middleware with a @@ -96,18 +80,15 @@ func runWebUI(env *execenv.Env, opts webUIOptions) error { if !opts.readOnly { author, err := identity.GetUserIdentity(env.Repo) if err != nil { - return err + return nil, nil, err } router.Use(auth.Middleware(author.Id())) } mrc := cache.NewMultiRepoCache() - _, events := mrc.RegisterDefaultRepository(env.Repo) - - err := execenv.CacheBuildProgressBar(env, events) - if err != nil { - return err + if err := execenv.CacheBuildProgressBar(env, events); err != nil { + return nil, nil, err } var errOut io.Writer @@ -117,46 +98,46 @@ func runWebUI(env *execenv.Env, opts webUIOptions) error { graphqlHandler := graphql.NewHandler(mrc, errOut) - // Routes router.Path("/playground").Handler(playground.Handler("git-bug", "/graphql")) router.Path("/graphql").Handler(graphqlHandler) router.Path("/gitfile/{repo}/{hash}").Handler(httpapi.NewGitFileHandler(mrc)) router.Path("/upload/{repo}").Methods("POST").Handler(httpapi.NewGitUploadFileHandler(mrc)) router.PathPrefix("/").Handler(webui.NewHandler()) - srv := &http.Server{ - Addr: addr, - Handler: router, - } - - done := make(chan bool) - - go func() { - <-env.Ctx.Done() - env.Out.Println("shutting down...") - - ctxTeardown, cancelTeardown := context.WithTimeout(context.Background(), 30*time.Second) - defer cancelTeardown() + return router, mrc.Close, nil +} - srv.SetKeepAlivesEnabled(false) - if err := srv.Shutdown(ctxTeardown); err != nil { - env.Err.Printf("Could not gracefully shutdown the WebUI: %v\n", err) +func runWebUI(env *execenv.Env, opts webUIOptions) error { + router, closeRoutes, err := setupRoutes(env, opts) + if err != nil { + return err + } + defer func() { + if err := closeRoutes(); err != nil { + env.Err.Println(err) } + }() - // Teardown - err := graphqlHandler.Close() + if opts.port == 0 { + opts.port, err = freeport.GetFreePort() if err != nil { - env.Err.Println(err) + return err } + } - close(done) - }() + addr := net.JoinHostPort(opts.bind, strconv.Itoa(opts.port)) + server := &http.Server{Addr: addr, Handler: router} + baseURL := "http://" + addr - env.Out.Printf("Web UI: %s\n", webUiAddr) - env.Out.Printf("Graphql API: http://%s/graphql\n", addr) - env.Out.Printf("Graphql Playground: http://%s/playground\n", addr) + env.Out.Printf("Web UI: %s\n", baseURL) + env.Out.Printf("Graphql API: %s/graphql\n", baseURL) + env.Out.Printf("Graphql Playground: %s/playground\n", baseURL) env.Out.Printf("\n[ Press Ctrl+c to quit ]\n\n") + toOpen := baseURL + if len(opts.query) > 0 { + toOpen = fmt.Sprintf("%s/?q=%s", baseURL, url.QueryEscape(opts.query)) + } configOpen, err := env.Repo.AnyConfig().ReadBool(webUIOpenConfigKey) if errors.Is(err, repository.ErrNoConfigEntry) { // default to true @@ -164,40 +145,44 @@ func runWebUI(env *execenv.Env, opts webUIOptions) error { } else if err != nil { return err } - - shouldOpen := (configOpen && !opts.noOpen) || opts.open - - if shouldOpen { - go func() { - const maxAttempts = 3 - if isUp(toOpen, maxAttempts, 3*time.Second) { - err = open.Run(toOpen) - if err != nil { - env.Err.Println(err) - return - } - - env.Out.Printf("opened your default browser to url: %s\n", toOpen) - return - } - - env.Err.Printf( - "uh oh! it appears that the http server hasn't started.\n"+ - "we failed to reach %s after %d attempts, exiting now.\n", - toOpen, maxAttempts, - ) - }() + if (configOpen && !opts.noOpen) || opts.open { + go openWhenUp(env, toOpen) } - err = srv.ListenAndServe() - if err != nil && err != http.ErrServerClosed { + go func() { + <-env.Ctx.Done() + env.Out.Println("shutting down...") + shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + server.SetKeepAlivesEnabled(false) + if err := server.Shutdown(shutdownCtx); err != nil { + env.Err.Printf("Could not gracefully shutdown the HTTP server: %v\n", err) + } + }() + + if err := server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) { return err } - - <-done return nil } +func openWhenUp(env *execenv.Env, toOpen string) { + const maxAttempts = 3 + if isUp(toOpen, maxAttempts, 3*time.Second) { + if err := open.Run(toOpen); err != nil { + env.Err.Println(err) + return + } + env.Out.Printf("opened your default browser to url: %s\n", toOpen) + return + } + env.Err.Printf( + "uh oh! it appears that the http server hasn't started.\n"+ + "we failed to reach %s after %d attempts.\n", + toOpen, maxAttempts, + ) +} + func isUp(url string, maxRetries int, initialDelay time.Duration) bool { client := &http.Client{ Timeout: 5 * time.Second, diff --git a/go.mod b/go.mod index 0c4c9c103460306cf3ba1e288681441e22a4c9da..9157e778ea8bb1b4474760241ca9617db478b970 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,6 @@ module github.com/git-bug/git-bug -go 1.24.0 - -toolchain go1.24.2 +go 1.25.0 require ( github.com/99designs/gqlgen v0.17.73 @@ -32,17 +30,19 @@ require ( github.com/vbauerster/mpb/v8 v8.8.2 github.com/vektah/gqlparser/v2 v2.5.26 gitlab.com/gitlab-org/api/client-go v0.116.0 - golang.org/x/crypto v0.45.0 - golang.org/x/mod v0.29.0 - golang.org/x/net v0.47.0 - golang.org/x/oauth2 v0.27.0 - golang.org/x/sync v0.18.0 - golang.org/x/sys v0.38.0 - golang.org/x/term v0.37.0 - golang.org/x/text v0.31.0 + golang.org/x/crypto v0.49.0 + golang.org/x/mod v0.34.0 + golang.org/x/net v0.52.0 + golang.org/x/oauth2 v0.36.0 + golang.org/x/sync v0.20.0 + golang.org/x/sys v0.42.0 + golang.org/x/term v0.41.0 + golang.org/x/text v0.35.0 golang.org/x/vuln v1.1.3 ) +tool github.com/99designs/gqlgen + require ( dario.cat/mergo v1.0.1 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect @@ -113,14 +113,12 @@ require ( github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect go.etcd.io/bbolt v1.4.0 // indirect - golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 // indirect + golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.38.0 // indirect + golang.org/x/tools v0.42.0 // indirect golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated // indirect golang.org/x/tools/godoc v0.1.0-deprecated // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) - -tool github.com/99designs/gqlgen diff --git a/go.sum b/go.sum index 37b5954cec89a26a1495f40a3f9d71ffaaf3b8a9..303df925bc009914af3ebd995a3d089948a5bb60 100644 --- a/go.sum +++ b/go.sum @@ -257,28 +257,28 @@ go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= +golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= +golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= -golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= -golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -291,33 +291,33 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 h1:LvzTn0GQhWuvKH/kVRS3R3bVAsdQWI7hvfLHGgh9+lU= -golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 h1:bTLqdHv7xrGlFbvf5/TXNxy/iUwwdkjhqQTJDjW7aj0= +golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4/go.mod h1:g5NllXBEermZrmR51cJDQxmJUHUOfRAaNyWBM+R+548= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= +golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= -golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= +golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= golang.org/x/tools/go/expect v0.1.0-deprecated h1:jY2C5HGYR5lqex3gEniOQL0r7Dq5+VGVgY1nudX5lXY= golang.org/x/tools/go/expect v0.1.0-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM=