Without --output, gen given one directory now writes index.mf in it, and given one file writes index.mf beside it; with no path or several, it still writes index.mf in the current directory. An --output given with an empty value is refused. What gen lists depends only on its arguments and the tree, never on where the manifest is written, so gen DIR followed by check DIR passes. Scanner.EnumeratePaths lists a file argument by its name, as EnumerateFile does. Before, it listed the file under an empty path and gen stopped with "path cannot be empty". The --output help text and the README's Tool Examples state the default. Model: opus-5-5
This commit is contained in:
+76
-27
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
@@ -26,6 +27,8 @@ var (
|
||||
// rendered message stays exactly as mfer has always printed it.
|
||||
errOutputExists = errors.New(
|
||||
"already exists (use --force to overwrite)")
|
||||
// errEmptyOutput indicates --output given with an empty value.
|
||||
errEmptyOutput = errors.New("--output must not be empty")
|
||||
)
|
||||
|
||||
// reportEnumProgress renders enumeration progress until the channel
|
||||
@@ -88,9 +91,40 @@ func (mfa *CLIApp) collectInputPaths(args cli.Args) ([]string, error) {
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
// buildScannerOptions constructs scanner options from the CLI flags.
|
||||
func (mfa *CLIApp) buildScannerOptions(cmd *cli.Command) *mfer.ScannerOptions {
|
||||
output := cmd.String("output")
|
||||
// outputPath returns the file gen writes the manifest to: the one --output
|
||||
// names, or else index.mf in the directory the only argument names, or
|
||||
// beside the file it names, or else in the current directory. An --output
|
||||
// given with an empty value is refused.
|
||||
func (mfa *CLIApp) outputPath(cmd *cli.Command) (string, error) {
|
||||
if cmd.IsSet("output") {
|
||||
output := cmd.String("output")
|
||||
if output == "" {
|
||||
return "", errEmptyOutput
|
||||
}
|
||||
|
||||
return output, nil
|
||||
}
|
||||
|
||||
if cmd.Args().Len() != 1 {
|
||||
return defaultManifestName, nil
|
||||
}
|
||||
|
||||
arg := cmd.Args().First()
|
||||
|
||||
// A path that does not exist is refused when it is enumerated.
|
||||
info, err := mfa.Fs.Stat(arg)
|
||||
if err == nil && !info.IsDir() {
|
||||
return filepath.Join(filepath.Dir(arg), defaultManifestName), nil
|
||||
}
|
||||
|
||||
return filepath.Join(arg, defaultManifestName), nil
|
||||
}
|
||||
|
||||
// buildScannerOptions constructs scanner options from the CLI flags and
|
||||
// the path the manifest is written to.
|
||||
func (mfa *CLIApp) buildScannerOptions(
|
||||
cmd *cli.Command, output string,
|
||||
) *mfer.ScannerOptions {
|
||||
opts := &mfer.ScannerOptions{
|
||||
IncludeDotfiles: cmd.Bool("include-dotfiles"),
|
||||
FollowSymLinks: cmd.Bool("follow-symlinks"),
|
||||
@@ -202,23 +236,54 @@ func (mfa *CLIApp) runEnumeratePhase(cmd *cli.Command, s *mfer.Scanner) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// runScanPhase reads the enumerated files and writes the manifest to out,
|
||||
// with optional progress reporting.
|
||||
func (mfa *CLIApp) runScanPhase(
|
||||
ctx context.Context, cmd *cli.Command, s *mfer.Scanner, out io.Writer,
|
||||
) error {
|
||||
var (
|
||||
scanProgress chan mfer.ScanStatus
|
||||
scanWg sync.WaitGroup
|
||||
)
|
||||
|
||||
if cmd.Bool("progress") {
|
||||
scanProgress = make(chan mfer.ScanStatus, 1)
|
||||
|
||||
scanWg.Add(1)
|
||||
|
||||
go reportScanProgress(scanProgress, &scanWg)
|
||||
}
|
||||
|
||||
err := s.ToManifest(ctx, out, scanProgress)
|
||||
|
||||
scanWg.Wait()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("generate manifest: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mfa *CLIApp) generateManifestOperation(
|
||||
ctx context.Context, cmd *cli.Command,
|
||||
) error {
|
||||
log.Debug("generateManifestOperation()")
|
||||
|
||||
s := mfer.NewScannerWithOptions(mfa.buildScannerOptions(cmd))
|
||||
|
||||
// Phase 1: Enumeration - collect paths and stat files
|
||||
err := mfa.runEnumeratePhase(cmd, s)
|
||||
outputPath, err := mfa.outputPath(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
showProgress := cmd.Bool("progress")
|
||||
s := mfer.NewScannerWithOptions(mfa.buildScannerOptions(cmd, outputPath))
|
||||
|
||||
// Phase 1: Enumeration - collect paths and stat files
|
||||
err = mfa.runEnumeratePhase(cmd, s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if output file exists
|
||||
outputPath := cmd.String("output")
|
||||
if exists, _ := afero.Exists(mfa.Fs, outputPath); exists && !cmd.Bool("force") {
|
||||
return fmt.Errorf("output file %s %w", outputPath, errOutputExists)
|
||||
}
|
||||
@@ -249,25 +314,9 @@ func (mfa *CLIApp) generateManifestOperation(
|
||||
}()
|
||||
|
||||
// Phase 2: Scan - read file contents and generate manifest
|
||||
var (
|
||||
scanProgress chan mfer.ScanStatus
|
||||
scanWg sync.WaitGroup
|
||||
)
|
||||
|
||||
if showProgress {
|
||||
scanProgress = make(chan mfer.ScanStatus, 1)
|
||||
|
||||
scanWg.Add(1)
|
||||
|
||||
go reportScanProgress(scanProgress, &scanWg)
|
||||
}
|
||||
|
||||
err = s.ToManifest(ctx, outFile, scanProgress)
|
||||
|
||||
scanWg.Wait()
|
||||
|
||||
err = mfa.runScanPhase(ctx, cmd, s, outFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generate manifest: %w", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Close file before rename to ensure all data is flushed
|
||||
|
||||
Reference in New Issue
Block a user