@@ -3,14 +3,17 @@ package main
33import (
44 "fmt"
55 "html/template"
6+ "log"
67 "os"
8+ "regexp"
9+ "strings"
710
8- "github.com/mlabouardy /nexus-cli/registry"
11+ "github.com/moepi /nexus-cli/registry"
912 "github.com/urfave/cli"
1013)
1114
1215const (
13- CREDENTIALS_TEMPLATES = `# Nexus Credentials
16+ credentialsTemplates = `# Nexus Credentials
1417nexus_host = "{{ .Host }}"
1518nexus_username = "{{ .Username }}"
1619nexus_password = "{{ .Password }}"
@@ -21,7 +24,7 @@ func main() {
2124 app := cli .NewApp ()
2225 app .Name = "Nexus CLI"
2326 app .Usage = "Manage Docker Private Registry on Nexus"
24- app .Version = "1.0.0-beta"
27+ app .Version = "1.0.0-beta-2 "
2528 app .Authors = []cli.Author {
2629 cli.Author {
2730 Name : "Mohamed Labouardy" ,
@@ -55,6 +58,14 @@ func main() {
5558 Name : "name, n" ,
5659 Usage : "List tags by image name" ,
5760 },
61+ cli.StringSliceFlag {
62+ Name : "expression, e" ,
63+ Usage : "Filter tags by regular expression" ,
64+ },
65+ cli.BoolFlag {
66+ Name : "invert, v" ,
67+ Usage : "Invert filter results" ,
68+ },
5869 },
5970 Action : func (c * cli.Context ) error {
6071 return listTagsByImage (c )
@@ -77,7 +88,7 @@ func main() {
7788 },
7889 {
7990 Name : "delete" ,
80- Usage : "Delete an image " ,
91+ Usage : "Delete images " ,
8192 Flags : []cli.Flag {
8293 cli.StringFlag {
8394 Name : "name, n" ,
@@ -88,9 +99,17 @@ func main() {
8899 cli.StringFlag {
89100 Name : "keep, k" ,
90101 },
102+ cli.StringSliceFlag {
103+ Name : "expression, e" ,
104+ Usage : "Filter tags by regular expression" ,
105+ },
106+ cli.BoolFlag {
107+ Name : "invert, v" ,
108+ Usage : "Invert results filter expressions" ,
109+ },
91110 },
92111 Action : func (c * cli.Context ) error {
93- return deleteImage (c )
112+ return deleteImages (c )
94113 },
95114 },
96115 },
@@ -125,7 +144,7 @@ func setNexusCredentials(c *cli.Context) error {
125144 repository ,
126145 }
127146
128- tmpl , err := template .New (".credentials" ).Parse (CREDENTIALS_TEMPLATES )
147+ tmpl , err := template .New (".credentials" ).Parse (credentialsTemplates )
129148 if err != nil {
130149 return cli .NewExitError (err .Error (), 1 )
131150 }
@@ -158,6 +177,36 @@ func listImages(c *cli.Context) error {
158177 return nil
159178}
160179
180+ func filterTagsByRegex (tags []string , expressions []string , invert bool ) ([]string , error ) {
181+ var retTags []string
182+ if len (expressions ) == 0 {
183+ return tags , nil
184+ }
185+ for _ , tag := range tags {
186+ tagMiss := false
187+ for _ , expression := range expressions {
188+ var expressionBool = ! invert
189+ if strings .HasPrefix (expression , "!" ) {
190+ expressionBool = invert
191+ expression = strings .Trim (expression , "!" )
192+ }
193+ retVal , err := regexp .MatchString (expression , tag )
194+ if err != nil {
195+ return retTags , err
196+ }
197+ if retVal != expressionBool {
198+ tagMiss = true
199+ break
200+ }
201+ }
202+ // tag must match all expression, so continue with next tag on match
203+ if ! tagMiss {
204+ retTags = append (retTags , tag )
205+ }
206+ }
207+ return retTags , nil
208+ }
209+
161210func listTagsByImage (c * cli.Context ) error {
162211 var imgName = c .String ("name" )
163212 r , err := registry .NewRegistry ()
@@ -169,6 +218,12 @@ func listTagsByImage(c *cli.Context) error {
169218 }
170219 tags , err := r .ListTagsByImage (imgName )
171220
221+ // filter tags by expressions
222+ tags , err = filterTagsByRegex (tags , c .StringSlice ("expression" ), c .Bool ("invert" ))
223+ if err != nil {
224+ log .Fatal (err )
225+ }
226+
172227 compareStringNumber := func (str1 , str2 string ) bool {
173228 return extractNumberFromString (str1 ) < extractNumberFromString (str2 )
174229 }
@@ -207,46 +262,71 @@ func showImageInfo(c *cli.Context) error {
207262 return nil
208263}
209264
210- func deleteImage (c * cli.Context ) error {
265+ func deleteImages (c * cli.Context ) error {
211266 var imgName = c .String ("name" )
212267 var tag = c .String ("tag" )
213268 var keep = c .Int ("keep" )
269+ var invert = c .Bool ("invert" )
270+
271+ // Show help if no image name is present
214272 if imgName == "" {
215273 fmt .Fprintf (c .App .Writer , "You should specify the image name\n " )
216274 cli .ShowSubcommandHelp (c )
217- } else {
218- r , err := registry .NewRegistry ()
275+ return nil
276+ }
277+
278+ r , err := registry .NewRegistry ()
279+ if err != nil {
280+ return cli .NewExitError (err .Error (), 1 )
281+ }
282+
283+ // if a specific tag is provided, ignore all other options
284+ if tag != "" {
285+ err = r .DeleteImageByTag (imgName , tag )
219286 if err != nil {
220287 return cli .NewExitError (err .Error (), 1 )
221288 }
222- if tag == "" {
223- if keep == 0 {
224- fmt .Fprintf (c .App .Writer , "You should either specify the tag or how many images you want to keep\n " )
225- cli .ShowSubcommandHelp (c )
226- } else {
227- tags , err := r .ListTagsByImage (imgName )
228- compareStringNumber := func (str1 , str2 string ) bool {
229- return extractNumberFromString (str1 ) < extractNumberFromString (str2 )
230- }
231- Compare (compareStringNumber ).Sort (tags )
232- if err != nil {
233- return cli .NewExitError (err .Error (), 1 )
234- }
235- if len (tags ) >= keep {
236- for _ , tag := range tags [:len (tags )- keep ] {
237- fmt .Printf ("%s:%s image will be deleted ...\n " , imgName , tag )
238- r .DeleteImageByTag (imgName , tag )
239- }
240- } else {
241- fmt .Printf ("Only %d images are available\n " , len (tags ))
242- }
243- }
244- } else {
289+ return nil
290+ }
291+
292+ // Get list of tags and filter them by all expressions provided
293+ tags , err := r .ListTagsByImage (imgName )
294+ tags , err = filterTagsByRegex (tags , c .StringSlice ("expression" ), invert )
295+ if err != nil {
296+ fmt .Fprintf (c .App .Writer , "Could not filter tags by regular expressions: %s\n " , err )
297+ return err
298+ }
299+
300+ // if no keep is specified, all flags are unset. Show help and exit.
301+ if c .IsSet ("keep" ) == false && len (c .StringSlice ("expression" )) == 0 {
302+ fmt .Fprintf (c .App .Writer , "You should either specify use tag / filter expressions, or specify how many images you want to keep\n " )
303+ cli .ShowSubcommandHelp (c )
304+ return fmt .Errorf ("You should either specify use tag / filter expressions, or specify how many images you want to keep" )
305+ }
306+
307+ if len (tags ) == 0 && ! c .IsSet ("keep" ) {
308+ fmt .Fprintf (c .App .Writer , "No images selected for deletion\n " )
309+ return fmt .Errorf ("No images selected for deletion" )
310+ }
311+
312+ // Remove images by using keep flag
313+ compareStringNumber := func (str1 , str2 string ) bool {
314+ return extractNumberFromString (str1 ) < extractNumberFromString (str2 )
315+ }
316+ Compare (compareStringNumber ).Sort (tags )
317+ if err != nil {
318+ return cli .NewExitError (err .Error (), 1 )
319+ }
320+ if len (tags ) >= keep {
321+ for _ , tag := range tags [:len (tags )- keep ] {
322+ fmt .Printf ("%s:%s image will be deleted ...\n " , imgName , tag )
245323 err = r .DeleteImageByTag (imgName , tag )
246324 if err != nil {
247325 return cli .NewExitError (err .Error (), 1 )
248326 }
249327 }
328+ } else {
329+ fmt .Printf ("Only %d images are available\n " , len (tags ))
250330 }
251331 return nil
252332}
0 commit comments