package cli import ( "fmt" "time" "github.com/urfave/cli/v2" "sneak.berlin/go/mfer/internal/checker" "sneak.berlin/go/mfer/internal/log" ) func (mfa *CLIApp) checkManifestOperation(ctx *cli.Context) error { log.Debug("checkManifestOperation()") // Get manifest path from args, default to index.mf manifestPath := "index.mf" if ctx.Args().Len() > 0 { manifestPath = ctx.Args().Get(0) } basePath := ctx.String("base") showProgress := ctx.Bool("progress") log.Debugf("checking manifest %s with base %s", manifestPath, basePath) // Create checker chk, err := checker.NewChecker(manifestPath, basePath, mfa.Fs) if err != nil { return fmt.Errorf("failed to load manifest: %w", err) } log.Debugf("manifest contains %d files, %d bytes", chk.FileCount(), chk.TotalBytes()) // Set up results channel results := make(chan checker.Result, 1) // Set up progress channel var progress chan checker.CheckStatus if showProgress { progress = make(chan checker.CheckStatus, 1) go func() { for status := range progress { log.Progressf("Checking: %d/%d files, %d failures", status.CheckedFiles, status.TotalFiles, status.Failures) } log.ProgressDone() }() } // Process results in a goroutine var failures int64 done := make(chan struct{}) go func() { for result := range results { if result.Status != checker.StatusOK { failures++ log.Infof("%s: %s (%s)", result.Status, result.Path, result.Message) } else { log.Debugf("%s: %s", result.Status, result.Path) } } close(done) }() // Run check err = chk.Check(ctx.Context, results, progress) if err != nil { return fmt.Errorf("check failed: %w", err) } // Wait for results processing to complete <-done // Check for extra files if requested if ctx.Bool("no-extra-files") { extraResults := make(chan checker.Result, 1) extraDone := make(chan struct{}) go func() { for result := range extraResults { failures++ log.Infof("%s: %s (%s)", result.Status, result.Path, result.Message) } close(extraDone) }() err = chk.FindExtraFiles(ctx.Context, extraResults) if err != nil { return fmt.Errorf("failed to check for extra files: %w", err) } <-extraDone } if !ctx.Bool("quiet") { elapsed := time.Since(mfa.startupTime).Seconds() rate := float64(chk.TotalBytes()) / elapsed / 1e6 if failures == 0 { log.Infof("checked %d files (%.1f MB) in %.1fs (%.1f MB/s): all OK", chk.FileCount(), float64(chk.TotalBytes())/1e6, elapsed, rate) } else { log.Infof("checked %d files (%.1f MB) in %.1fs (%.1f MB/s): %d failed", chk.FileCount(), float64(chk.TotalBytes())/1e6, elapsed, rate, failures) } } if failures > 0 { mfa.exitCode = 1 } return nil }