@@ -81,6 +81,23 @@ type BufLockFile interface {
81
81
// Files with FileVersionV1Beta1 or FileVersionV1 will not have PluginKeys.
82
82
// Only files with FileVersionV2 will have PluginKeys with Digests of DigestTypeP1.
83
83
RemotePluginKeys () []bufplugin.PluginKey
84
+ // RemotePolicyKeys returns the PolicyKeys representing the remote policies as specified in the buf.lock file.
85
+ //
86
+ // All PolicyKeys will have unique FullNames.
87
+ // PolicyKeys are sorted by FullName.
88
+ //
89
+ // Files with FileVersionV1Beta1 or FileVersionV1 will not have PolicyKeys.
90
+ // Only files with FileVersionV2 will have PolicyKeys with Digests of DigestTypeP1.
91
+ RemotePolicyKeys () []bufpolicy.PolicyKey
92
+ // RemotePluginKeysForPolicy returns the PluginKeys representing the remote plugins as specified in the buf.lock file
93
+ // for the given policy.
94
+ //
95
+ // All PluginKeys will have unique FullNames.
96
+ // PluginKeys are sorted by FullName.
97
+ //
98
+ // Files with FileVersionV1Beta1 or FileVersionV1 will not have PluginKeys.
99
+ // Only files with FileVersionV2 will have PluginKeys with Digests of DigestTypeP1.
100
+ RemotePluginKeysForPolicy (bufpolicy.PluginKey ) []bufplugin.PluginKey
84
101
85
102
isBufLockFile ()
86
103
}
@@ -190,17 +207,21 @@ func BufLockFileWithDigestResolver(
190
207
// *** PRIVATE ***
191
208
192
209
type bufLockFile struct {
193
- fileVersion FileVersion
194
- objectData ObjectData
195
- depModuleKeys []bufmodule.ModuleKey
196
- remotePluginKeys []bufplugin.PluginKey
210
+ fileVersion FileVersion
211
+ objectData ObjectData
212
+ depModuleKeys []bufmodule.ModuleKey
213
+ remotePluginKeys []bufplugin.PluginKey
214
+ remotePolicyKeys []bufpolicy.PolicyKey
215
+ remotePluginKeysForPolicy map [bufpolicy.PluginKey ][]bufplugin.PluginKey
197
216
}
198
217
199
218
func newBufLockFile (
200
219
fileVersion FileVersion ,
201
220
objectData ObjectData ,
202
221
depModuleKeys []bufmodule.ModuleKey ,
203
222
remotePluginKeys []bufplugin.PluginKey ,
223
+ remotePolicyKeys []bufpolicy.PolicyKey ,
224
+ remotePluginKeysForPolicy map [string ][]bufplugin.PluginKey ,
204
225
) (* bufLockFile , error ) {
205
226
if err := validateNoDuplicateModuleKeysByFullName (depModuleKeys ); err != nil {
206
227
return nil , err
@@ -216,13 +237,22 @@ func newBufLockFile(
216
237
if len (remotePluginKeys ) > 0 {
217
238
return nil , errors .New ("remote plugins are not supported in v1 or v1beta1 buf.lock files" )
218
239
}
240
+ if len (remotePolicyKeys ) > 0 || len (remotePluginKeysForPolicy ) > 0 {
241
+ return nil , errors .New ("remote policies are not supported in v1 or v1beta1 buf.lock files" )
242
+ }
219
243
case FileVersionV2 :
220
244
if err := validateModuleExpectedDigestType (depModuleKeys , fileVersion , bufmodule .DigestTypeB5 ); err != nil {
221
245
return nil , err
222
246
}
223
247
if err := validatePluginExpectedDigestType (remotePluginKeys , fileVersion , bufplugin .DigestTypeP1 ); err != nil {
224
248
return nil , err
225
249
}
250
+ if err := validatePolicyExpectedDigestType (remotePolicyKeys , fileVersion , bufpolicy .DigestTypeP1 ); err != nil {
251
+ return nil , err
252
+ }
253
+ if err := validatePluginKeysForPolicy (remotePluginKeysForPolicy , fileVersion ); err != nil {
254
+ return nil , err
255
+ }
226
256
default :
227
257
return nil , syserror .Newf ("unknown FileVersion: %v" , fileVersion )
228
258
}
@@ -273,6 +303,17 @@ func (l *bufLockFile) RemotePluginKeys() []bufplugin.PluginKey {
273
303
return l .remotePluginKeys
274
304
}
275
305
306
+ func (l * bufLockFile ) RemotePolicyKeys () []bufpolicy.PolicyKey {
307
+ return l .remotePolicyKeys
308
+ }
309
+
310
+ func (l * bufLockFile ) RemotePluginKeysForPolicy (pluginKey bufpolicy.PluginKey ) []bufplugin.PluginKey {
311
+ if l .remotePluginKeysForPolicy == nil {
312
+ return nil
313
+ }
314
+ return l .remotePluginKeysForPolicy [pluginKey .String ()]
315
+ }
316
+
276
317
func (* bufLockFile ) isBufLockFile () {}
277
318
func (* bufLockFile ) isFile () {}
278
319
func (* bufLockFile ) isFileInfo () {}
@@ -346,7 +387,7 @@ func readBufLockFile(
346
387
}
347
388
depModuleKeys [i ] = depModuleKey
348
389
}
349
- return newBufLockFile (fileVersion , objectData , depModuleKeys , nil /* remotePluginKeys */ )
390
+ return newBufLockFile (fileVersion , objectData , depModuleKeys , nil /* remotePluginKeys */ , nil /* remotePolicyKeys */ , nil /* remotePluginKeysForPolicy */ )
350
391
case FileVersionV2 :
351
392
var externalBufLockFile externalBufLockFileV2
352
393
if err := getUnmarshalStrict (allowJSON )(data , & externalBufLockFile ); err != nil {
@@ -387,44 +428,92 @@ func readBufLockFile(
387
428
}
388
429
depModuleKeys [i ] = depModuleKey
389
430
}
390
- remotePluginKeys := make ([]bufplugin.PluginKey , len (externalBufLockFile .Plugins ))
391
- for i , plugin := range externalBufLockFile .Plugins {
392
- if plugin .Name == "" {
393
- return nil , errors .New ("no plugin name specified" )
431
+ remotePluginKeys , err := parseRemotePluginDeps (externalBufLockFile .Plugins )
432
+ if err != nil {
433
+ return nil , err
434
+ }
435
+ remotePolicyKeys := make ([]bufpolicy.PolicyKey , len (externalBufLockFile .Policies ))
436
+ remotePluginKeysForPolicy := make (map [string ][]bufplugin.PluginKey )
437
+ for i , policy := range externalBufLockFile .Policies {
438
+ if policy .Name == "" {
439
+ return nil , errors .New ("no policy name specified" )
394
440
}
395
- pluginFullName , err := bufparse .ParseFullName (plugin .Name )
441
+ policyFullName , err := bufparse .ParseFullName (policy .Name )
396
442
if err != nil {
397
- return nil , fmt .Errorf ("invalid plugin name: %w" , err )
443
+ return nil , fmt .Errorf ("invalid policy name: %w" , err )
398
444
}
399
- if plugin .Commit == "" {
400
- return nil , fmt .Errorf ("no commit specified for plugin %s" , pluginFullName .String ())
445
+ if policy .Commit == "" {
446
+ return nil , fmt .Errorf ("no commit specified for policy %s" , policyFullName .String ())
401
447
}
402
- if plugin .Digest == "" {
403
- return nil , fmt .Errorf ("no digest specified for plugin %s" , pluginFullName .String ())
448
+ if policy .Digest == "" {
449
+ return nil , fmt .Errorf ("no digest specified for policy %s" , policyFullName .String ())
404
450
}
405
- commitID , err := uuidutil .FromDashless (plugin .Commit )
451
+ commitID , err := uuidutil .FromDashless (policy .Commit )
406
452
if err != nil {
407
453
return nil , err
408
454
}
409
- pluginKey , err := bufplugin . NewPluginKey (
410
- pluginFullName ,
455
+ policyKey , err := bufpolicy . NewpolicyKey (
456
+ policyFullName ,
411
457
commitID ,
412
- func () (bufplugin .Digest , error ) {
413
- return bufplugin .ParseDigest (plugin .Digest )
458
+ func () (bufpolicy .Digest , error ) {
459
+ return bufpolicy .ParseDigest (policy .Digest )
414
460
},
415
461
)
416
462
if err != nil {
417
463
return nil , err
418
464
}
419
- remotePluginKeys [i ] = pluginKey
465
+ remotePolicyKeys [i ] = policyKey
466
+ // Parse the plugins for this policy.
467
+ remotePluginKeys , err := parseRemotePluginDeps (policy .Plugins )
468
+ if err != nil {
469
+ return nil , fmt .Errorf ("invalid plugins for policy %q: %w" , policyFullName .String (), err )
470
+ }
471
+ if len (remotePluginKeys ) > 0 {
472
+ remotePluginKeysForPolicy [policyKey .String ()] = remotePluginKeys
473
+ }
420
474
}
421
- return newBufLockFile (fileVersion , objectData , depModuleKeys , remotePluginKeys )
475
+ return newBufLockFile (fileVersion , objectData , depModuleKeys , remotePluginKeys , remotePolicyKeys , remotePluginKeysForPolicy )
422
476
default :
423
477
// This is a system error since we've already parsed.
424
478
return nil , syserror .Newf ("unknown FileVersion: %v" , fileVersion )
425
479
}
426
480
}
427
481
482
+ func parseRemotePluginDeps (pluginDeps []externalBufLockFileDepV2 ) ([]bufplugin.PluginKey , error ) {
483
+ remotePluginKeys := make ([]bufplugin.PluginKey , len (pluginDeps ))
484
+ for i , plugin := range pluginDeps {
485
+ if plugin .Name == "" {
486
+ return nil , errors .New ("no plugin name specified" )
487
+ }
488
+ pluginFullName , err := bufparse .ParseFullName (plugin .Name )
489
+ if err != nil {
490
+ return nil , fmt .Errorf ("invalid plugin name: %w" , err )
491
+ }
492
+ if plugin .Commit == "" {
493
+ return nil , fmt .Errorf ("no commit specified for plugin %s" , pluginFullName .String ())
494
+ }
495
+ if plugin .Digest == "" {
496
+ return nil , fmt .Errorf ("no digest specified for plugin %s" , pluginFullName .String ())
497
+ }
498
+ commitID , err := uuidutil .FromDashless (plugin .Commit )
499
+ if err != nil {
500
+ return nil , err
501
+ }
502
+ pluginKey , err := bufplugin .NewPluginKey (
503
+ pluginFullName ,
504
+ commitID ,
505
+ func () (bufplugin.Digest , error ) {
506
+ return bufplugin .ParseDigest (plugin .Digest )
507
+ },
508
+ )
509
+ if err != nil {
510
+ return nil , err
511
+ }
512
+ remotePluginKeys [i ] = pluginKey
513
+ }
514
+ return remotePluginKeys , nil
515
+ }
516
+
428
517
func writeBufLockFile (
429
518
writer io.Writer ,
430
519
bufLockFile BufLockFile ,
@@ -609,6 +698,45 @@ func validatePluginExpectedDigestType(
609
698
return nil
610
699
}
611
700
701
+ func validatePolicyExpectedDigestType (
702
+ policyKeys []bufpolicy.PolicyKey ,
703
+ fileVersion FileVersion ,
704
+ expectedDigestType bufpolicy.DigestType ,
705
+ ) error {
706
+ for _ , policyKey := range policyKeys {
707
+ digest , err := policyKey .Digest ()
708
+ if err != nil {
709
+ return err
710
+ }
711
+ if digest .Type () != expectedDigestType {
712
+ return fmt .Errorf (
713
+ "%s lock files must use digest type %v, but remote policy %s had a digest type of %v" ,
714
+ fileVersion ,
715
+ expectedDigestType ,
716
+ policyKey .String (),
717
+ digest .Type (),
718
+ )
719
+ }
720
+ }
721
+ return nil
722
+ }
723
+
724
+ func validatePluginKeysForPolicy (
725
+ pluginKeysForPolicy map [bufplugin.PluginKey ][]bufplugin.PluginKey ,
726
+ fileVersion FileVersion ,
727
+ expectedDigestType bufplugin.DigestType ,
728
+ ) error {
729
+ for policyKey , pluginKeys := range pluginKeysForPolicy {
730
+ if err := validateNoDuplicatePluginKeysByFullName (pluginKeys ); err != nil {
731
+ return fmt .Errorf ("duplicate plugins attempted to be added to policy %q: %w" , policyKey .String (), err )
732
+ }
733
+ if err := validatePluginExpectedDigestType (pluginKeys , fileVersion , expectedDigestType ); err != nil {
734
+ return fmt .Errorf ("invalid plugins for policy %q: %w" , policyKey .String (), err )
735
+ }
736
+ }
737
+ return nil
738
+ }
739
+
612
740
// externalBufLockFileV1Beta1V1 represents the v1 or v1beta1 buf.lock file,
613
741
// which have the same shape.
614
742
type externalBufLockFileV1Beta1V1 struct {
@@ -631,9 +759,10 @@ type externalBufLockFileDepV1Beta1V1 struct {
631
759
632
760
// externalBufLockFileV2 represents the v2 buf.lock file.
633
761
type externalBufLockFileV2 struct {
634
- Version string `json:"version,omitempty" yaml:"version,omitempty"`
635
- Deps []externalBufLockFileDepV2 `json:"deps,omitempty" yaml:"deps,omitempty"`
636
- Plugins []externalBufLockFileDepV2 `json:"plugins,omitempty" yaml:"plugins,omitempty"`
762
+ Version string `json:"version,omitempty" yaml:"version,omitempty"`
763
+ Deps []externalBufLockFileDepV2 `json:"deps,omitempty" yaml:"deps,omitempty"`
764
+ Plugins []externalBufLockFileDepV2 `json:"plugins,omitempty" yaml:"plugins,omitempty"`
765
+ Policies []externalBufLockFilePolicyV2 `json:"policies,omitempty" yaml:"policies,omitempty"`
637
766
}
638
767
639
768
// externalBufLockFileDepV2 represents a single dep within a v2 buf.lock file.
@@ -644,6 +773,11 @@ type externalBufLockFileDepV2 struct {
644
773
Digest string `json:"digest,omitempty" yaml:"digest,omitempty"`
645
774
}
646
775
776
+ type externalBufLockFilePolicyV2 struct {
777
+ externalBufLockFileDepV2
778
+ Plugins []externalBufLockFileDepV2 `json:"plugins,omitempty" yaml:"plugins,omitempty"`
779
+ }
780
+
647
781
type bufLockFileOptions struct {
648
782
digestResolver func (
649
783
ctx context.Context ,
0 commit comments