diff --git a/rmstale.go b/rmstale.go index 38dc91b..5bee2a3 100644 --- a/rmstale.go +++ b/rmstale.go @@ -36,6 +36,7 @@ func main() { ext string showVersion bool extMsg string + dryRun bool ) flag.StringVar(&folder, "p", os.TempDir(), "Path to check for stale files.") flag.StringVar(&folder, "path", os.TempDir(), "Path to check for stale files.") @@ -47,6 +48,8 @@ func main() { flag.StringVar(&ext, "extension", "", "Filter files by extension.") flag.BoolVar(&showVersion, "v", false, "Display version information.") flag.BoolVar(&showVersion, "version", false, "Display version information.") + flag.BoolVar(&dryRun, "d", false, "Dry run mode, no files will be removed.") + flag.BoolVar(&dryRun, "dry-run", false, "Dry run mode, no files will be removed.") // Parse flags flag.Parse() @@ -81,7 +84,7 @@ func main() { logger.Infof("rmstale started against folder '%v'%s for contents older than %v days.", filepath.FromSlash(folder), extMsg, age) - if err := procDir(folder, folder, age, ext); err != nil { + if err := procDir(folder, folder, age, ext, dryRun); err != nil { logger.Errorf("Something went wrong: %v", err) } } @@ -108,7 +111,7 @@ func prompt(format string, a ...interface{}) bool { // It takes the file path (fp) of the directory to process, the root folder (rootFolder) for reference, // the age (age) in days to determine staleness, and the file extension (ext) to filter files. // It returns an error if any operation fails. -func procDir(fp, rootFolder string, age int, ext string) error { +func procDir(fp, rootFolder string, age int, ext string, dryRun bool) error { // get the fileInfo for the directory di, err := os.Stat(fp) if err != nil { @@ -131,12 +134,12 @@ func procDir(fp, rootFolder string, age int, ext string) error { for _, item := range infos { if item.IsDir() { - if err := procDir(path.Join(fp, item.Name()), rootFolder, age, ext); err != nil { + if err := procDir(path.Join(fp, item.Name()), rootFolder, age, ext, dryRun); err != nil { return err } } else { if isStale(item, age) && matchExt(item.Name(), ext) { - removeItem(path.Join(fp, item.Name()), rootFolder) + removeItem(path.Join(fp, item.Name()), rootFolder, dryRun) } } } @@ -146,7 +149,7 @@ func procDir(fp, rootFolder string, age int, ext string) error { return err } if empty && isStale(di, age) && ext == "" { - removeItem(fp, rootFolder) + removeItem(fp, rootFolder, dryRun) } return nil @@ -175,11 +178,15 @@ func isStale(fi os.FileInfo, age int) bool { } // removeItem removes an item from the filesystem. -func removeItem(fp, rootFolder string) { +func removeItem(fp, rootFolder string, dryRun bool) { if fp == rootFolder { logger.Infof("Not removing folder '%v' as it is the root folder...\n", filepath.FromSlash(fp)) return } + if dryRun { + logger.Infof("[DRY RUN] '%v' would be removed...", filepath.FromSlash(fp)) + return + } logger.Infof("Removing '%v'...", filepath.FromSlash(fp)) if err := os.Remove(fp); err != nil { logger.Errorf("%v", err) diff --git a/rmstale_test.go b/rmstale_test.go index 63afc7f..5f7bbba 100644 --- a/rmstale_test.go +++ b/rmstale_test.go @@ -142,35 +142,40 @@ func (suite *RMStateSuite) TestFileRemoval() { name string filename string directory string + dryRun bool want bool }{ { name: "Test with a file", filename: suite.oldFile1.Name(), directory: suite.rootDir, + dryRun: false, want: false, }, { name: "Test with an empty folder", filename: suite.oldEmptySubdir, directory: suite.rootDir, + dryRun: false, want: false, }, { name: "Test with a non-empty folder", filename: suite.oldSubdir1, directory: suite.rootDir, + dryRun: false, want: true, }, { name: "Test when given the root folder", filename: suite.rootDir, directory: suite.rootDir, + dryRun: false, want: true, }, } { suite.Run(t.name, func() { - removeItem(t.filename, t.directory) + removeItem(t.filename, t.directory, t.dryRun) got := exists(t.filename) suite.Equal(t.want, got) }) @@ -226,6 +231,7 @@ func (suite *RMStateSuite) TestProcDirErrors() { path string directory string ext string + dryRun bool wantErr bool }{ { @@ -233,6 +239,7 @@ func (suite *RMStateSuite) TestProcDirErrors() { path: "badFile", directory: suite.rootDir, ext: "", + dryRun: false, wantErr: true, }, { @@ -240,11 +247,12 @@ func (suite *RMStateSuite) TestProcDirErrors() { path: suite.oldFile1.Name(), directory: suite.oldFile1.Name(), ext: "", + dryRun: false, wantErr: true, }, } { suite.Run(t.name, func() { - err := procDir(t.path, t.directory, suite.age, t.ext) + err := procDir(t.path, t.directory, suite.age, t.ext, t.dryRun) suite.Equal(t.wantErr, (err != nil)) }) } @@ -252,7 +260,7 @@ func (suite *RMStateSuite) TestProcDirErrors() { // TestDirectoryProcessing tests the running the entire process over a directory func (suite *RMStateSuite) TestDirectoryProcessing() { - err := procDir(suite.rootDir, suite.rootDir, suite.age, "") + err := procDir(suite.rootDir, suite.rootDir, suite.age, "", false) // Ensure that err == nil suite.Nil(err) @@ -282,7 +290,7 @@ func (suite *RMStateSuite) TestDirectoryProcessing() { // TestFilteredDirectoryProcessing tests the running the entire process over a directory func (suite *RMStateSuite) TestFilteredDirectoryProcessing() { - err := procDir(suite.rootDir, suite.rootDir, suite.age, "yes") + err := procDir(suite.rootDir, suite.rootDir, suite.age, "yes", false) // Ensure that err == nil suite.Nil(err) @@ -308,6 +316,34 @@ func (suite *RMStateSuite) TestFilteredDirectoryProcessing() { suite.True(exists(suite.oldEmptySubdir)) } +// TestDryRunOption tests the dry run option +func (suite *RMStateSuite) TestDryRunOption() { + err := procDir(suite.rootDir, suite.rootDir, suite.age, "yes", true) + // Ensure that err == nil + suite.Nil(err) + + // Check that all of the old files are retained + suite.True(exists(suite.oldFile3yes.Name())) + suite.True(exists(suite.oldFile4yes.Name())) + + // Check that all of the old files not matching the extension are retained + suite.True(exists(suite.oldFile3no.Name())) + suite.True(exists(suite.oldFile4no.Name())) + + // Check that the new files are retained + suite.True(exists(suite.recentFile1.Name())) + suite.True(exists(suite.recentFile2no.Name())) + suite.True(exists(suite.recentFile2yes.Name())) + suite.True(exists(suite.recentFile3.Name())) + + // Check all directories are retained + suite.True(exists(suite.recentSubdir1)) + suite.True(exists(suite.oldSubdir1)) + suite.True(exists(suite.oldSubdir2)) + suite.True(exists(suite.oldSubdir3)) + suite.True(exists(suite.oldEmptySubdir)) +} + // TestVersionInfo tests the version information output func (suite *RMStateSuite) TestVersionInfo() { expected := "rmstale v0.0.0"