diff --git a/go.mod b/go.mod index 10938b4..a1119b9 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,7 @@ module github.com/drone-plugins/drone-s3 require ( github.com/aws/aws-sdk-go v1.16.17 + github.com/bmatcuk/doublestar v1.1.1 github.com/joho/godotenv v1.3.0 github.com/mattn/go-zglob v0.0.1 github.com/sirupsen/logrus v1.3.0 diff --git a/go.sum b/go.sum index 0745407..2aa1ac9 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/aws/aws-sdk-go v1.16.17 h1:hHRKZhoB4qEY17aGNp71UxQFyYpx6WZXGMUzx9y/A4w= github.com/aws/aws-sdk-go v1.16.17/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/bmatcuk/doublestar v1.1.1 h1:YroD6BJCZBYx06yYFEWvUuKVWQn3vLLQAVmDmvTSaiQ= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= diff --git a/main.go b/main.go index 06b0756..1013825 100644 --- a/main.go +++ b/main.go @@ -98,6 +98,11 @@ func main() { Name: "env-file", Usage: "source env file", }, + cli.StringFlag{ + Name: "target-remove", + Usage: "glob for which files to remove from target", + EnvVar: "PLUGIN_TARGET_REMOVE", + }, } if err := app.Run(os.Args); err != nil { @@ -125,6 +130,7 @@ func run(c *cli.Context) error { CacheControl: c.String("cache-control"), PathStyle: c.Bool("path-style"), DryRun: c.Bool("dry-run"), + TargetRemove: c.String("target-remove"), } return plugin.Exec() diff --git a/plugin.go b/plugin.go index 50b919f..7b61dec 100644 --- a/plugin.go +++ b/plugin.go @@ -10,6 +10,7 @@ import ( "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3" + "github.com/bmatcuk/doublestar" "github.com/mattn/go-zglob" log "github.com/sirupsen/logrus" ) @@ -74,6 +75,8 @@ type Plugin struct { PathStyle bool // Dry run without uploading/ DryRun bool + // Glob for which files to remove from target + TargetRemove string } // Exec runs the plugin @@ -114,6 +117,72 @@ func (p *Plugin) Exec() error { return err } + if len(p.TargetRemove) != 0 { + + log.WithFields(log.Fields{ + "glob": p.TargetRemove, + }).Info("Deleting files according to glob") + + log.Info("Listing files in bucket") + listInput := &s3.ListObjectsInput{ + Bucket: &p.Bucket, + } + + s3Objects, err := client.ListObjects(listInput) + if err != nil { + return err + } + + var toRemove []string + for _, object := range s3Objects.Contents { + filename := object.Key + + globmatch, err := doublestar.PathMatch(p.TargetRemove, *filename) + + if err != nil { + return err + } + + if globmatch { + toRemove = append(toRemove, *filename) + } + } + + if len(toRemove) > 0 { + log.WithFields(log.Fields{ + "files": len(toRemove), + }).Info("Deleting files from bucket") + + var removeIdentifiers []*s3.ObjectIdentifier + for _, key := range toRemove { + id := s3.ObjectIdentifier{ + Key: aws.String(key), + } + removeIdentifiers = append(removeIdentifiers, &id) + } + + deleteInput := &s3.DeleteObjectsInput{ + Bucket: &p.Bucket, + Delete: &s3.Delete{ + Objects: removeIdentifiers, + Quiet: aws.Bool(false), + }, + } + + // when executing a dry-run we skip this step because we don't actually + // want to remove files from S3. + if !p.DryRun { + log.WithFields(log.Fields{ + "files": len(removeIdentifiers), + }).Info("Attempting to delete files") + + if _, err := client.DeleteObjects(deleteInput); err != nil { + return err + } + } + } + } + for _, match := range matches { stat, err := os.Stat(match)