Skip to content

Commit

Permalink
feat: add a whatif/dry run option (#201)
Browse files Browse the repository at this point in the history
* feat: add a whatif/dry run option

Fixes #186

* Update logger message in rmstale.go for dry run option
  • Loading branch information
danstis authored Apr 7, 2024
1 parent ca33617 commit d1c5646
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 10 deletions.
19 changes: 13 additions & 6 deletions rmstale.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.")
Expand All @@ -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()
Expand Down Expand Up @@ -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)
}
}
Expand All @@ -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 {
Expand All @@ -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)
}
}
}
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down
44 changes: 40 additions & 4 deletions rmstale_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
Expand Down Expand Up @@ -226,33 +231,36 @@ func (suite *RMStateSuite) TestProcDirErrors() {
path string
directory string
ext string
dryRun bool
wantErr bool
}{
{
name: "Test with a missing file",
path: "badFile",
directory: suite.rootDir,
ext: "",
dryRun: false,
wantErr: true,
},
{
name: "Test with a file",
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))
})
}
}

// 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)

Expand Down Expand Up @@ -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)

Expand All @@ -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"
Expand Down

0 comments on commit d1c5646

Please sign in to comment.