mirror of https://github.com/databricks/cli.git
add acceptance/selftest
This commit is contained in:
parent
c657006530
commit
e0dda22fe9
|
@ -3,6 +3,7 @@
|
||||||
>>> chmod 000 .git
|
>>> chmod 000 .git
|
||||||
|
|
||||||
>>> $CLI bundle validate
|
>>> $CLI bundle validate
|
||||||
|
Warn: failed to read .git: unable to load repository specific gitconfig: open config: permission denied
|
||||||
Error: unable to load repository specific gitconfig: open config: permission denied
|
Error: unable to load repository specific gitconfig: open config: permission denied
|
||||||
|
|
||||||
Name: git-permerror
|
Name: git-permerror
|
||||||
|
@ -16,6 +17,7 @@ Found 1 error
|
||||||
Exit code: 1
|
Exit code: 1
|
||||||
|
|
||||||
>>> $CLI bundle validate -o json
|
>>> $CLI bundle validate -o json
|
||||||
|
Warn: failed to read .git: unable to load repository specific gitconfig: open config: permission denied
|
||||||
Error: unable to load repository specific gitconfig: open config: permission denied
|
Error: unable to load repository specific gitconfig: open config: permission denied
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,6 +28,7 @@ Exit code: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
>>> withdir subdir/a/b $CLI bundle validate -o json
|
>>> withdir subdir/a/b $CLI bundle validate -o json
|
||||||
|
Warn: failed to read .git: unable to load repository specific gitconfig: open config: permission denied
|
||||||
Error: unable to load repository specific gitconfig: open config: permission denied
|
Error: unable to load repository specific gitconfig: open config: permission denied
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,12 +44,16 @@ Exit code: 1
|
||||||
>>> chmod 000 .git/HEAD
|
>>> chmod 000 .git/HEAD
|
||||||
|
|
||||||
>>> $CLI bundle validate -o json
|
>>> $CLI bundle validate -o json
|
||||||
|
Warn: failed to load current branch: open HEAD: permission denied
|
||||||
|
Warn: failed to load latest commit: open HEAD: permission denied
|
||||||
{
|
{
|
||||||
"bundle_root_path": ".",
|
"bundle_root_path": ".",
|
||||||
"inferred": true
|
"inferred": true
|
||||||
}
|
}
|
||||||
|
|
||||||
>>> withdir subdir/a/b $CLI bundle validate -o json
|
>>> withdir subdir/a/b $CLI bundle validate -o json
|
||||||
|
Warn: failed to load current branch: open HEAD: permission denied
|
||||||
|
Warn: failed to load latest commit: open HEAD: permission denied
|
||||||
{
|
{
|
||||||
"bundle_root_path": ".",
|
"bundle_root_path": ".",
|
||||||
"inferred": true
|
"inferred": true
|
||||||
|
@ -58,6 +65,7 @@ Exit code: 1
|
||||||
>>> chmod 000 .git/config
|
>>> chmod 000 .git/config
|
||||||
|
|
||||||
>>> $CLI bundle validate -o json
|
>>> $CLI bundle validate -o json
|
||||||
|
Warn: failed to read .git: unable to load repository specific gitconfig: open config: permission denied
|
||||||
Error: unable to load repository specific gitconfig: open config: permission denied
|
Error: unable to load repository specific gitconfig: open config: permission denied
|
||||||
|
|
||||||
|
|
||||||
|
@ -68,6 +76,7 @@ Exit code: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
>>> withdir subdir/a/b $CLI bundle validate -o json
|
>>> withdir subdir/a/b $CLI bundle validate -o json
|
||||||
|
Warn: failed to read .git: unable to load repository specific gitconfig: open config: permission denied
|
||||||
Error: unable to load repository specific gitconfig: open config: permission denied
|
Error: unable to load repository specific gitconfig: open config: permission denied
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ Workspace:
|
||||||
Validation OK!
|
Validation OK!
|
||||||
|
|
||||||
>>> $CLI bundle validate -t prod
|
>>> $CLI bundle validate -t prod
|
||||||
|
Warn: target with 'mode: production' should specify an explicit 'targets.prod.git' configuration
|
||||||
Name: my_dbt_sql
|
Name: my_dbt_sql
|
||||||
Target: prod
|
Target: prod
|
||||||
Workspace:
|
Workspace:
|
||||||
|
|
|
@ -20,6 +20,7 @@ Workspace:
|
||||||
Validation OK!
|
Validation OK!
|
||||||
|
|
||||||
>>> $CLI bundle validate -t prod
|
>>> $CLI bundle validate -t prod
|
||||||
|
Warn: target with 'mode: production' should specify an explicit 'targets.prod.git' configuration
|
||||||
Name: my_default_python
|
Name: my_default_python
|
||||||
Target: prod
|
Target: prod
|
||||||
Workspace:
|
Workspace:
|
||||||
|
|
|
@ -22,6 +22,7 @@ Workspace:
|
||||||
Validation OK!
|
Validation OK!
|
||||||
|
|
||||||
>>> $CLI bundle validate -t prod
|
>>> $CLI bundle validate -t prod
|
||||||
|
Warn: target with 'mode: production' should specify an explicit 'targets.prod.git' configuration
|
||||||
Name: my_default_sql
|
Name: my_default_sql
|
||||||
Target: prod
|
Target: prod
|
||||||
Workspace:
|
Workspace:
|
||||||
|
|
|
@ -5,9 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/databricks/cli/libs/log"
|
"github.com/databricks/cli/libs/log"
|
||||||
)
|
)
|
||||||
|
@ -19,7 +17,6 @@ import (
|
||||||
// https://github.com/golang/example/blob/master/slog-handler-guide/README.md
|
// https://github.com/golang/example/blob/master/slog-handler-guide/README.md
|
||||||
type friendlyHandler struct {
|
type friendlyHandler struct {
|
||||||
opts Options
|
opts Options
|
||||||
goas []groupOrAttrs
|
|
||||||
mu *sync.Mutex
|
mu *sync.Mutex
|
||||||
out io.Writer
|
out io.Writer
|
||||||
|
|
||||||
|
@ -53,11 +50,11 @@ func NewFriendlyHandler(out io.Writer, opts *Options) slog.Handler {
|
||||||
|
|
||||||
// Cache (colorized) level strings.
|
// Cache (colorized) level strings.
|
||||||
// The colors to use for each level are configured in `colors.go`.
|
// The colors to use for each level are configured in `colors.go`.
|
||||||
h.levelTrace = h.sprintf(ttyColorLevelTrace, "%5s", "TRACE")
|
h.levelTrace = h.sprintf(ttyColorLevelTrace, "%s", "Trace: ")
|
||||||
h.levelDebug = h.sprintf(ttyColorLevelDebug, "%5s", "DEBUG")
|
h.levelDebug = h.sprintf(ttyColorLevelDebug, "%s", "Debug: ")
|
||||||
h.levelInfo = h.sprintf(ttyColorLevelInfo, "%5s", "INFO")
|
h.levelInfo = h.sprintf(ttyColorLevelInfo, "%s", "Info: ")
|
||||||
h.levelWarn = h.sprintf(ttyColorLevelWarn, "%5s", "WARN")
|
h.levelWarn = h.sprintf(ttyColorLevelWarn, "%s", "Warn: ")
|
||||||
h.levelError = h.sprintf(ttyColorLevelError, "%5s", "ERROR")
|
h.levelError = h.sprintf(ttyColorLevelError, "%s", "Error: ")
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,159 +87,22 @@ func (h *friendlyHandler) Enabled(ctx context.Context, level slog.Level) bool {
|
||||||
return level >= h.opts.Level.Level()
|
return level >= h.opts.Level.Level()
|
||||||
}
|
}
|
||||||
|
|
||||||
type handleState struct {
|
|
||||||
h *friendlyHandler
|
|
||||||
|
|
||||||
buf []byte
|
|
||||||
prefix string
|
|
||||||
|
|
||||||
// Keep stack of groups to pass to [slog.ReplaceAttr] function.
|
|
||||||
groups []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *friendlyHandler) handleState() *handleState {
|
|
||||||
return &handleState{
|
|
||||||
h: h,
|
|
||||||
|
|
||||||
buf: make([]byte, 0, 1024),
|
|
||||||
prefix: "",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *handleState) openGroup(name string) {
|
|
||||||
s.groups = append(s.groups, name)
|
|
||||||
s.prefix += name + "."
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *handleState) closeGroup(name string) {
|
|
||||||
s.prefix = s.prefix[:len(s.prefix)-len(name)-1]
|
|
||||||
s.groups = s.groups[:len(s.groups)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *handleState) append(args ...any) {
|
|
||||||
s.buf = fmt.Append(s.buf, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *handleState) appendf(format string, args ...any) {
|
|
||||||
s.buf = fmt.Appendf(s.buf, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *handleState) appendAttr(a slog.Attr) {
|
|
||||||
if rep := s.h.opts.ReplaceAttr; rep != nil && a.Value.Kind() != slog.KindGroup {
|
|
||||||
// Resolve before calling ReplaceAttr, so the user doesn't have to.
|
|
||||||
a.Value = a.Value.Resolve()
|
|
||||||
a = rep(s.groups, a)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve the Attr's value before doing anything else.
|
|
||||||
a.Value = a.Value.Resolve()
|
|
||||||
|
|
||||||
// Ignore empty Attrs.
|
|
||||||
if a.Equal(slog.Attr{}) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch a.Value.Kind() {
|
|
||||||
case slog.KindGroup:
|
|
||||||
attrs := a.Value.Group()
|
|
||||||
// Output only non-empty groups.
|
|
||||||
if len(attrs) > 0 {
|
|
||||||
if a.Key != "" {
|
|
||||||
s.openGroup(a.Key)
|
|
||||||
}
|
|
||||||
for _, aa := range attrs {
|
|
||||||
s.appendAttr(aa)
|
|
||||||
}
|
|
||||||
if a.Key != "" {
|
|
||||||
s.closeGroup(a.Key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case slog.KindTime:
|
|
||||||
s.append(
|
|
||||||
" ",
|
|
||||||
s.h.sprint(ttyColorAttrKey, s.prefix, a.Key),
|
|
||||||
s.h.sprint(ttyColorAttrSeparator, "="),
|
|
||||||
s.h.sprint(ttyColorAttrValue, a.Value.Time().Format(time.RFC3339Nano)),
|
|
||||||
)
|
|
||||||
default:
|
|
||||||
str := a.Value.String()
|
|
||||||
format := "%s"
|
|
||||||
|
|
||||||
// Quote values wih spaces, to make them easy to parse.
|
|
||||||
if strings.ContainsAny(str, " \t\n") {
|
|
||||||
format = "%q"
|
|
||||||
}
|
|
||||||
|
|
||||||
s.append(
|
|
||||||
" ",
|
|
||||||
s.h.sprint(ttyColorAttrKey, s.prefix, a.Key),
|
|
||||||
s.h.sprint(ttyColorAttrSeparator, "="),
|
|
||||||
s.h.sprint(ttyColorAttrValue, fmt.Sprintf(format, str)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle implements slog.Handler.
|
// Handle implements slog.Handler.
|
||||||
func (h *friendlyHandler) Handle(ctx context.Context, r slog.Record) error {
|
func (h *friendlyHandler) Handle(ctx context.Context, r slog.Record) error {
|
||||||
state := h.handleState()
|
out := fmt.Sprintf("%s%s\n", h.coloredLevel(r), h.sprint(ttyColorMessage, r.Message))
|
||||||
state.append(h.sprintf(ttyColorTime, "%02d:%02d:%02d ", r.Time.Hour(), r.Time.Minute(), r.Time.Second()))
|
|
||||||
state.appendf("%s ", h.coloredLevel(r))
|
|
||||||
state.append(h.sprint(ttyColorMessage, r.Message))
|
|
||||||
|
|
||||||
// Handle state from WithGroup and WithAttrs.
|
|
||||||
goas := h.goas
|
|
||||||
if r.NumAttrs() == 0 {
|
|
||||||
// If the record has no Attrs, remove groups at the end of the list; they are empty.
|
|
||||||
for len(goas) > 0 && goas[len(goas)-1].group != "" {
|
|
||||||
goas = goas[:len(goas)-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, goa := range goas {
|
|
||||||
if goa.group != "" {
|
|
||||||
state.openGroup(goa.group)
|
|
||||||
} else {
|
|
||||||
for _, a := range goa.attrs {
|
|
||||||
state.appendAttr(a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add attributes from the record.
|
|
||||||
r.Attrs(func(a slog.Attr) bool {
|
|
||||||
state.appendAttr(a)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
// Add newline.
|
|
||||||
state.append("\n")
|
|
||||||
|
|
||||||
// Write the log line.
|
|
||||||
h.mu.Lock()
|
h.mu.Lock()
|
||||||
defer h.mu.Unlock()
|
defer h.mu.Unlock()
|
||||||
_, err := h.out.Write(state.buf)
|
_, err := h.out.Write([]byte(out))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *friendlyHandler) withGroupOrAttrs(goa groupOrAttrs) *friendlyHandler {
|
|
||||||
h2 := *h
|
|
||||||
h2.goas = make([]groupOrAttrs, len(h.goas)+1)
|
|
||||||
copy(h2.goas, h.goas)
|
|
||||||
h2.goas[len(h2.goas)-1] = goa
|
|
||||||
return &h2
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithGroup implements slog.Handler.
|
// WithGroup implements slog.Handler.
|
||||||
func (h *friendlyHandler) WithGroup(name string) slog.Handler {
|
func (h *friendlyHandler) WithGroup(name string) slog.Handler {
|
||||||
if name == "" {
|
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
return h.withGroupOrAttrs(groupOrAttrs{group: name})
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithAttrs implements slog.Handler.
|
// WithAttrs implements slog.Handler.
|
||||||
func (h *friendlyHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
func (h *friendlyHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||||
if len(attrs) == 0 {
|
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
return h.withGroupOrAttrs(groupOrAttrs{attrs: attrs})
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue