diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0e88400 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,5 @@ +root = true + +[*.go] +indent_style = tab +indent_size = 4 diff --git a/cmd/cmd.go b/cmd.go similarity index 77% rename from cmd/cmd.go rename to cmd.go index b227a32..e8e02ac 100644 --- a/cmd/cmd.go +++ b/cmd.go @@ -1,4 +1,4 @@ -package cmd +package main import ( "fmt" @@ -31,7 +31,7 @@ func ensureDirs(path string) error { return nil } -func run(r io.Reader, w io.Writer, args []string) error { +func run(r io.Reader, w io.Writer, args ...string) error { if len(args) != 2 { return fmt.Errorf("expected 2 arguments, got %d", len(args)) } @@ -50,20 +50,20 @@ func run(r io.Reader, w io.Writer, args []string) error { return err } umask := setPermissions(gitdir) - handler := transfer.NewPktline(r, w) + handler := transfer.NewPktline(r, w, logger) for _, cap := range capabilities { if err := handler.WritePacketText(cap); err != nil { - transfer.Logf("error sending capability: %s: %v", cap, err) + logger.Log("error sending capability", "cap", cap, "err", err) } } if err := handler.WriteFlush(); err != nil { - transfer.Logf("error flushing capabilities: %v", err) + logger.Log("error flushing capabilities", "err", err) } now := time.Now() - transfer.Logf("umask %o", umask) + logger.Log("umask", "umask", umask) backend := local.New(lfsPath, umask, &now) - p := transfer.NewProcessor(handler, backend) - defer transfer.Log("done processing commands") + p := transfer.NewProcessor(handler, backend, logger) + defer logger.Log("done processing commands") switch op { case "upload": return p.ProcessCommands(transfer.UploadOperation) @@ -94,17 +94,17 @@ func Command(stdin io.Reader, stdout io.Writer, stderr io.Writer, args ...string errc := make(chan error, 1) setup(done) - transfer.Logf("git-lfs-transfer %s", "v1") - defer transfer.Log("git-lfs-transfer completed") + logger.Log("git-lfs-transfer", "version", "v1") + defer logger.Log("git-lfs-transfer completed") go func() { - errc <- run(stdin, stdout, args) + errc <- run(stdin, stdout, args...) }() select { case s := <-done: - transfer.Logf("signal %q received", s) + logger.Log("signal received", "signal", s) case err := <-errc: - transfer.Log("done running") + logger.Log("done running") fmt.Fprintln(stderr, Usage()) fmt.Fprintln(stderr, err) if err != nil { diff --git a/cmd/cmd_other.go b/cmd_other.go similarity index 95% rename from cmd/cmd_other.go rename to cmd_other.go index ef1586f..e80536c 100644 --- a/cmd/cmd_other.go +++ b/cmd_other.go @@ -1,7 +1,7 @@ //go:build !(darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris) // +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris -package cmd +package main import ( "os" diff --git a/cmd/cmd_unix.go b/cmd_unix.go similarity index 98% rename from cmd/cmd_unix.go rename to cmd_unix.go index ffea638..f0444c3 100644 --- a/cmd/cmd_unix.go +++ b/cmd_unix.go @@ -1,7 +1,7 @@ //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris // +build darwin dragonfly freebsd linux netbsd openbsd solaris -package cmd +package main import ( "os" diff --git a/internal/local/backend.go b/internal/local/backend.go index 0c4599c..6407d3d 100644 --- a/internal/local/backend.go +++ b/internal/local/backend.go @@ -72,7 +72,6 @@ func (l *LocalBackend) FinishUpload(state interface{}, _ transfer.Args) error { case *UploadState: destPath := oidExpectedPath(l.lfsPath, state.Oid) parent := filepath.Dir(destPath) - transfer.Logf("finishing upload of %s at %s", destPath, parent) if err := os.MkdirAll(parent, 0777); err != nil { return err } @@ -106,7 +105,6 @@ func (l *LocalBackend) StartUpload(oid string, r io.Reader, _ transfer.Args) (in if r == nil { return nil, fmt.Errorf("%w: received null data", transfer.ErrMissingData) } - transfer.Logf("start uploading %s", oid) tempDir := filepath.Join(l.lfsPath, "incomplete") randBytes := make([]byte, 12) if _, err := rand.Read(randBytes); err != nil { @@ -142,7 +140,6 @@ func (l *LocalBackend) Verify(oid string, args transfer.Args) (transfer.Status, return nil, err } if stat.Size() != int64(expectedSize) { - transfer.Logf("size mismatch, expected %d, got %d", expectedSize, stat.Size()) return transfer.NewFailureStatus(transfer.StatusConflict, "size mismatch"), nil } return transfer.SuccessStatus(), nil @@ -169,7 +166,7 @@ func (l *localLockBackend) Timestamp() *time.Time { } // Create implements main.LockBackend. -func (l *localLockBackend) Create(path, refname string) (transfer.Lock, error) { +func (l *localLockBackend) Create(path, _ string) (transfer.Lock, error) { id := localBackendLock{}.HashFor(path) var b bytes.Buffer b.WriteString(fmt.Sprintf("%s:%d:", LocalBackendLockVersion, l.Timestamp().Unix())) @@ -242,12 +239,10 @@ func (l *localLockBackend) Range(_ string, _ int, f func(l transfer.Lock) error) if err != nil { return "", err } - transfer.Logf("found %d locks", len(data)) sort.Slice(data, func(i, j int) bool { return data[i].Name() < data[j].Name() }) for _, lf := range data { - transfer.Logf("found lock %s", lf.Name()) var lock transfer.Lock lock, err = l.FromID(lf.Name()) if err != nil { diff --git a/internal/local/backend_unix.go b/internal/local/backend_unix.go index 729c946..17fdf35 100644 --- a/internal/local/backend_unix.go +++ b/internal/local/backend_unix.go @@ -19,7 +19,6 @@ func (l *LocalBackend) FixPermissions(path string) (transfer.Status, error) { if err != nil { return nil, err } - transfer.Logf("settings permissions of %s to %o", path, 0777&^l.umask) if err := os.Chmod(path, 0777&^l.umask); err != nil { return nil, err } diff --git a/main.go b/main.go index 6c3ab84..989429d 100644 --- a/main.go +++ b/main.go @@ -5,19 +5,37 @@ import ( "fmt" "os" - "github.com/charmbracelet/git-lfs-transfer/cmd" "github.com/charmbracelet/git-lfs-transfer/transfer" + "github.com/rubyist/tracerx" ) +type tracerxLogger struct{} + +// Log logs the given arguments if Debug is true. +func (*tracerxLogger) Log(msg string, kv ...interface{}) { + format := msg + for i := 0; i < len(kv); i += 2 { + format += " %s=%v" + } + tracerx.Printf(format, kv...) +} + +var logger = new(tracerxLogger) + +func init() { + tracerx.DefaultKey = "GIT" + tracerx.Prefix = "trace git-lfs-transfer: " +} + func main() { args := os.Args if len(args) < 2 { - fmt.Fprintln(os.Stderr, cmd.Usage()) + fmt.Fprintln(os.Stderr, Usage()) fmt.Fprintf(os.Stderr, "expected 2 arguments, got %d\n", len(args)-1) os.Exit(1) } - if err := cmd.Command(os.Stdin, os.Stdout, os.Stderr, args[1:]...); err != nil { - fmt.Fprintf(os.Stderr, cmd.Usage()) + if err := Command(os.Stdin, os.Stdout, os.Stderr, args[1:]...); err != nil { + fmt.Fprintf(os.Stderr, Usage()) fmt.Fprintln(os.Stderr) fmt.Fprintln(os.Stderr, err) switch { diff --git a/transfer/args.go b/transfer/args.go index 992c476..3f7ae4c 100644 --- a/transfer/args.go +++ b/transfer/args.go @@ -29,7 +29,6 @@ func ParseArgs(parts []string) (Args, error) { key, value := parts[0], parts[1] args[key] = value } - Logf("args: %d %v", len(args), args) return args, nil } diff --git a/transfer/log.go b/transfer/log.go index a3ed11f..937402d 100644 --- a/transfer/log.go +++ b/transfer/log.go @@ -1,20 +1,14 @@ package transfer -import ( - "github.com/rubyist/tracerx" -) +// Logger is a logging interface. +type Logger interface { + // Log logs the given message and structured arguments. + Log(msg string, kv ...interface{}) +} -var ( - // Debug is the debug flag. - Debug = false -) +type noopLogger struct{} -// Log logs the given arguments if Debug is true. -func Log(v ...interface{}) { - tracerx.Printf("%v", v...) -} +var _ Logger = (*noopLogger)(nil) -// Logf logs the given arguments if Debug is true. -func Logf(format string, v ...interface{}) { - tracerx.Printf(format, v...) -} +// Log implements Logger. +func (*noopLogger) Log(string, ...interface{}) {} diff --git a/transfer/oid.go b/transfer/oid.go index ccd689a..42ac533 100644 --- a/transfer/oid.go +++ b/transfer/oid.go @@ -34,7 +34,7 @@ func (p Pointer) IsValid() bool { return true } -// RelativePath returns the relative storage path of the pointer +// RelativePath returns the relative storage path of the pointer. func (p Pointer) RelativePath() string { if len(p.Oid) < 5 { return p.Oid diff --git a/transfer/pktline.go b/transfer/pktline.go index 85583e2..fa6cbb9 100644 --- a/transfer/pktline.go +++ b/transfer/pktline.go @@ -30,70 +30,75 @@ const ( // PktLine is a Git packet line handler. type Pktline struct { *pktline.Pktline - r io.Reader - w io.Writer + r io.Reader + w io.Writer + logger Logger } // NewPktline creates a new Git packet line handler. -func NewPktline(r io.Reader, w io.Writer) *Pktline { +func NewPktline(r io.Reader, w io.Writer, logger Logger) *Pktline { + if logger == nil { + logger = new(noopLogger) + } return &Pktline{ Pktline: pktline.NewPktline(r, w), r: r, w: w, + logger: logger, } } // SendError sends an error msg. func (p *Pktline) SendError(status uint32, message string) error { - Logf("sending error: %d %s", status, message) + p.logger.Log("sending error status", "code", status, "msg", message) if err := p.WritePacketText(fmt.Sprintf("status %03d", status)); err != nil { - Logf("error writing status: %s", err) + p.logger.Log("failed to write packet", "err", err) } if err := p.WriteDelim(); err != nil { - Logf("error writing delim: %s", err) + p.logger.Log("failed to write delimiter", "err", err) } if err := p.WritePacketText(message); err != nil { - Logf("error writing message: %s", err) + p.logger.Log("failed to write message", "err", err) } return p.WriteFlush() } // SendStatus sends a status message. func (p *Pktline) SendStatus(status Status) error { - Logf("sending status: %s", status) + p.logger.Log("sending status", "code", status) if err := p.WritePacketText(fmt.Sprintf("status %03d", status.Code())); err != nil { - Logf("error writing status: %s", err) + p.logger.Log("failed to write status", "err", err) } if args := status.Args(); len(args) > 0 { for _, arg := range args { if err := p.WritePacketText(arg); err != nil { - Logf("error writing arg: %s", err) + p.logger.Log("failed to write argument", "arg", arg, "err", err) } } } if msgs := status.Messages(); len(msgs) > 0 { if err := p.WriteDelim(); err != nil { - Logf("error writing delim: %s", err) + p.logger.Log("failed to write delimiter", "err", err) } for _, msg := range msgs { if err := p.WritePacketText(msg); err != nil { - Logf("error writing msg: %s", err) + p.logger.Log("failed to write message", "err", err) } } } else if r := status.Reader(); r != nil { - Logf("sending reader") + p.logger.Log("sending reader") // Close reader if it implements io.Closer. if c, ok := r.(io.Closer); ok { defer c.Close() // nolint: errcheck } if err := p.WriteDelim(); err != nil { - Logf("error writing delim: %v", err) + p.logger.Log("failed to write delimiter", "err", err) } w := p.Writer() if _, err := io.Copy(w, r); err != nil { - Logf("error copying reader: %v", err) + p.logger.Log("failed to copy reader", "err", err) } - defer Logf("done copying") + defer p.logger.Log("done copying") return w.Flush() } return p.WriteFlush() diff --git a/transfer/processor.go b/transfer/processor.go index 493da00..47de002 100644 --- a/transfer/processor.go +++ b/transfer/processor.go @@ -15,13 +15,18 @@ import ( type Processor struct { handler *Pktline backend Backend + logger Logger } // NewProcessor creates a new transfer processor. -func NewProcessor(line *Pktline, backend Backend) *Processor { +func NewProcessor(line *Pktline, backend Backend, logger Logger) *Processor { + if logger == nil { + logger = new(noopLogger) + } return &Processor{ handler: line, backend: backend, + logger: logger, } } @@ -29,7 +34,7 @@ func NewProcessor(line *Pktline, backend Backend) *Processor { func (p *Processor) Version() (Status, error) { _, err := p.handler.ReadPacketListToFlush() if err != nil { - Logf("version error: %s", err) + p.logger.Log("invalid version", "err", err) } return NewSuccessStatus([]string{}), nil } @@ -51,8 +56,7 @@ func (p *Processor) ReadBatch(op string, args Args) ([]BatchItem, error) { default: return nil, fmt.Errorf("%w: %s", ErrNotAllowed, fmt.Sprintf("unsupported hash algorithm: %s", hashAlgo)) } - Logf("data: %d %v", len(data), data) - Logf("batch: %s args: %d %v data: %d %v", op, len(args), args, len(data), data) + p.logger.Log("read batch", "operation", op, "args-len", len(args), "args", args, "data-len", len(data), "data", data) items := make([]BatchItem, 0) for _, line := range data { if line == "" { @@ -82,12 +86,12 @@ func (p *Processor) ReadBatch(op string, args Args) ([]BatchItem, error) { } items = append(items, item) } - Logf("items %v", items) + p.logger.Log("batch items", "items", items) its, err := p.backend.Batch(op, items, args) if err != nil { return nil, err } - Logf("batch items: %v", its) + p.logger.Log("batch items", "items", items) return its, nil } @@ -239,12 +243,12 @@ func (p *Processor) Lock() (Status, error) { for { lock, err := lockBackend.Create(path, refname) if errors.Is(err, ErrConflict) { - Logf("lock conflict") + p.logger.Log("lock conflict") lock, err = lockBackend.FromPath(path) if err != nil { - Logf("lock conflict, but no lock found") + p.logger.Log("lock conflict, but no lock found") if retried { - Logf("lock conflict, but no lock found, and retried") + p.logger.Log("lock conflict, but no lock found, and retried") return nil, err } retried = true @@ -253,10 +257,10 @@ func (p *Processor) Lock() (Status, error) { return NewFailureStatusWithArgs(StatusConflict, "conflict", lock.AsArguments()...), nil } if err != nil { - Logf("lock error: %v", err) + p.logger.Log("failed to create lock", "err", err) return nil, err } - Logf("lock success: %v", lock) + p.logger.Log("lock success", "lock", lock) return NewSuccessStatusWithCode(StatusCreated, lock.AsArguments()...), nil } // unreachable @@ -315,7 +319,7 @@ func (p *Processor) ListLocks(useOwnerID bool) (Status, error) { // skip nil locks return nil } - Logf("adding lock %s %s", lock.Path(), lock.ID()) + p.logger.Log("adding lock", "path", lock.Path(), "id", lock.ID()) locks = append(locks, lock) return nil }) @@ -374,7 +378,7 @@ func (p *Processor) Unlock(id string) (Status, error) { // ProcessCommands processes commands from the transfer protocol. func (p *Processor) ProcessCommands(op string) error { - Log("processing commands") + p.logger.Log("processing commands") for { pkt, err := p.handler.ReadPacketText() if errors.Is(err, io.EOF) { @@ -383,21 +387,21 @@ func (p *Processor) ProcessCommands(op string) error { if err != nil { return err } - Logf("received packet: %s", pkt) + p.logger.Log("received packet", "packet", pkt) if pkt == "" { if err := p.handler.SendError(StatusBadRequest, "unknown command"); err != nil { - Logf("error pktline sending error: %v", err) + p.logger.Log("failed to send pktline", "err", err) } continue } msgs := strings.Split(pkt, " ") if len(msgs) < 1 { if err := p.handler.SendError(StatusBadRequest, "no command provided"); err != nil { - Logf("error pktline sending error: %v", err) + p.logger.Log("failed to send pktline", "err", err) } continue } - Logf("received command: %s %v", msgs[0], msgs[1:]) + p.logger.Log("received command", "command", msgs[0], "messages", msgs[1:]) var status Status switch msgs[0] { case versionCommand: @@ -409,10 +413,10 @@ func (p *Processor) ProcessCommands(op string) error { case batchCommand: switch op { case UploadOperation: - Logf("upload batch command received") + p.logger.Log("upload batch command received") status, err = p.UploadBatch() case DownloadOperation: - Logf("download batch command received") + p.logger.Log("download batch command received") status, err = p.DownloadBatch() default: err = p.handler.SendError(StatusBadRequest, "unknown operation") @@ -444,7 +448,7 @@ func (p *Processor) ProcessCommands(op string) error { case DownloadOperation: status, err = p.ListLocks(false) } - Logf("list lock status: %v %v", status, err) + p.logger.Log("list lock command", "status", status, "err", err) case unlockCommand: if len(msgs) > 1 { status, err = p.Unlock(msgs[1]) @@ -453,7 +457,7 @@ func (p *Processor) ProcessCommands(op string) error { } case quitCommand: if err := p.handler.SendStatus(SuccessStatus()); err != nil { - Logf("error pktline sending status: %v", err) + p.logger.Log("failed to send pktline", "err", err) } return nil default: @@ -466,20 +470,20 @@ func (p *Processor) ProcessCommands(op string) error { errors.Is(err, ErrInvalidPacket), errors.Is(err, ErrCorruptData): if err := p.handler.SendError(StatusBadRequest, fmt.Errorf("error: %w", err).Error()); err != nil { - Logf("error pktline sending error: %v", err) + p.logger.Log("failed to send pktline", "err", err) } default: - Logf("error processing command: %v", err) + p.logger.Log("failed to process command", "err", err) if err := p.handler.SendError(StatusInternalServerError, "internal error"); err != nil { - Logf("error pktline sending error: %v", err) + p.logger.Log("failed to send pktline", "err", err) } } } if status != nil { if err := p.handler.SendStatus(status); err != nil { - Logf("error pktline sending status: %v", err) + p.logger.Log("failed to send pktline", "err", err) } } - Log("processed command") + p.logger.Log("processed command") } }