@@ -27,7 +27,8 @@ type Manifest2822Entry struct {
27
27
28
28
Maintainers []string `delim:"," strip:"\n\r\t "`
29
29
30
- Tags []string `delim:"," strip:"\n\r\t "`
30
+ Tags []string `delim:"," strip:"\n\r\t "`
31
+ SharedTags []string `delim:"," strip:"\n\r\t "`
31
32
32
33
GitRepo string
33
34
GitFetch string
@@ -46,6 +47,7 @@ func (entry Manifest2822Entry) Clone() Manifest2822Entry {
46
47
// SLICES! grr
47
48
entry .Maintainers = append ([]string {}, entry .Maintainers ... )
48
49
entry .Tags = append ([]string {}, entry .Tags ... )
50
+ entry .SharedTags = append ([]string {}, entry .SharedTags ... )
49
51
entry .Constraints = append ([]string {}, entry .Constraints ... )
50
52
return entry
51
53
}
@@ -60,6 +62,10 @@ func (entry Manifest2822Entry) TagsString() string {
60
62
return strings .Join (entry .Tags , StringSeparator2822 )
61
63
}
62
64
65
+ func (entry Manifest2822Entry ) SharedTagsString () string {
66
+ return strings .Join (entry .SharedTags , StringSeparator2822 )
67
+ }
68
+
63
69
func (entry Manifest2822Entry ) ConstraintsString () string {
64
70
return strings .Join (entry .Constraints , StringSeparator2822 )
65
71
}
@@ -77,6 +83,9 @@ func (entry Manifest2822Entry) ClearDefaults(defaults Manifest2822Entry) Manifes
77
83
if entry .TagsString () == defaults .TagsString () {
78
84
entry .Tags = nil
79
85
}
86
+ if entry .SharedTagsString () == defaults .SharedTagsString () {
87
+ entry .SharedTags = nil
88
+ }
80
89
if entry .GitRepo == defaults .GitRepo {
81
90
entry .GitRepo = ""
82
91
}
@@ -103,6 +112,9 @@ func (entry Manifest2822Entry) String() string {
103
112
if str := entry .TagsString (); str != "" {
104
113
ret = append (ret , "Tags: " + str )
105
114
}
115
+ if str := entry .SharedTagsString (); str != "" {
116
+ ret = append (ret , "SharedTags: " + str )
117
+ }
106
118
if str := entry .GitRepo ; str != "" {
107
119
ret = append (ret , "GitRepo: " + str )
108
120
}
@@ -145,6 +157,16 @@ func (entry Manifest2822Entry) HasTag(tag string) bool {
145
157
return false
146
158
}
147
159
160
+ // HasSharedTag returns true if the given tag exists in entry.SharedTags.
161
+ func (entry Manifest2822Entry ) HasSharedTag (tag string ) bool {
162
+ for _ , existingTag := range entry .SharedTags {
163
+ if tag == existingTag {
164
+ return true
165
+ }
166
+ }
167
+ return false
168
+ }
169
+
148
170
func (manifest Manifest2822 ) GetTag (tag string ) * Manifest2822Entry {
149
171
for _ , entry := range manifest .Entries {
150
172
if entry .HasTag (tag ) {
@@ -154,6 +176,62 @@ func (manifest Manifest2822) GetTag(tag string) *Manifest2822Entry {
154
176
return nil
155
177
}
156
178
179
+ // GetSharedTag returns a list of entries with the given tag in entry.SharedTags (or the empty list if there are no entries with the given tag).
180
+ func (manifest Manifest2822 ) GetSharedTag (tag string ) []Manifest2822Entry {
181
+ ret := []Manifest2822Entry {}
182
+ for _ , entry := range manifest .Entries {
183
+ if entry .HasSharedTag (tag ) {
184
+ ret = append (ret , entry )
185
+ }
186
+ }
187
+ return ret
188
+ }
189
+
190
+ // GetAllSharedTags returns a list of the sum of all SharedTags in all entries of this image manifest (in the order they appear in the file).
191
+ func (manifest Manifest2822 ) GetAllSharedTags () []string {
192
+ fakeEntry := Manifest2822Entry {}
193
+ for _ , entry := range manifest .Entries {
194
+ fakeEntry .SharedTags = append (fakeEntry .SharedTags , entry .SharedTags ... )
195
+ }
196
+ fakeEntry .DeduplicateSharedTags ()
197
+ return fakeEntry .SharedTags
198
+ }
199
+
200
+ type SharedTagGroup struct {
201
+ SharedTags []string
202
+ Entries []* Manifest2822Entry
203
+ }
204
+
205
+ // GetSharedTagGroups returns a map of shared tag groups to the list of entries they share (as described in https://github.com/docker-library/go-dockerlibrary/pull/2#issuecomment-277853597).
206
+ func (manifest Manifest2822 ) GetSharedTagGroups () []SharedTagGroup {
207
+ inter := map [string ][]string {}
208
+ interOrder := []string {} // order matters, and maps randomize order
209
+ interKeySep := ","
210
+ for _ , sharedTag := range manifest .GetAllSharedTags () {
211
+ interKeyParts := []string {}
212
+ for _ , entry := range manifest .GetSharedTag (sharedTag ) {
213
+ interKeyParts = append (interKeyParts , entry .Tags [0 ])
214
+ }
215
+ interKey := strings .Join (interKeyParts , interKeySep )
216
+ if _ , ok := inter [interKey ]; ! ok {
217
+ interOrder = append (interOrder , interKey )
218
+ }
219
+ inter [interKey ] = append (inter [interKey ], sharedTag )
220
+ }
221
+ ret := []SharedTagGroup {}
222
+ for _ , tags := range interOrder {
223
+ group := SharedTagGroup {
224
+ SharedTags : inter [tags ],
225
+ Entries : []* Manifest2822Entry {},
226
+ }
227
+ for _ , tag := range strings .Split (tags , interKeySep ) {
228
+ group .Entries = append (group .Entries , manifest .GetTag (tag ))
229
+ }
230
+ ret = append (ret , group )
231
+ }
232
+ return ret
233
+ }
234
+
157
235
func (manifest * Manifest2822 ) AddEntry (entry Manifest2822Entry ) error {
158
236
if len (entry .Tags ) < 1 {
159
237
return fmt .Errorf ("missing Tags" )
@@ -165,20 +243,36 @@ func (manifest *Manifest2822) AddEntry(entry Manifest2822Entry) error {
165
243
return fmt .Errorf ("Tags %q has invalid Maintainers: %q (expected format %q)" , strings .Join (invalidMaintainers , ", " ), MaintainersFormat )
166
244
}
167
245
246
+ entry .DeduplicateSharedTags ()
247
+
168
248
seenTag := map [string ]bool {}
169
249
for _ , tag := range entry .Tags {
170
250
if otherEntry := manifest .GetTag (tag ); otherEntry != nil {
171
251
return fmt .Errorf ("Tags %q includes duplicate tag: %q (duplicated in %q)" , entry .TagsString (), tag , otherEntry .TagsString ())
172
252
}
253
+ if otherEntries := manifest .GetSharedTag (tag ); len (otherEntries ) > 0 {
254
+ return fmt .Errorf ("Tags %q includes tag conflicting with a shared tag: %q (shared tag in %q)" , entry .TagsString (), tag , otherEntries [0 ].TagsString ())
255
+ }
173
256
if seenTag [tag ] {
174
257
return fmt .Errorf ("Tags %q includes duplicate tag: %q" , entry .TagsString (), tag )
175
258
}
176
259
seenTag [tag ] = true
177
260
}
261
+ for _ , tag := range entry .SharedTags {
262
+ if otherEntry := manifest .GetTag (tag ); otherEntry != nil {
263
+ return fmt .Errorf ("Tags %q includes conflicting shared tag: %q (duplicated in %q)" , entry .TagsString (), tag , otherEntry .TagsString ())
264
+ }
265
+ if seenTag [tag ] {
266
+ return fmt .Errorf ("Tags %q includes duplicate tag: %q (in SharedTags)" , entry .TagsString (), tag )
267
+ }
268
+ seenTag [tag ] = true
269
+ }
178
270
179
271
for i , existingEntry := range manifest .Entries {
180
272
if existingEntry .SameBuildArtifacts (entry ) {
181
273
manifest .Entries [i ].Tags = append (existingEntry .Tags , entry .Tags ... )
274
+ manifest .Entries [i ].SharedTags = append (existingEntry .SharedTags , entry .SharedTags ... )
275
+ manifest .Entries [i ].DeduplicateSharedTags ()
182
276
return nil
183
277
}
184
278
}
@@ -210,6 +304,20 @@ func (entry Manifest2822Entry) InvalidMaintainers() []string {
210
304
return invalid
211
305
}
212
306
307
+ // DeduplicateSharedTags will remove duplicate values from entry.SharedTags, preserving order.
308
+ func (entry * Manifest2822Entry ) DeduplicateSharedTags () {
309
+ aggregate := []string {}
310
+ seen := map [string ]bool {}
311
+ for _ , tag := range entry .SharedTags {
312
+ if seen [tag ] {
313
+ continue
314
+ }
315
+ seen [tag ] = true
316
+ aggregate = append (aggregate , tag )
317
+ }
318
+ entry .SharedTags = aggregate
319
+ }
320
+
213
321
type decoderWrapper struct {
214
322
* control.Decoder
215
323
}
0 commit comments