@@ -22,6 +22,10 @@ import (
2222 docker "github.com/drone-plugins/drone-docker"
2323)
2424
25+ type ecrAPI interface {
26+ DescribeImages (* ecr.DescribeImagesInput ) (* ecr.DescribeImagesOutput , error )
27+ }
28+
2529const defaultRegion = "us-east-1"
2630
2731func main () {
@@ -31,18 +35,19 @@ func main() {
3135 }
3236
3337 var (
34- repo = getenv ("PLUGIN_REPO" )
35- registry = getenv ("PLUGIN_REGISTRY" )
36- region = getenv ("PLUGIN_REGION" , "ECR_REGION" , "AWS_REGION" )
37- key = getenv ("PLUGIN_ACCESS_KEY" , "ECR_ACCESS_KEY" , "AWS_ACCESS_KEY_ID" )
38- secret = getenv ("PLUGIN_SECRET_KEY" , "ECR_SECRET_KEY" , "AWS_SECRET_ACCESS_KEY" )
39- create = parseBoolOrDefault (false , getenv ("PLUGIN_CREATE_REPOSITORY" , "ECR_CREATE_REPOSITORY" ))
40- lifecyclePolicy = getenv ("PLUGIN_LIFECYCLE_POLICY" )
41- repositoryPolicy = getenv ("PLUGIN_REPOSITORY_POLICY" )
42- assumeRole = getenv ("PLUGIN_ASSUME_ROLE" )
43- externalId = getenv ("PLUGIN_EXTERNAL_ID" )
44- scanOnPush = parseBoolOrDefault (false , getenv ("PLUGIN_SCAN_ON_PUSH" ))
45- idToken = os .Getenv ("PLUGIN_OIDC_TOKEN_ID" )
38+ repo = getenv ("PLUGIN_REPO" )
39+ registry = getenv ("PLUGIN_REGISTRY" )
40+ region = getenv ("PLUGIN_REGION" , "ECR_REGION" , "AWS_REGION" )
41+ key = getenv ("PLUGIN_ACCESS_KEY" , "ECR_ACCESS_KEY" , "AWS_ACCESS_KEY_ID" )
42+ secret = getenv ("PLUGIN_SECRET_KEY" , "ECR_SECRET_KEY" , "AWS_SECRET_ACCESS_KEY" )
43+ create = parseBoolOrDefault (false , getenv ("PLUGIN_CREATE_REPOSITORY" , "ECR_CREATE_REPOSITORY" ))
44+ lifecyclePolicy = getenv ("PLUGIN_LIFECYCLE_POLICY" )
45+ repositoryPolicy = getenv ("PLUGIN_REPOSITORY_POLICY" )
46+ assumeRole = getenv ("PLUGIN_ASSUME_ROLE" )
47+ externalId = getenv ("PLUGIN_EXTERNAL_ID" )
48+ scanOnPush = parseBoolOrDefault (false , getenv ("PLUGIN_SCAN_ON_PUSH" ))
49+ idToken = os .Getenv ("PLUGIN_OIDC_TOKEN_ID" )
50+ skipPushIfTagExists = parseBoolOrDefault (false , getenv ("PLUGIN_SKIP_PUSH_IF_TAG_EXISTS" ))
4651 )
4752
4853 // set the region
@@ -114,6 +119,34 @@ func main() {
114119 os .Setenv ("DOCKER_PASSWORD" , password )
115120 os .Setenv ("PLUGIN_REGISTRY_TYPE" , "ECR" )
116121
122+ // Skip if tag already exits for both mutable and immutable repos
123+ if skipPushIfTagExists {
124+ tagInput := getenv ("PLUGIN_TAG" , "PLUGIN_TAGS" )
125+ var tags []string
126+ if tagInput == "" {
127+ tags = []string {"latest" }
128+ } else {
129+ for _ , t := range strings .Split (tagInput , "," ) {
130+ trimmed := strings .TrimSpace (t )
131+ if trimmed != "" {
132+ tags = append (tags , trimmed )
133+ }
134+ }
135+ }
136+
137+ repositoryName := trimHostname (repo , registry )
138+ for _ , t := range tags {
139+ exists , err := tagExists (svc , repositoryName , t )
140+ if err != nil {
141+ logrus .Fatalf ("Error checking if image exists for tag %s: %v" , t , err )
142+ }
143+ if exists {
144+ logrus .Infof ("%s:%s: Image tag exists. Skipping push." , repo , t )
145+ os .Exit (0 )
146+ }
147+ }
148+ }
149+
117150 // invoke the base docker plugin binary
118151 cmd := exec .Command (docker .GetDroneDockerExecCmd ())
119152 cmd .Stdout = os .Stdout
@@ -249,3 +282,20 @@ func getECRClient(sess *session.Session, role string, externalId string, idToken
249282 })
250283 }
251284}
285+
286+ func tagExists (svc ecrAPI , repository , tag string ) (bool , error ) {
287+ input := & ecr.DescribeImagesInput {
288+ RepositoryName : aws .String (repository ),
289+ ImageIds : []* ecr.ImageIdentifier {
290+ {ImageTag : aws .String (tag )},
291+ },
292+ }
293+ output , err := svc .DescribeImages (input )
294+ if err != nil {
295+ if aerr , ok := err .(awserr.Error ); ok && aerr .Code () == "ImageNotFoundException" {
296+ return false , nil
297+ }
298+ return false , err
299+ }
300+ return len (output .ImageDetails ) > 0 , nil
301+ }
0 commit comments