xmain: rm -rf
This commit is contained in:
parent
820226c72b
commit
07e316fac5
18 changed files with 36 additions and 761 deletions
|
|
@ -3,8 +3,9 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"oss.terrastruct.com/util-go/xmain"
|
||||||
|
|
||||||
"oss.terrastruct.com/d2/d2plugin"
|
"oss.terrastruct.com/d2/d2plugin"
|
||||||
"oss.terrastruct.com/d2/lib/xmain"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,9 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
"oss.terrastruct.com/d2/d2graph"
|
|
||||||
"oss.terrastruct.com/util-go/xexec"
|
"oss.terrastruct.com/util-go/xexec"
|
||||||
|
|
||||||
|
"oss.terrastruct.com/d2/d2graph"
|
||||||
)
|
)
|
||||||
|
|
||||||
// plugins contains the bundled d2 plugins.
|
// plugins contains the bundled d2 plugins.
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"oss.terrastruct.com/util-go/xmain"
|
||||||
|
|
||||||
"oss.terrastruct.com/d2/d2graph"
|
"oss.terrastruct.com/d2/d2graph"
|
||||||
"oss.terrastruct.com/d2/lib/xmain"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Serve returns a xmain.RunFunc that will invoke the plugin p as necessary to service the
|
// Serve returns a xmain.RunFunc that will invoke the plugin p as necessary to service the
|
||||||
|
|
|
||||||
3
fmt.go
3
fmt.go
|
|
@ -6,9 +6,10 @@ import (
|
||||||
|
|
||||||
"oss.terrastruct.com/xdefer"
|
"oss.terrastruct.com/xdefer"
|
||||||
|
|
||||||
|
"oss.terrastruct.com/util-go/xmain"
|
||||||
|
|
||||||
"oss.terrastruct.com/d2/d2format"
|
"oss.terrastruct.com/d2/d2format"
|
||||||
"oss.terrastruct.com/d2/d2parser"
|
"oss.terrastruct.com/d2/d2parser"
|
||||||
"oss.terrastruct.com/d2/lib/xmain"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func fmtCmd(ctx context.Context, ms *xmain.State) (err error) {
|
func fmtCmd(ctx context.Context, ms *xmain.State) (err error) {
|
||||||
|
|
|
||||||
7
go.mod
generated
7
go.mod
generated
|
|
@ -17,17 +17,13 @@ require (
|
||||||
go.uber.org/multierr v1.8.0
|
go.uber.org/multierr v1.8.0
|
||||||
golang.org/x/image v0.1.0
|
golang.org/x/image v0.1.0
|
||||||
golang.org/x/net v0.2.0
|
golang.org/x/net v0.2.0
|
||||||
golang.org/x/text v0.4.0
|
|
||||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2
|
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2
|
||||||
gonum.org/v1/plot v0.12.0
|
gonum.org/v1/plot v0.12.0
|
||||||
nhooyr.io/websocket v1.8.7
|
nhooyr.io/websocket v1.8.7
|
||||||
oss.terrastruct.com/cmdlog v0.0.0-20221201100934-012c01b3431c
|
|
||||||
oss.terrastruct.com/diff v1.0.2-0.20221116222035-8bf4dd3ab541
|
oss.terrastruct.com/diff v1.0.2-0.20221116222035-8bf4dd3ab541
|
||||||
oss.terrastruct.com/util-go v0.0.0-20221201190418-569dcbf6dc3f
|
oss.terrastruct.com/util-go v0.0.0-20221201191904-5edc89ce397b
|
||||||
oss.terrastruct.com/xcontext v0.0.0-20221018000442-50fdafb12f4f
|
|
||||||
oss.terrastruct.com/xdefer v0.0.0-20221017222355-6f3b6e4d1557
|
oss.terrastruct.com/xdefer v0.0.0-20221017222355-6f3b6e4d1557
|
||||||
oss.terrastruct.com/xjson v0.0.0-20221018000420-4986731c4c4a
|
oss.terrastruct.com/xjson v0.0.0-20221018000420-4986731c4c4a
|
||||||
oss.terrastruct.com/xos v0.0.0-20221130233107-5fb84d57c9e3
|
|
||||||
oss.terrastruct.com/xrand v0.0.0-20221020211818-4ac08e618333
|
oss.terrastruct.com/xrand v0.0.0-20221020211818-4ac08e618333
|
||||||
rogchap.com/v8go v0.7.1-0.20221102201510-1f00b5007d95
|
rogchap.com/v8go v0.7.1-0.20221102201510-1f00b5007d95
|
||||||
)
|
)
|
||||||
|
|
@ -59,6 +55,7 @@ require (
|
||||||
golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9 // indirect
|
golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9 // indirect
|
||||||
golang.org/x/sys v0.2.0 // indirect
|
golang.org/x/sys v0.2.0 // indirect
|
||||||
golang.org/x/term v0.2.0 // indirect
|
golang.org/x/term v0.2.0 // indirect
|
||||||
|
golang.org/x/text v0.4.0 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc // indirect
|
google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc // indirect
|
||||||
google.golang.org/protobuf v1.28.1 // indirect
|
google.golang.org/protobuf v1.28.1 // indirect
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||||
|
|
|
||||||
12
go.sum
generated
12
go.sum
generated
|
|
@ -798,22 +798,14 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
|
||||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
|
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
|
||||||
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
||||||
oss.terrastruct.com/cmdlog v0.0.0-20221201100934-012c01b3431c h1:C1DDLzj2NrVi1YJpbZluEYZ2MkpJGppZYfSQ+F87TT0=
|
|
||||||
oss.terrastruct.com/cmdlog v0.0.0-20221201100934-012c01b3431c/go.mod h1:C8u/lYTvQWc1xC7rHpgFfpScfQC4NMeGGMmlKVZZUXM=
|
|
||||||
oss.terrastruct.com/diff v1.0.2-0.20221116222035-8bf4dd3ab541 h1:I9B1O1IJ6spivIQxbFRZmbhAwVeLwrcQRR1JbYUOvrI=
|
oss.terrastruct.com/diff v1.0.2-0.20221116222035-8bf4dd3ab541 h1:I9B1O1IJ6spivIQxbFRZmbhAwVeLwrcQRR1JbYUOvrI=
|
||||||
oss.terrastruct.com/diff v1.0.2-0.20221116222035-8bf4dd3ab541/go.mod h1:ags2QDy/T6jr69hT6bpmAmhr2H98n9o8Atf3QlUJPiU=
|
oss.terrastruct.com/diff v1.0.2-0.20221116222035-8bf4dd3ab541/go.mod h1:ags2QDy/T6jr69hT6bpmAmhr2H98n9o8Atf3QlUJPiU=
|
||||||
oss.terrastruct.com/util-go v0.0.0-20221201185848-8cc30ca56bbe h1:1CTXmBqea1vbVhYsyZ3NiCYUFZgURIQF/ItjrdlhwlE=
|
oss.terrastruct.com/util-go v0.0.0-20221201191904-5edc89ce397b h1:o8+5KfZpQyaw7uKcPIdc9HOqVjVDEdsPZpdRV1k0rmc=
|
||||||
oss.terrastruct.com/util-go v0.0.0-20221201185848-8cc30ca56bbe/go.mod h1:bL3CBn27CtTm++1iqRh2p6f8AIWeTdtlTN199Kg9JYM=
|
oss.terrastruct.com/util-go v0.0.0-20221201191904-5edc89ce397b/go.mod h1:Fwy72FDIOOM4K8F96ScXkxHHppR1CPfUyo9+x9c1PBU=
|
||||||
oss.terrastruct.com/util-go v0.0.0-20221201190418-569dcbf6dc3f h1:ADVDj5TeMMxXMEKAxC9RKLCQq4gaW6GKynAeojB9VQQ=
|
|
||||||
oss.terrastruct.com/util-go v0.0.0-20221201190418-569dcbf6dc3f/go.mod h1:bL3CBn27CtTm++1iqRh2p6f8AIWeTdtlTN199Kg9JYM=
|
|
||||||
oss.terrastruct.com/xcontext v0.0.0-20221018000442-50fdafb12f4f h1:7voRCwKM7TZkTo9u7hj+uV/zXoVB8czWrTq6MVIh3dg=
|
|
||||||
oss.terrastruct.com/xcontext v0.0.0-20221018000442-50fdafb12f4f/go.mod h1:Y0coTLsWwX0q3a+/Ndq797t+vWyxm42T49Ik3bzaDKY=
|
|
||||||
oss.terrastruct.com/xdefer v0.0.0-20221017222355-6f3b6e4d1557 h1:rPbhJbN1q7B4tnppSPoAMwq0t6Pk5SrQDQ5S6uoNNHg=
|
oss.terrastruct.com/xdefer v0.0.0-20221017222355-6f3b6e4d1557 h1:rPbhJbN1q7B4tnppSPoAMwq0t6Pk5SrQDQ5S6uoNNHg=
|
||||||
oss.terrastruct.com/xdefer v0.0.0-20221017222355-6f3b6e4d1557/go.mod h1:plvfydF5METAlsbpeuSz44jckaOwrCWX3M0kTLoCA4I=
|
oss.terrastruct.com/xdefer v0.0.0-20221017222355-6f3b6e4d1557/go.mod h1:plvfydF5METAlsbpeuSz44jckaOwrCWX3M0kTLoCA4I=
|
||||||
oss.terrastruct.com/xjson v0.0.0-20221018000420-4986731c4c4a h1:AAcupsjBwpbcyLASX0ppDlxbfHWb5Neq5gWdGpLfaSA=
|
oss.terrastruct.com/xjson v0.0.0-20221018000420-4986731c4c4a h1:AAcupsjBwpbcyLASX0ppDlxbfHWb5Neq5gWdGpLfaSA=
|
||||||
oss.terrastruct.com/xjson v0.0.0-20221018000420-4986731c4c4a/go.mod h1:XJ71qiTzk/dbTWuYbuLJuRpBdKFN06Sk5FdFpq2TNmE=
|
oss.terrastruct.com/xjson v0.0.0-20221018000420-4986731c4c4a/go.mod h1:XJ71qiTzk/dbTWuYbuLJuRpBdKFN06Sk5FdFpq2TNmE=
|
||||||
oss.terrastruct.com/xos v0.0.0-20221130233107-5fb84d57c9e3 h1:bchGZ5WryJNqr/yZ00rTcgZh1AComRFwKKBWOIxzJZE=
|
|
||||||
oss.terrastruct.com/xos v0.0.0-20221130233107-5fb84d57c9e3/go.mod h1:lUSNCN0HA3pWHOMXT6gRNJtjg1U5t0TEEwAzPyV6enA=
|
|
||||||
oss.terrastruct.com/xrand v0.0.0-20221020211818-4ac08e618333 h1:7EdxwXM75Id1VIN71QbE8bLzZRMs0qD7olnDw5gbI7w=
|
oss.terrastruct.com/xrand v0.0.0-20221020211818-4ac08e618333 h1:7EdxwXM75Id1VIN71QbE8bLzZRMs0qD7olnDw5gbI7w=
|
||||||
oss.terrastruct.com/xrand v0.0.0-20221020211818-4ac08e618333/go.mod h1:O7TAoBmlQhoi46RdgVikDcoLRb/vLflhkXCAd+nO4SM=
|
oss.terrastruct.com/xrand v0.0.0-20221020211818-4ac08e618333/go.mod h1:O7TAoBmlQhoi46RdgVikDcoLRb/vLflhkXCAd+nO4SM=
|
||||||
rogchap.com/v8go v0.7.1-0.20221102201510-1f00b5007d95 h1:r89YHVIWeQj/A3Nu6462eqARUECJlJkLRk36pfML1xA=
|
rogchap.com/v8go v0.7.1-0.20221102201510-1f00b5007d95 h1:r89YHVIWeQj/A3Nu6462eqARUECJlJkLRk36pfML1xA=
|
||||||
|
|
|
||||||
3
help.go
3
help.go
|
|
@ -9,8 +9,9 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"oss.terrastruct.com/util-go/xmain"
|
||||||
|
|
||||||
"oss.terrastruct.com/d2/d2plugin"
|
"oss.terrastruct.com/d2/d2plugin"
|
||||||
"oss.terrastruct.com/d2/lib/xmain"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func help(ms *xmain.State) {
|
func help(ms *xmain.State) {
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import (
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
"oss.terrastruct.com/xdefer"
|
"oss.terrastruct.com/xdefer"
|
||||||
|
|
||||||
"oss.terrastruct.com/d2/lib/xmain"
|
"oss.terrastruct.com/util-go/xmain"
|
||||||
)
|
)
|
||||||
|
|
||||||
const maxImageSize int64 = 1 << 25 // 33_554_432
|
const maxImageSize int64 = 1 << 25 // 33_554_432
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,10 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"oss.terrastruct.com/cmdlog"
|
"oss.terrastruct.com/util-go/cmdlog"
|
||||||
"oss.terrastruct.com/xos"
|
"oss.terrastruct.com/util-go/xos"
|
||||||
|
|
||||||
"oss.terrastruct.com/d2/lib/xmain"
|
"oss.terrastruct.com/util-go/xmain"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed test_png.png
|
//go:embed test_png.png
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
"github.com/playwright-community/playwright-go"
|
"github.com/playwright-community/playwright-go"
|
||||||
|
|
||||||
"oss.terrastruct.com/d2/lib/xmain"
|
"oss.terrastruct.com/util-go/xmain"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Playwright struct {
|
type Playwright struct {
|
||||||
|
|
|
||||||
165
lib/xhttp/err.go
165
lib/xhttp/err.go
|
|
@ -1,165 +0,0 @@
|
||||||
package xhttp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"oss.terrastruct.com/cmdlog"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Error represents an HTTP error.
|
|
||||||
// It's exported only for comparison in tests.
|
|
||||||
type Error struct {
|
|
||||||
Code int
|
|
||||||
Resp interface{}
|
|
||||||
Err error
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ interface {
|
|
||||||
Is(error) bool
|
|
||||||
Unwrap() error
|
|
||||||
} = Error{}
|
|
||||||
|
|
||||||
// Errorf creates a new error with code, resp, msg and v.
|
|
||||||
//
|
|
||||||
// When returned from an xhttp.HandlerFunc, it will be correctly logged
|
|
||||||
// and written to the connection. See xhttp.WrapHandlerFunc
|
|
||||||
func Errorf(code int, resp interface{}, msg string, v ...interface{}) error {
|
|
||||||
return errorWrap(code, resp, fmt.Errorf(msg, v...))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrorWrap wraps err with the code and resp for xhttp.HandlerFunc.
|
|
||||||
//
|
|
||||||
// When returned from an xhttp.HandlerFunc, it will be correctly logged
|
|
||||||
// and written to the connection. See xhttp.WrapHandlerFunc
|
|
||||||
func ErrorWrap(code int, resp interface{}, err error) error {
|
|
||||||
return errorWrap(code, resp, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func errorWrap(code int, resp interface{}, err error) error {
|
|
||||||
if resp == nil {
|
|
||||||
resp = http.StatusText(code)
|
|
||||||
}
|
|
||||||
return Error{code, resp, err}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e Error) Unwrap() error {
|
|
||||||
return e.Err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e Error) Is(err error) bool {
|
|
||||||
e2, ok := err.(Error)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return e.Code == e2.Code && e.Resp == e2.Resp && errors.Is(e.Err, e2.Err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e Error) Error() string {
|
|
||||||
return fmt.Sprintf("http error with code %v and resp %#v: %v", e.Code, e.Resp, e.Err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandlerFunc is like http.HandlerFunc but returns an error.
|
|
||||||
// See Errorf and ErrorWrap.
|
|
||||||
type HandlerFunc func(w http.ResponseWriter, r *http.Request) error
|
|
||||||
|
|
||||||
type HandlerFuncAdapter struct {
|
|
||||||
Log *cmdlog.Logger
|
|
||||||
Func HandlerFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServeHTTP adapts xhttp.HandlerFunc into http.Handler for usage with standard
|
|
||||||
// HTTP routers like chi.
|
|
||||||
//
|
|
||||||
// It logs and writes any error from xhttp.HandlerFunc to the connection.
|
|
||||||
//
|
|
||||||
// If err was created with xhttp.Errorf or wrapped with xhttp.WrapError, then the error
|
|
||||||
// will be logged at the correct level for the status code and xhttp.JSON will be called
|
|
||||||
// with the code and resp.
|
|
||||||
//
|
|
||||||
// 400s are logged as warns and 500s as errors.
|
|
||||||
//
|
|
||||||
// If the error was not created with the xhttp helpers then a 500 will be written.
|
|
||||||
//
|
|
||||||
// If resp is nil, then resp is set to http.StatusText(code)
|
|
||||||
//
|
|
||||||
// If the code is not a 400 or a 500, then an error about about the unexpected error code
|
|
||||||
// will be logged and a 500 will be written. The original error will also be logged.
|
|
||||||
func (a HandlerFuncAdapter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
var h http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
err := a.Func(w, r)
|
|
||||||
if err != nil {
|
|
||||||
handleError(a.Log, w, err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
h.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleError(clog *cmdlog.Logger, w http.ResponseWriter, err error) {
|
|
||||||
var herr Error
|
|
||||||
ok := errors.As(err, &herr)
|
|
||||||
if !ok {
|
|
||||||
herr = ErrorWrap(http.StatusInternalServerError, nil, err).(Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
var logger *log.Logger
|
|
||||||
switch {
|
|
||||||
case 400 <= herr.Code && herr.Code < 500:
|
|
||||||
logger = clog.Warn
|
|
||||||
case 500 <= herr.Code && herr.Code < 600:
|
|
||||||
logger = clog.Error
|
|
||||||
default:
|
|
||||||
logger = clog.Error
|
|
||||||
|
|
||||||
clog.Error.Printf("unexpected non error http status code %d with resp: %#v", herr.Code, herr.Resp)
|
|
||||||
|
|
||||||
herr.Code = http.StatusInternalServerError
|
|
||||||
herr.Resp = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if herr.Resp == nil {
|
|
||||||
herr.Resp = http.StatusText(herr.Code)
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Printf("error handling http request: %v", err)
|
|
||||||
|
|
||||||
ww, ok := w.(writtenResponseWriter)
|
|
||||||
if !ok {
|
|
||||||
clog.Warn.Printf("response writer does not implement Written, double write logs possible: %#v", w)
|
|
||||||
} else if ww.Written() {
|
|
||||||
// Avoid double writes if an error occurred while the response was
|
|
||||||
// being written.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON(clog, w, herr.Code, map[string]interface{}{
|
|
||||||
"error": herr.Resp,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type writtenResponseWriter interface {
|
|
||||||
Written() bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func JSON(clog *cmdlog.Logger, w http.ResponseWriter, code int, v interface{}) {
|
|
||||||
if v == nil {
|
|
||||||
v = map[string]interface{}{
|
|
||||||
"status": http.StatusText(code),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := json.Marshal(v)
|
|
||||||
if err != nil {
|
|
||||||
clog.Error.Printf("json marshal error: %v", err)
|
|
||||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
|
||||||
w.WriteHeader(code)
|
|
||||||
_, _ = w.Write(b)
|
|
||||||
}
|
|
||||||
125
lib/xhttp/log.go
125
lib/xhttp/log.go
|
|
@ -1,125 +0,0 @@
|
||||||
package xhttp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"runtime/debug"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/text/message"
|
|
||||||
|
|
||||||
"oss.terrastruct.com/cmdlog"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ResponseWriter interface {
|
|
||||||
http.ResponseWriter
|
|
||||||
http.Hijacker
|
|
||||||
http.Flusher
|
|
||||||
writtenResponseWriter
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ ResponseWriter = &responseWriter{}
|
|
||||||
|
|
||||||
type responseWriter struct {
|
|
||||||
rw http.ResponseWriter
|
|
||||||
|
|
||||||
written bool
|
|
||||||
status int
|
|
||||||
length int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rw *responseWriter) Header() http.Header {
|
|
||||||
return rw.rw.Header()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rw *responseWriter) WriteHeader(statusCode int) {
|
|
||||||
if !rw.written {
|
|
||||||
rw.written = true
|
|
||||||
rw.status = statusCode
|
|
||||||
}
|
|
||||||
rw.rw.WriteHeader(statusCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rw *responseWriter) Write(p []byte) (int, error) {
|
|
||||||
if !rw.written && len(p) > 0 {
|
|
||||||
rw.written = true
|
|
||||||
if rw.status == 0 {
|
|
||||||
rw.status = http.StatusOK
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rw.length += len(p)
|
|
||||||
return rw.rw.Write(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rw *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
|
||||||
hj, ok := rw.rw.(http.Hijacker)
|
|
||||||
if !ok {
|
|
||||||
return nil, nil, fmt.Errorf("underlying response writer does not implement http.Hijacker: %T", rw.rw)
|
|
||||||
}
|
|
||||||
return hj.Hijack()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rw *responseWriter) Flush() {
|
|
||||||
f, ok := rw.rw.(http.Flusher)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
f.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rw *responseWriter) Written() bool {
|
|
||||||
return rw.written
|
|
||||||
}
|
|
||||||
|
|
||||||
func Log(clog *cmdlog.Logger, next http.Handler) http.Handler {
|
|
||||||
englishPrinter := message.NewPrinter(message.MatchLanguage("en"))
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
defer func() {
|
|
||||||
rec := recover()
|
|
||||||
if rec != nil {
|
|
||||||
clog.Error.Printf("caught panic: %#v\n%s", rec, debug.Stack())
|
|
||||||
JSON(clog, w, http.StatusInternalServerError, map[string]interface{}{
|
|
||||||
"error": http.StatusText(http.StatusInternalServerError),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
rw := &responseWriter{
|
|
||||||
rw: w,
|
|
||||||
}
|
|
||||||
|
|
||||||
start := time.Now()
|
|
||||||
next.ServeHTTP(rw, r)
|
|
||||||
dur := time.Since(start)
|
|
||||||
|
|
||||||
if !rw.Written() {
|
|
||||||
_, err := rw.Write(nil)
|
|
||||||
if errors.Is(err, http.ErrHijacked) {
|
|
||||||
clog.Success.Printf("%s %s %v: hijacked", r.Method, r.URL, dur)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
clog.Warn.Printf("%s %s %v: no response written", r.Method, r.URL, dur)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var statusLogger *log.Logger
|
|
||||||
switch {
|
|
||||||
case 100 <= rw.status && rw.status <= 299:
|
|
||||||
statusLogger = clog.Success
|
|
||||||
case 300 <= rw.status && rw.status <= 399:
|
|
||||||
statusLogger = clog.Info
|
|
||||||
case 400 <= rw.status && rw.status <= 499:
|
|
||||||
statusLogger = clog.Warn
|
|
||||||
case 500 <= rw.status && rw.status <= 599:
|
|
||||||
statusLogger = clog.Error
|
|
||||||
}
|
|
||||||
lengthStr := englishPrinter.Sprint(rw.length)
|
|
||||||
// TODO: make work with watch.go on hijack, not after
|
|
||||||
statusLogger.Printf("%s %s %d %sB %v", r.Method, r.URL, rw.status, lengthStr, dur)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
// Package xhttp implements http helpers.
|
|
||||||
package xhttp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"oss.terrastruct.com/xcontext"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewServer(log *log.Logger, h http.Handler) *http.Server {
|
|
||||||
return &http.Server{
|
|
||||||
MaxHeaderBytes: 1 << 18, // 262,144B
|
|
||||||
ReadTimeout: time.Minute,
|
|
||||||
WriteTimeout: time.Minute,
|
|
||||||
IdleTimeout: time.Hour,
|
|
||||||
ErrorLog: log,
|
|
||||||
Handler: http.MaxBytesHandler(h, 1<<20), // 1,048,576B
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Serve(ctx context.Context, shutdownTimeout time.Duration, s *http.Server, l net.Listener) error {
|
|
||||||
s.BaseContext = func(net.Listener) context.Context {
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
done := make(chan error, 1)
|
|
||||||
go func() {
|
|
||||||
done <- s.Serve(l)
|
|
||||||
}()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case err := <-done:
|
|
||||||
return err
|
|
||||||
case <-ctx.Done():
|
|
||||||
ctx = xcontext.WithoutCancel(ctx)
|
|
||||||
ctx, cancel := context.WithTimeout(ctx, shutdownTimeout)
|
|
||||||
defer cancel()
|
|
||||||
return s.Shutdown(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
// flag_helpers.go are private functions from pflag/flag.go
|
|
||||||
package xmain
|
|
||||||
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
func wrap(i, w int, s string) string {
|
|
||||||
if w == 0 {
|
|
||||||
return strings.Replace(s, "\n", "\n"+strings.Repeat(" ", i), -1)
|
|
||||||
}
|
|
||||||
wrap := w - i
|
|
||||||
var r, l string
|
|
||||||
if wrap < 24 {
|
|
||||||
i = 16
|
|
||||||
wrap = w - i
|
|
||||||
r += "\n" + strings.Repeat(" ", i)
|
|
||||||
}
|
|
||||||
if wrap < 24 {
|
|
||||||
return strings.Replace(s, "\n", r, -1)
|
|
||||||
}
|
|
||||||
slop := 5
|
|
||||||
wrap = wrap - slop
|
|
||||||
l, s = wrapN(wrap, slop, s)
|
|
||||||
r = r + strings.Replace(l, "\n", "\n"+strings.Repeat(" ", i), -1)
|
|
||||||
for s != "" {
|
|
||||||
var t string
|
|
||||||
t, s = wrapN(wrap, slop, s)
|
|
||||||
r = r + "\n" + strings.Repeat(" ", i) + strings.Replace(t, "\n", "\n"+strings.Repeat(" ", i), -1)
|
|
||||||
}
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func wrapN(i, slop int, s string) (string, string) {
|
|
||||||
if i+slop > len(s) {
|
|
||||||
return s, ""
|
|
||||||
}
|
|
||||||
w := strings.LastIndexAny(s[:i], " \t\n")
|
|
||||||
if w <= 0 {
|
|
||||||
return s, ""
|
|
||||||
}
|
|
||||||
nlPos := strings.LastIndex(s[:i], "\n")
|
|
||||||
if nlPos > 0 && nlPos < w {
|
|
||||||
return s[:nlPos], s[nlPos+1:]
|
|
||||||
}
|
|
||||||
return s[:w], s[w+1:]
|
|
||||||
}
|
|
||||||
|
|
@ -1,173 +0,0 @@
|
||||||
package xmain
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
"oss.terrastruct.com/cmdlog"
|
|
||||||
"oss.terrastruct.com/xos"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Opts struct {
|
|
||||||
Args []string
|
|
||||||
Flags *pflag.FlagSet
|
|
||||||
env *xos.Env
|
|
||||||
log *cmdlog.Logger
|
|
||||||
|
|
||||||
flagEnv map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewOpts(env *xos.Env, log *cmdlog.Logger, args []string) *Opts {
|
|
||||||
flags := pflag.NewFlagSet("", pflag.ContinueOnError)
|
|
||||||
flags.SortFlags = false
|
|
||||||
flags.Usage = func() {}
|
|
||||||
flags.SetOutput(io.Discard)
|
|
||||||
return &Opts{
|
|
||||||
Args: args,
|
|
||||||
Flags: flags,
|
|
||||||
env: env,
|
|
||||||
log: log,
|
|
||||||
flagEnv: make(map[string]string),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mostly copy pasted pasted from pflag.FlagUsagesWrapped
|
|
||||||
// with modifications for env var
|
|
||||||
func (o *Opts) Defaults() string {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
|
|
||||||
var lines []string
|
|
||||||
|
|
||||||
maxlen := 0
|
|
||||||
maxEnvLen := 0
|
|
||||||
o.Flags.VisitAll(func(flag *pflag.Flag) {
|
|
||||||
if flag.Hidden {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
line := ""
|
|
||||||
if flag.Shorthand != "" && flag.ShorthandDeprecated == "" {
|
|
||||||
line = fmt.Sprintf(" -%s, --%s", flag.Shorthand, flag.Name)
|
|
||||||
} else {
|
|
||||||
line = fmt.Sprintf(" --%s", flag.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
varname, usage := pflag.UnquoteUsage(flag)
|
|
||||||
if varname != "" {
|
|
||||||
line += " " + varname
|
|
||||||
}
|
|
||||||
if flag.NoOptDefVal != "" {
|
|
||||||
switch flag.Value.Type() {
|
|
||||||
case "string":
|
|
||||||
line += fmt.Sprintf("[=\"%s\"]", flag.NoOptDefVal)
|
|
||||||
case "bool":
|
|
||||||
if flag.NoOptDefVal != "true" {
|
|
||||||
line += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
|
|
||||||
}
|
|
||||||
case "count":
|
|
||||||
if flag.NoOptDefVal != "+1" {
|
|
||||||
line += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
line += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
line += "\x00"
|
|
||||||
|
|
||||||
if len(line) > maxlen {
|
|
||||||
maxlen = len(line)
|
|
||||||
}
|
|
||||||
|
|
||||||
if e, ok := o.flagEnv[flag.Name]; ok {
|
|
||||||
line += fmt.Sprintf("$%s", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
line += "\x01"
|
|
||||||
|
|
||||||
if len(line) > maxEnvLen {
|
|
||||||
maxEnvLen = len(line)
|
|
||||||
}
|
|
||||||
|
|
||||||
line += usage
|
|
||||||
if flag.Value.Type() == "string" {
|
|
||||||
line += fmt.Sprintf(" (default %q)", flag.DefValue)
|
|
||||||
} else {
|
|
||||||
line += fmt.Sprintf(" (default %s)", flag.DefValue)
|
|
||||||
}
|
|
||||||
if len(flag.Deprecated) != 0 {
|
|
||||||
line += fmt.Sprintf(" (DEPRECATED: %s)", flag.Deprecated)
|
|
||||||
}
|
|
||||||
|
|
||||||
lines = append(lines, line)
|
|
||||||
})
|
|
||||||
|
|
||||||
for _, line := range lines {
|
|
||||||
sidx1 := strings.Index(line, "\x00")
|
|
||||||
sidx2 := strings.Index(line, "\x01")
|
|
||||||
spacing1 := strings.Repeat(" ", maxlen-sidx1)
|
|
||||||
spacing2 := strings.Repeat(" ", (maxEnvLen-maxlen)-sidx2+sidx1)
|
|
||||||
fmt.Fprintln(buf, line[:sidx1], spacing1, line[sidx1+1:sidx2], spacing2, wrap(maxEnvLen+3, 0, line[sidx2+1:]))
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Opts) getEnv(flag, k string) string {
|
|
||||||
if k != "" {
|
|
||||||
o.flagEnv[flag] = k
|
|
||||||
return o.env.Getenv(k)
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Opts) Int64(envKey, flag, shortFlag string, defaultVal int64, usage string) (*int64, error) {
|
|
||||||
if env := o.getEnv(flag, envKey); env != "" {
|
|
||||||
envVal, err := strconv.ParseInt(env, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, UsageErrorf(`invalid environment variable %s. Expected int64. Found "%v".`, envKey, envVal)
|
|
||||||
}
|
|
||||||
defaultVal = envVal
|
|
||||||
}
|
|
||||||
|
|
||||||
return o.Flags.Int64P(flag, shortFlag, defaultVal, usage), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Opts) String(envKey, flag, shortFlag string, defaultVal, usage string) *string {
|
|
||||||
if env := o.getEnv(flag, envKey); env != "" {
|
|
||||||
defaultVal = env
|
|
||||||
}
|
|
||||||
|
|
||||||
return o.Flags.StringP(flag, shortFlag, defaultVal, usage)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Opts) Bool(envKey, flag, shortFlag string, defaultVal bool, usage string) (*bool, error) {
|
|
||||||
if env := o.getEnv(flag, envKey); env != "" {
|
|
||||||
if !boolyEnv(env) {
|
|
||||||
return nil, UsageErrorf(`invalid environment variable %s. Expected bool. Found "%s".`, envKey, env)
|
|
||||||
}
|
|
||||||
if truthyEnv(env) {
|
|
||||||
defaultVal = true
|
|
||||||
} else {
|
|
||||||
defaultVal = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return o.Flags.BoolP(flag, shortFlag, defaultVal, usage), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func boolyEnv(s string) bool {
|
|
||||||
return falseyEnv(s) || truthyEnv(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func falseyEnv(s string) bool {
|
|
||||||
return s == "0" || s == "false"
|
|
||||||
}
|
|
||||||
|
|
||||||
func truthyEnv(s string) bool {
|
|
||||||
return s == "1" || s == "true"
|
|
||||||
}
|
|
||||||
|
|
@ -1,180 +0,0 @@
|
||||||
// Package xmain provides a standard stub for the main of a command handling logging,
|
|
||||||
// flags, signals and shutdown.
|
|
||||||
package xmain
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"cdr.dev/slog"
|
|
||||||
"cdr.dev/slog/sloggers/sloghuman"
|
|
||||||
|
|
||||||
"oss.terrastruct.com/xos"
|
|
||||||
|
|
||||||
"oss.terrastruct.com/cmdlog"
|
|
||||||
|
|
||||||
ctxlog "oss.terrastruct.com/d2/lib/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
type RunFunc func(context.Context, *State) error
|
|
||||||
|
|
||||||
func Main(run RunFunc) {
|
|
||||||
name := ""
|
|
||||||
args := []string(nil)
|
|
||||||
if len(os.Args) > 0 {
|
|
||||||
name = os.Args[0]
|
|
||||||
args = os.Args[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
ms := &State{
|
|
||||||
Name: name,
|
|
||||||
|
|
||||||
Stdin: os.Stdin,
|
|
||||||
Stdout: os.Stdout,
|
|
||||||
Stderr: os.Stderr,
|
|
||||||
|
|
||||||
Env: xos.NewEnv(os.Environ()),
|
|
||||||
}
|
|
||||||
ms.Log = cmdlog.New(ms.Env, os.Stderr)
|
|
||||||
ms.Opts = NewOpts(ms.Env, ms.Log, args)
|
|
||||||
|
|
||||||
sigs := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(sigs, os.Interrupt, syscall.SIGTERM)
|
|
||||||
|
|
||||||
err := ms.Main(context.Background(), sigs, run)
|
|
||||||
if err != nil {
|
|
||||||
code := 1
|
|
||||||
msg := ""
|
|
||||||
usage := false
|
|
||||||
|
|
||||||
var eerr ExitError
|
|
||||||
var uerr UsageError
|
|
||||||
if errors.As(err, &eerr) {
|
|
||||||
code = eerr.Code
|
|
||||||
msg = eerr.Message
|
|
||||||
} else if errors.As(err, &uerr) {
|
|
||||||
msg = err.Error()
|
|
||||||
usage = true
|
|
||||||
} else {
|
|
||||||
msg = err.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
if msg != "" {
|
|
||||||
ms.Log.Error.Print(msg)
|
|
||||||
if usage {
|
|
||||||
ms.Log.Error.Print("Run with --help to see usage.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
os.Exit(code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type State struct {
|
|
||||||
Name string
|
|
||||||
|
|
||||||
Stdin io.Reader
|
|
||||||
Stdout io.WriteCloser
|
|
||||||
Stderr io.WriteCloser
|
|
||||||
|
|
||||||
Log *cmdlog.Logger
|
|
||||||
Env *xos.Env
|
|
||||||
Opts *Opts
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ms *State) Main(ctx context.Context, sigs <-chan os.Signal, run func(context.Context, *State) error) error {
|
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
done := make(chan error, 1)
|
|
||||||
go func() {
|
|
||||||
defer close(done)
|
|
||||||
done <- run(ctx, ms)
|
|
||||||
}()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case err := <-done:
|
|
||||||
return err
|
|
||||||
case sig := <-sigs:
|
|
||||||
ms.Log.Warn.Printf("received signal %v: shutting down...", sig)
|
|
||||||
cancel()
|
|
||||||
select {
|
|
||||||
case err := <-done:
|
|
||||||
if err != nil && !errors.Is(err, context.Canceled) {
|
|
||||||
return fmt.Errorf("failed to shutdown: %w", err)
|
|
||||||
}
|
|
||||||
if sig == syscall.SIGTERM {
|
|
||||||
// We successfully shutdown.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return ExitError{Code: 1}
|
|
||||||
case <-time.After(time.Minute):
|
|
||||||
return ExitError{
|
|
||||||
Code: 1,
|
|
||||||
Message: "took longer than 1 minute to shutdown: exiting forcefully",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ExitError struct {
|
|
||||||
Code int `json:"code"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExitErrorf(code int, msg string, v ...interface{}) ExitError {
|
|
||||||
return ExitError{
|
|
||||||
Code: code,
|
|
||||||
Message: fmt.Sprintf(msg, v...),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ee ExitError) Error() string {
|
|
||||||
s := fmt.Sprintf("exiting with code %d", ee.Code)
|
|
||||||
if ee.Message != "" {
|
|
||||||
s += ": " + ee.Message
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
type UsageError struct {
|
|
||||||
Message string `json:"message"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func UsageErrorf(msg string, v ...interface{}) UsageError {
|
|
||||||
return UsageError{
|
|
||||||
Message: fmt.Sprintf(msg, v...),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ue UsageError) Error() string {
|
|
||||||
return fmt.Sprintf("bad usage: %s", ue.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ms *State) ReadPath(fp string) ([]byte, error) {
|
|
||||||
if fp == "-" {
|
|
||||||
return io.ReadAll(ms.Stdin)
|
|
||||||
}
|
|
||||||
return os.ReadFile(fp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ms *State) WritePath(fp string, p []byte) error {
|
|
||||||
if fp == "-" {
|
|
||||||
_, err := ms.Stdout.Write(p)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return ms.Stdout.Close()
|
|
||||||
}
|
|
||||||
return os.WriteFile(fp, p, 0644)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove after removing slog
|
|
||||||
func DiscardSlog(ctx context.Context) context.Context {
|
|
||||||
return ctxlog.With(ctx, slog.Make(sloghuman.Sink(io.Discard)))
|
|
||||||
}
|
|
||||||
15
main.go
15
main.go
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -14,6 +15,8 @@ import (
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
"go.uber.org/multierr"
|
"go.uber.org/multierr"
|
||||||
|
|
||||||
|
"oss.terrastruct.com/util-go/xmain"
|
||||||
|
|
||||||
"oss.terrastruct.com/d2/d2layouts/d2sequence"
|
"oss.terrastruct.com/d2/d2layouts/d2sequence"
|
||||||
"oss.terrastruct.com/d2/d2lib"
|
"oss.terrastruct.com/d2/d2lib"
|
||||||
"oss.terrastruct.com/d2/d2plugin"
|
"oss.terrastruct.com/d2/d2plugin"
|
||||||
|
|
@ -21,10 +24,13 @@ import (
|
||||||
"oss.terrastruct.com/d2/d2themes"
|
"oss.terrastruct.com/d2/d2themes"
|
||||||
"oss.terrastruct.com/d2/d2themes/d2themescatalog"
|
"oss.terrastruct.com/d2/d2themes/d2themescatalog"
|
||||||
"oss.terrastruct.com/d2/lib/imgbundler"
|
"oss.terrastruct.com/d2/lib/imgbundler"
|
||||||
|
ctxlog "oss.terrastruct.com/d2/lib/log"
|
||||||
"oss.terrastruct.com/d2/lib/png"
|
"oss.terrastruct.com/d2/lib/png"
|
||||||
"oss.terrastruct.com/d2/lib/textmeasure"
|
"oss.terrastruct.com/d2/lib/textmeasure"
|
||||||
"oss.terrastruct.com/d2/lib/version"
|
"oss.terrastruct.com/d2/lib/version"
|
||||||
"oss.terrastruct.com/d2/lib/xmain"
|
|
||||||
|
"cdr.dev/slog"
|
||||||
|
"cdr.dev/slog/sloggers/sloghuman"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
@ -33,7 +39,7 @@ func main() {
|
||||||
|
|
||||||
func run(ctx context.Context, ms *xmain.State) (err error) {
|
func run(ctx context.Context, ms *xmain.State) (err error) {
|
||||||
// :(
|
// :(
|
||||||
ctx = xmain.DiscardSlog(ctx)
|
ctx = DiscardSlog(ctx)
|
||||||
|
|
||||||
// These should be kept up-to-date with the d2 man page
|
// These should be kept up-to-date with the d2 man page
|
||||||
watchFlag, err := ms.Opts.Bool("D2_WATCH", "watch", "w", false, "watch for changes to input and live reload. Use $HOST and $PORT to specify the listening address.\n(default localhost:0, which is will open on a randomly available local port).")
|
watchFlag, err := ms.Opts.Bool("D2_WATCH", "watch", "w", false, "watch for changes to input and live reload. Use $HOST and $PORT to specify the listening address.\n(default localhost:0, which is will open on a randomly available local port).")
|
||||||
|
|
@ -257,3 +263,8 @@ func renameExt(fp string, newExt string) string {
|
||||||
return strings.TrimSuffix(fp, ext) + newExt
|
return strings.TrimSuffix(fp, ext) + newExt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: remove after removing slog
|
||||||
|
func DiscardSlog(ctx context.Context) context.Context {
|
||||||
|
return ctxlog.With(ctx, slog.Make(sloghuman.Sink(io.Discard)))
|
||||||
|
}
|
||||||
|
|
|
||||||
6
watch.go
6
watch.go
|
|
@ -21,10 +21,12 @@ import (
|
||||||
|
|
||||||
"oss.terrastruct.com/util-go/xbrowser"
|
"oss.terrastruct.com/util-go/xbrowser"
|
||||||
|
|
||||||
|
"oss.terrastruct.com/util-go/xhttp"
|
||||||
|
|
||||||
|
"oss.terrastruct.com/util-go/xmain"
|
||||||
|
|
||||||
"oss.terrastruct.com/d2/d2plugin"
|
"oss.terrastruct.com/d2/d2plugin"
|
||||||
"oss.terrastruct.com/d2/lib/png"
|
"oss.terrastruct.com/d2/lib/png"
|
||||||
"oss.terrastruct.com/d2/lib/xhttp"
|
|
||||||
"oss.terrastruct.com/d2/lib/xmain"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Enabled with the build tag "dev".
|
// Enabled with the build tag "dev".
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue