diff --git a/Makefile b/Makefile index f70fdd9..24e9a00 100644 --- a/Makefile +++ b/Makefile @@ -41,14 +41,12 @@ mfer/mf.pb.go: mfer/mf.proto mfer.cmd: $(SOURCEFILES) mfer/mf.pb.go protoc --version - make test cd cmd/mfer && go build -tags urfave_cli_no_docs -o ../../mfer.cmd $(GOFLAGS) . clean: rm -rfv mfer/*.pb.go mfer.cmd cmd/mfer/mfer fmt: - devprereqs gofumpt -l -w mfer internal cmd golangci-lint run --fix -prettier -w *.json diff --git a/internal/cli/gen.go b/internal/cli/gen.go index c2e1b0d..1ed57ef 100644 --- a/internal/cli/gen.go +++ b/internal/cli/gen.go @@ -1,40 +1,54 @@ package cli import ( - "fmt" + "bytes" + "path/filepath" + "git.eeqj.de/sneak/mfer/internal/log" "git.eeqj.de/sneak/mfer/mfer" - "github.com/davecgh/go-spew/spew" "github.com/urfave/cli/v2" ) func (mfa *CLIApp) generateManifestOperation(ctx *cli.Context) error { - fmt.Println("generateManifestOperation()") + log.Debug("generateManifestOperation()") myArgs := ctx.Args() - spew.Dump(myArgs) - - fmt.Printf("%#v\n", ctx.Args().First()) - if ctx.Args().Len() > 0 { - fmt.Printf("%#v\n", ctx.Args().Get(1)) - } - - // fmt.Printf("called with arg: %s\n", c.String("input")) + log.Dump(myArgs) opts := &mfer.ManifestScanOptions{ IgnoreDotfiles: ctx.Bool("IgnoreDotfiles"), FollowSymLinks: ctx.Bool("FollowSymLinks"), } - paths := make([]string, ctx.Args().Len()) + paths := make([]string, ctx.Args().Len()-1) for i := 0; i < ctx.Args().Len(); i++ { - paths = append(paths, ctx.Args().Get(i)) + ap, err := filepath.Abs(ctx.Args().Get(i)) + if err != nil { + return err + } + log.Dump(ap) + paths = append(paths, ap) } mf, err := mfer.NewFromPaths(opts, paths...) if err != nil { panic(err) } - mf.WithContext(ctx) + mf.WithContext(ctx.Context) - spew.Dump(mf) + log.Dump(mf) + err = mf.Scan() + if err != nil { + return err + } + + buf := new(bytes.Buffer) + + err = mf.WriteTo(buf) + if err != nil { + return err + } + + dat := buf.Bytes() + + log.Dump(dat) return nil } diff --git a/internal/cli/mfer.go b/internal/cli/mfer.go index 78ca912..9f85ae1 100644 --- a/internal/cli/mfer.go +++ b/internal/cli/mfer.go @@ -6,7 +6,6 @@ import ( "time" "git.eeqj.de/sneak/mfer/internal/log" - "github.com/pterm/pterm" "github.com/urfave/cli/v2" ) @@ -19,15 +18,35 @@ type CLIApp struct { app *cli.App } +const banner = ` ___ ___ ___ ___ + /__/\ / /\ / /\ / /\ + | |::\ / /:/_ / /:/_ / /::\ + | |:|:\ / /:/ /\ / /:/ /\ / /:/\:\ + __|__|:|\:\ / /:/ /:/ / /:/ /:/_ / /:/~/:/ + /__/::::| \:\ /__/:/ /:/ /__/:/ /:/ /\ /__/:/ /:/___ + \ \:\~~\__\/ \ \:\/:/ \ \:\/:/ /:/ \ \:\/:::::/ + \ \:\ \ \::/ \ \::/ /:/ \ \::/~~~~ + \ \:\ \ \:\ \ \:\/:/ \ \:\ + \ \:\ \ \:\ \ \::/ \ \:\ + \__\/ \__\/ \__\/ \__\/` + func (mfa *CLIApp) printBanner() { - s, _ := pterm.DefaultBigText.WithLetters(pterm.NewLettersFromString(mfa.appname)).Srender() - pterm.DefaultCenter.Println(s) // Print BigLetters with the default CenterPrinter + fmt.Println(banner) } func (mfa *CLIApp) VersionString() string { return fmt.Sprintf("%s (%s)", mfa.version, mfa.gitrev) } +func (mfa *CLIApp) setVerbosity(v int) { + _, present := os.LookupEnv("MFER_DEBUG") + if present { + log.EnableDebugLogging() + } else { + log.SetLevelFromVerbosity(v) + } +} + func (mfa *CLIApp) run() { mfa.startupTime = time.Now() @@ -38,6 +57,8 @@ func (mfa *CLIApp) run() { log.Init() + var verbosity int + mfa.app = &cli.App{ Name: mfa.appname, Usage: "Manifest generator", @@ -48,6 +69,7 @@ func (mfa *CLIApp) run() { Name: "verbose", Usage: "Verbosity level", Aliases: []string{"v"}, + Count: &verbosity, }, &cli.BoolFlag{ Name: "quiet", @@ -55,12 +77,6 @@ func (mfa *CLIApp) run() { Aliases: []string{"q"}, }, }, - Action: func(c *cli.Context) error { - if c.Bool("verbose") { - log.IncreaseLevel() - } - return nil - }, Commands: []*cli.Command{ { Name: "generate", @@ -70,6 +86,7 @@ func (mfa *CLIApp) run() { if !c.Bool("quiet") { mfa.printBanner() } + mfa.setVerbosity(verbosity) return mfa.generateManifestOperation(c) }, Flags: []cli.Flag{ @@ -83,13 +100,6 @@ func (mfa *CLIApp) run() { Aliases: []string{"ignore-dotfiles"}, Usage: "Ignore any dot (hidden) files encountered", }, - // FIXME this should be a positional arg - &cli.StringFlag{ - Name: "input", - Value: ".", - Aliases: []string{"i"}, - Usage: "Specify input directory.", - }, &cli.StringFlag{ Name: "output", Value: "./index.mf", @@ -105,6 +115,7 @@ func (mfa *CLIApp) run() { if !c.Bool("quiet") { mfa.printBanner() } + mfa.setVerbosity(verbosity) return mfa.checkManifestOperation(c) }, }, @@ -123,6 +134,7 @@ func (mfa *CLIApp) run() { if !c.Bool("quiet") { mfa.printBanner() } + mfa.setVerbosity(verbosity) return mfa.fetchManifestOperation(c) }, }, diff --git a/internal/cli/misc.go b/internal/cli/misc.go new file mode 100644 index 0000000..f017f8a --- /dev/null +++ b/internal/cli/misc.go @@ -0,0 +1,32 @@ +package cli + +import "fmt" + +// FIXME make this write to a bytes.Buffer with fprintf +func dumpByteSlice(b []byte) { + var a [16]byte + n := (len(b) + 15) &^ 15 + for i := 0; i < n; i++ { + if i%16 == 0 { + fmt.Printf("%4d", i) + } + if i%8 == 0 { + fmt.Print(" ") + } + if i < len(b) { + fmt.Printf(" %02X", b[i]) + } else { + fmt.Print(" ") + } + if i >= len(b) { + a[i%16] = ' ' + } else if b[i] < 32 || b[i] > 126 { + a[i%16] = '.' + } else { + a[i%16] = b[i] + } + if i%16 == 15 { + fmt.Printf(" %s\n", string(a[:])) + } + } +} diff --git a/internal/log/log.go b/internal/log/log.go index 9df89bf..df399f2 100644 --- a/internal/log/log.go +++ b/internal/log/log.go @@ -3,9 +3,12 @@ package log import ( "github.com/apex/log" acli "github.com/apex/log/handlers/cli" + "github.com/davecgh/go-spew/spew" "github.com/pterm/pterm" ) +type Level = log.Level + func DisableStyling() { pterm.DisableColor() pterm.DisableStyling() @@ -22,24 +25,50 @@ func Init() { log.SetLevel(log.InfoLevel) } +func Debug(arg string) { + log.Debug(arg) +} + +func Dump(args ...interface{}) { + str := spew.Sdump(args...) + Debug(str) +} + +func EnableDebugLogging() { + SetLevel(log.DebugLevel) +} + +func VerbosityStepsToLogLevel(l int) log.Level { + switch l { + case 1: + return log.WarnLevel + case 2: + return log.InfoLevel + case 3: + return log.DebugLevel + } + return log.ErrorLevel +} + +func SetLevelFromVerbosity(l int) { + SetLevel(VerbosityStepsToLogLevel(l)) +} + func SetLevel(arg log.Level) { log.SetLevel(arg) } -func GetLevel() log.Level { +func GetLogger() *log.Logger { if logger, ok := log.Log.(*log.Logger); ok { - return logger.Level + return logger } - return 0 + panic("unable to get logger") } -func IncreaseLevel() { - SetLevel(GetLevel() + 1) +func GetLevel() log.Level { + return GetLogger().Level } func WithError(e error) *log.Entry { - if logger, ok := log.Log.(*log.Logger); ok { - return logger.WithError(e) - } - return nil + return GetLogger().WithError(e) } diff --git a/mfer/manifest.go b/mfer/manifest.go index 87c3ab9..2fde3e1 100644 --- a/mfer/manifest.go +++ b/mfer/manifest.go @@ -1,14 +1,17 @@ package mfer import ( + "bytes" + "context" + "errors" "fmt" "io/fs" "path" "path/filepath" "strings" + "git.eeqj.de/sneak/mfer/internal/log" "github.com/spf13/afero" - "github.com/urfave/cli/v2" ) type manifestFile struct { @@ -21,14 +24,14 @@ func (m *manifestFile) String() string { } type manifest struct { - sourceFS []afero.Fs - //sourceFSRoot string + sourceFS []afero.Fs files []*manifestFile scanOptions *ManifestScanOptions totalFileSize int64 pbInner *MFFile pbOuter *MFFileOuter - ctx *cli.Context + output *bytes.Buffer + ctx context.Context errors []*error } @@ -50,7 +53,7 @@ func (m *manifest) AddError(e error) *manifest { return m } -func (m *manifest) WithContext(c *cli.Context) *manifest { +func (m *manifest) WithContext(c context.Context) *manifest { m.ctx = c return m } @@ -68,7 +71,7 @@ func (m *manifest) addInputPath(inputPath string) error { func (m *manifest) addInputFS(f afero.Fs) error { if m.sourceFS == nil { - m.sourceFS = make([]afero.Fs, 1) + m.sourceFS = make([]afero.Fs, 0) } m.sourceFS = append(m.sourceFS, f) // FIXME do some sort of check on f here? @@ -81,14 +84,14 @@ func New() *manifest { } func NewFromPaths(options *ManifestScanOptions, inputPaths ...string) (*manifest, error) { + log.Dump(inputPaths) m := New() m.scanOptions = options for _, p := range inputPaths { - m.addInputPath(p) - } - err := m.scan() - if err != nil { - return nil, err + err := m.addInputPath(p) + if err != nil { + return nil, err + } } return m, nil } @@ -154,9 +157,13 @@ func (m *manifest) addFile(p string, fi fs.FileInfo, sfsIndex int) error { return nil } -func (m *manifest) scan() error { +func (m *manifest) Scan() error { // FIXME scan and whatever function does the hashing should take ctx + log.Debug("manifest scanning") for idx, sfs := range m.sourceFS { + if sfs == nil { + return errors.New("invalid source fs") + } e := afero.Walk(sfs, "/", func(p string, info fs.FileInfo, err error) error { return m.addFile(p, info, idx) }) diff --git a/mfer/mfer_test.go b/mfer/mfer_test.go index ca954eb..d9ec855 100644 --- a/mfer/mfer_test.go +++ b/mfer/mfer_test.go @@ -1,8 +1,10 @@ package mfer import ( + "bytes" "testing" + "git.eeqj.de/sneak/mfer/internal/log" "github.com/davecgh/go-spew/spew" "github.com/spf13/afero" "github.com/stretchr/testify/assert" @@ -26,6 +28,7 @@ func init() { afero.WriteFile(af, "/a/b/c/hello2.txt", []byte("hello world\n\n\n\n"), 0o755) afero.WriteFile(af, "/.hidden/hello.txt", []byte("hello world\n"), 0o755) afero.WriteFile(af, "/.hidden/hello2.txt", []byte("hello world\n"), 0o755) + log.EnableDebugLogging() } func TestPathHiddenFunc(t *testing.T) { @@ -39,7 +42,7 @@ func TestPathHiddenFunc(t *testing.T) { func TestManifestGenerationOne(t *testing.T) { m, err := NewFromFS(&ManifestScanOptions{ IgnoreDotfiles: true, - }, mf) + }, af) assert.Nil(t, err) assert.NotNil(t, m) assert.Equal(t, int64(2), m.GetFileCount()) @@ -49,7 +52,7 @@ func TestManifestGenerationOne(t *testing.T) { func TestManifestGenerationTwo(t *testing.T) { m, err := NewFromFS(&ManifestScanOptions{ IgnoreDotfiles: false, - }, mf) + }, af) assert.Nil(t, err) assert.NotNil(t, m) spew.Dump(m) @@ -57,4 +60,8 @@ func TestManifestGenerationTwo(t *testing.T) { assert.Equal(t, int64(54), m.GetTotalFileSize()) err = m.generate() assert.Nil(t, err) + var buf bytes.Buffer + err = m.WriteTo(&buf) + assert.Nil(t, err) + spew.Dump(buf) } diff --git a/mfer/output.go b/mfer/output.go index 659e61b..5292767 100644 --- a/mfer/output.go +++ b/mfer/output.go @@ -14,11 +14,20 @@ func (m *manifest) WriteToFile(path string) error { } defer f.Close() - return m.Write(f) + return m.WriteTo(f) } -func (m *manifest) Write(output io.Writer) error { - // FIXME implement - panic("nope") - return nil // nolint:all +func (m *manifest) WriteTo(output io.Writer) error { + if m.pbOuter == nil { + err := m.generate() + if err != nil { + return err + } + } + + _, err := output.Write(m.output.Bytes()) + if err != nil { + return err + } + return nil } diff --git a/mfer/serialize.go b/mfer/serialize.go index baa4785..edac9ad 100644 --- a/mfer/serialize.go +++ b/mfer/serialize.go @@ -1,9 +1,11 @@ package mfer import ( + "bytes" + "errors" "time" - "github.com/davecgh/go-spew/spew" + "git.eeqj.de/sneak/mfer/internal/log" "google.golang.org/protobuf/proto" ) @@ -18,25 +20,52 @@ func newTimestampFromTime(t time.Time) *Timestamp { } func (m *manifest) generate() error { + log.Debug("generate()") + + const MAGIC string = "ZNAVSRFG" + if m.pbInner == nil { e := m.generateInner() if e != nil { return e } } - output, err := proto.Marshal(m.pbInner) + if m.pbOuter == nil { + e := m.generateOuter() + if e != nil { + return e + } + } + dat, err := proto.Marshal(m.pbOuter) + if err != nil { + return err + } + m.output = bytes.NewBuffer([]byte(MAGIC)) + _, err = m.output.Write(dat) if err != nil { return err } - spew.Dump(output) return nil } -func (m *manifest) generateOuter(inner *MFFile) error { +func (m *manifest) generateOuter() error { + log.Debug("generateOuter()") + if m.pbInner == nil { + return errors.New("internal error") + } + innerData, err := proto.Marshal(m.pbInner) + if err != nil { + return err + } + o := &MFFileOuter{ + InnerMessage: innerData, + } + m.pbOuter = o return nil } func (m *manifest) generateInner() error { + log.Debug("generateInner()") m.pbInner = &MFFile{ Version: MFFile_ONE, CreatedAt: newTimestampFromTime(time.Now()), @@ -50,6 +79,6 @@ func (m *manifest) generateInner() error { } m.pbInner.Files = append(m.pbInner.Files, nf) } - spew.Dump(m.pbInner) + log.Dump(m.pbInner) return nil }