@@ -3,14 +3,17 @@ package main
3
3
import (
4
4
"fmt"
5
5
"html/template"
6
+ "log"
6
7
"os"
8
+ "regexp"
9
+ "strings"
7
10
8
- "github.com/mlabouardy /nexus-cli/registry"
11
+ "github.com/moepi /nexus-cli/registry"
9
12
"github.com/urfave/cli"
10
13
)
11
14
12
15
const (
13
- CREDENTIALS_TEMPLATES = `# Nexus Credentials
16
+ credentialsTemplates = `# Nexus Credentials
14
17
nexus_host = "{{ .Host }}"
15
18
nexus_username = "{{ .Username }}"
16
19
nexus_password = "{{ .Password }}"
@@ -21,7 +24,7 @@ func main() {
21
24
app := cli .NewApp ()
22
25
app .Name = "Nexus CLI"
23
26
app .Usage = "Manage Docker Private Registry on Nexus"
24
- app .Version = "1.0.0-beta"
27
+ app .Version = "1.0.0-beta-2 "
25
28
app .Authors = []cli.Author {
26
29
cli.Author {
27
30
Name : "Mohamed Labouardy" ,
@@ -55,6 +58,14 @@ func main() {
55
58
Name : "name, n" ,
56
59
Usage : "List tags by image name" ,
57
60
},
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
+ },
58
69
},
59
70
Action : func (c * cli.Context ) error {
60
71
return listTagsByImage (c )
@@ -77,7 +88,7 @@ func main() {
77
88
},
78
89
{
79
90
Name : "delete" ,
80
- Usage : "Delete an image " ,
91
+ Usage : "Delete images " ,
81
92
Flags : []cli.Flag {
82
93
cli.StringFlag {
83
94
Name : "name, n" ,
@@ -88,9 +99,17 @@ func main() {
88
99
cli.StringFlag {
89
100
Name : "keep, k" ,
90
101
},
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
+ },
91
110
},
92
111
Action : func (c * cli.Context ) error {
93
- return deleteImage (c )
112
+ return deleteImages (c )
94
113
},
95
114
},
96
115
},
@@ -125,7 +144,7 @@ func setNexusCredentials(c *cli.Context) error {
125
144
repository ,
126
145
}
127
146
128
- tmpl , err := template .New (".credentials" ).Parse (CREDENTIALS_TEMPLATES )
147
+ tmpl , err := template .New (".credentials" ).Parse (credentialsTemplates )
129
148
if err != nil {
130
149
return cli .NewExitError (err .Error (), 1 )
131
150
}
@@ -158,6 +177,36 @@ func listImages(c *cli.Context) error {
158
177
return nil
159
178
}
160
179
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
+
161
210
func listTagsByImage (c * cli.Context ) error {
162
211
var imgName = c .String ("name" )
163
212
r , err := registry .NewRegistry ()
@@ -169,6 +218,12 @@ func listTagsByImage(c *cli.Context) error {
169
218
}
170
219
tags , err := r .ListTagsByImage (imgName )
171
220
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
+
172
227
compareStringNumber := func (str1 , str2 string ) bool {
173
228
return extractNumberFromString (str1 ) < extractNumberFromString (str2 )
174
229
}
@@ -207,46 +262,71 @@ func showImageInfo(c *cli.Context) error {
207
262
return nil
208
263
}
209
264
210
- func deleteImage (c * cli.Context ) error {
265
+ func deleteImages (c * cli.Context ) error {
211
266
var imgName = c .String ("name" )
212
267
var tag = c .String ("tag" )
213
268
var keep = c .Int ("keep" )
269
+ var invert = c .Bool ("invert" )
270
+
271
+ // Show help if no image name is present
214
272
if imgName == "" {
215
273
fmt .Fprintf (c .App .Writer , "You should specify the image name\n " )
216
274
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 )
219
286
if err != nil {
220
287
return cli .NewExitError (err .Error (), 1 )
221
288
}
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 )
245
323
err = r .DeleteImageByTag (imgName , tag )
246
324
if err != nil {
247
325
return cli .NewExitError (err .Error (), 1 )
248
326
}
249
327
}
328
+ } else {
329
+ fmt .Printf ("Only %d images are available\n " , len (tags ))
250
330
}
251
331
return nil
252
332
}
0 commit comments