Add atomic writes, humanized sizes, debug logging, and -v/-q per-command
- Atomic writes for mfer gen: writes to temp file, renames on success, cleans up temp on error/interrupt. Prevents empty manifests on Ctrl-C. - Humanized byte sizes using dustin/go-humanize (e.g., "10 MiB" not "10485760") - Progress lines clear when done (using ANSI escape \r\033[K]) - Debug logging when files are added to manifest (mfer gen -vv) - Move -v/-q flags from global to per-command for better UX - Add tests for atomic write behavior with failing filesystem mock
This commit is contained in:
@@ -57,14 +57,31 @@ func (mfa *CLIApp) VersionString() string {
|
||||
return mfer.Version
|
||||
}
|
||||
|
||||
func (mfa *CLIApp) setVerbosity(quiet bool, v int) {
|
||||
func (mfa *CLIApp) setVerbosity(c *cli.Context) {
|
||||
_, present := os.LookupEnv("MFER_DEBUG")
|
||||
if present {
|
||||
log.EnableDebugLogging()
|
||||
} else if quiet {
|
||||
} else if c.Bool("quiet") {
|
||||
log.SetLevel(log.ErrorLevel)
|
||||
} else {
|
||||
log.SetLevelFromVerbosity(v)
|
||||
log.SetLevelFromVerbosity(c.Count("verbose"))
|
||||
}
|
||||
}
|
||||
|
||||
// commonFlags returns the flags shared by most commands (-v, -q)
|
||||
func commonFlags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "verbose",
|
||||
Aliases: []string{"v"},
|
||||
Usage: "Increase verbosity (-v for verbose, -vv for debug)",
|
||||
Count: new(int),
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "quiet",
|
||||
Aliases: []string{"q"},
|
||||
Usage: "Suppress output except errors",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,8 +97,6 @@ func (mfa *CLIApp) run(args []string) {
|
||||
log.SetOutput(mfa.Stdout, mfa.Stderr)
|
||||
log.Init()
|
||||
|
||||
var verbosity int
|
||||
|
||||
mfa.app = &cli.App{
|
||||
Name: mfa.appname,
|
||||
Usage: "Manifest generator",
|
||||
@@ -96,30 +111,17 @@ func (mfa *CLIApp) run(args []string) {
|
||||
mfa.printBanner()
|
||||
return cli.ShowAppHelp(c)
|
||||
},
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "verbose",
|
||||
Usage: "Verbosity level",
|
||||
Aliases: []string{"v"},
|
||||
Count: &verbosity,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "quiet",
|
||||
Usage: "don't produce output except on error",
|
||||
Aliases: []string{"q"},
|
||||
},
|
||||
},
|
||||
Commands: []*cli.Command{
|
||||
{
|
||||
Name: "generate",
|
||||
Aliases: []string{"gen"},
|
||||
Usage: "Generate manifest file",
|
||||
Action: func(c *cli.Context) error {
|
||||
mfa.setVerbosity(c.Bool("quiet"), verbosity)
|
||||
mfa.setVerbosity(c)
|
||||
mfa.printBanner()
|
||||
return mfa.generateManifestOperation(c)
|
||||
},
|
||||
Flags: []cli.Flag{
|
||||
Flags: append(commonFlags(),
|
||||
&cli.BoolFlag{
|
||||
Name: "FollowSymLinks",
|
||||
Aliases: []string{"follow-symlinks"},
|
||||
@@ -146,18 +148,18 @@ func (mfa *CLIApp) run(args []string) {
|
||||
Aliases: []string{"P"},
|
||||
Usage: "Show progress during enumeration and scanning",
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
{
|
||||
Name: "check",
|
||||
Usage: "Validate files using manifest file",
|
||||
ArgsUsage: "[manifest file]",
|
||||
Action: func(c *cli.Context) error {
|
||||
mfa.setVerbosity(c.Bool("quiet"), verbosity)
|
||||
mfa.setVerbosity(c)
|
||||
mfa.printBanner()
|
||||
return mfa.checkManifestOperation(c)
|
||||
},
|
||||
Flags: []cli.Flag{
|
||||
Flags: append(commonFlags(),
|
||||
&cli.StringFlag{
|
||||
Name: "base",
|
||||
Aliases: []string{"b"},
|
||||
@@ -173,18 +175,18 @@ func (mfa *CLIApp) run(args []string) {
|
||||
Name: "no-extra-files",
|
||||
Usage: "Fail if files exist in base directory that are not in manifest",
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
{
|
||||
Name: "freshen",
|
||||
Usage: "Update manifest with changed, new, and removed files",
|
||||
ArgsUsage: "[manifest file]",
|
||||
Action: func(c *cli.Context) error {
|
||||
mfa.setVerbosity(c.Bool("quiet"), verbosity)
|
||||
mfa.setVerbosity(c)
|
||||
mfa.printBanner()
|
||||
return mfa.freshenManifestOperation(c)
|
||||
},
|
||||
Flags: []cli.Flag{
|
||||
Flags: append(commonFlags(),
|
||||
&cli.StringFlag{
|
||||
Name: "base",
|
||||
Aliases: []string{"b"},
|
||||
@@ -206,7 +208,7 @@ func (mfa *CLIApp) run(args []string) {
|
||||
Aliases: []string{"P"},
|
||||
Usage: "Show progress during scanning and hashing",
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
{
|
||||
Name: "version",
|
||||
@@ -240,10 +242,11 @@ func (mfa *CLIApp) run(args []string) {
|
||||
Name: "fetch",
|
||||
Usage: "fetch manifest and referenced files",
|
||||
Action: func(c *cli.Context) error {
|
||||
mfa.setVerbosity(c.Bool("quiet"), verbosity)
|
||||
mfa.setVerbosity(c)
|
||||
mfa.printBanner()
|
||||
return mfa.fetchManifestOperation(c)
|
||||
},
|
||||
Flags: commonFlags(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user