@@ -25,7 +25,7 @@ class Settings {
25
25
return settings
26
26
}
27
27
28
- static async syncSubOrgs ( nop , context , suborg , repo , config , ref ) {
28
+ static async syncSubOrgs ( nop , context , suborg , repo , config , ref ) {
29
29
const settings = new Settings ( nop , context , repo , config , ref , suborg )
30
30
try {
31
31
await settings . loadConfigs ( )
@@ -37,7 +37,7 @@ class Settings {
37
37
}
38
38
}
39
39
40
- static async sync ( nop , context , repo , config , ref ) {
40
+ static async sync ( nop , context , repo , config , ref ) {
41
41
const settings = new Settings ( nop , context , repo , config , ref )
42
42
try {
43
43
await settings . loadConfigs ( repo )
@@ -52,13 +52,13 @@ class Settings {
52
52
}
53
53
}
54
54
55
- static async handleError ( nop , context , repo , config , ref , nopcommand ) {
55
+ static async handleError ( nop , context , repo , config , ref , nopcommand ) {
56
56
const settings = new Settings ( nop , context , repo , config , ref )
57
57
settings . appendToResults ( [ nopcommand ] )
58
58
await settings . handleResults ( )
59
59
}
60
60
61
- constructor ( nop , context , repo , config , ref , suborg ) {
61
+ constructor ( nop , context , repo , config , ref , suborg ) {
62
62
this . ref = ref
63
63
this . context = context
64
64
this . installation_id = context . payload . installation . id
@@ -97,7 +97,7 @@ class Settings {
97
97
}
98
98
99
99
// Create a check in the Admin repo for safe-settings.
100
- async createCheckRun ( ) {
100
+ async createCheckRun ( ) {
101
101
const startTime = new Date ( )
102
102
let conclusion = 'success'
103
103
let details = `Run on: \`${ new Date ( ) . toISOString ( ) } \``
@@ -143,7 +143,7 @@ class Settings {
143
143
} )
144
144
}
145
145
146
- logError ( msg ) {
146
+ logError ( msg ) {
147
147
this . log . error ( msg )
148
148
this . errors . push ( {
149
149
owner : this . repo . owner ,
@@ -153,7 +153,7 @@ class Settings {
153
153
} )
154
154
}
155
155
156
- async handleResults ( ) {
156
+ async handleResults ( ) {
157
157
const { payload } = this . context
158
158
159
159
// Create a checkrun if not in nop mode
@@ -163,6 +163,13 @@ class Settings {
163
163
return
164
164
}
165
165
166
+ //remove duplicate rows in this.results
167
+ this . results = this . results . filter ( ( thing , index , self ) => {
168
+ return index === self . findIndex ( ( t ) => {
169
+ return t . type === thing . type && t . repo === thing . repo && t . plugin === thing . plugin
170
+ } )
171
+ } )
172
+
166
173
let error = false
167
174
// Different logic
168
175
const stats = {
@@ -227,23 +234,23 @@ class Settings {
227
234
#### :robot: Safe-Settings config changes detected:
228
235
229
236
${ this . results . reduce ( ( x , y ) => {
230
- if ( ! y ) {
231
- return x
232
- }
233
- if ( y . type === 'ERROR' ) {
234
- error = true
235
- return `${ x }
237
+ if ( ! y ) {
238
+ return x
239
+ }
240
+ if ( y . type === 'ERROR' ) {
241
+ error = true
242
+ return `${ x }
236
243
<tr><td> ❗ ${ y . action . msg } </td><td> ${ y . plugin } </td><td> ${ prettify ( y . repo ) } </td><td> ${ prettify ( y . action . additions ) } </td><td> ${ prettify ( y . action . deletions ) } </td><td> ${ prettify ( y . action . modifications ) } </td><tr>`
237
- } else if ( y . action . additions === null && y . action . deletions === null && y . action . modifications === null ) {
238
- return `${ x } `
239
- } else {
240
- if ( y . action === undefined ) {
241
- return `${ x } `
242
- }
243
- return `${ x }
244
+ } else if ( y . action . additions === null && y . action . deletions === null && y . action . modifications === null ) {
245
+ return `${ x } `
246
+ } else {
247
+ if ( y . action === undefined ) {
248
+ return `${ x } `
249
+ }
250
+ return `${ x }
244
251
<tr><td> ✋ </td><td> ${ y . plugin } </td><td> ${ prettify ( y . repo ) } </td><td> ${ prettify ( y . action . additions ) } </td><td> ${ prettify ( y . action . deletions ) } </td><td> ${ prettify ( y . action . modifications ) } </td><tr>`
245
- }
246
- } , table ) }
252
+ }
253
+ } , table ) }
247
254
`
248
255
249
256
const pullRequest = payload . check_run . check_suite . pull_requests [ 0 ]
@@ -273,12 +280,12 @@ ${this.results.reduce((x, y) => {
273
280
await this . github . checks . update ( params )
274
281
}
275
282
276
- async loadConfigs ( repo ) {
283
+ async loadConfigs ( repo ) {
277
284
this . subOrgConfigs = await this . getSubOrgConfigs ( )
278
285
this . repoConfigs = await this . getRepoConfigs ( repo )
279
286
}
280
287
281
- async updateOrg ( ) {
288
+ async updateOrg ( ) {
282
289
const rulesetsConfig = this . config . rulesets
283
290
if ( rulesetsConfig ) {
284
291
const RulesetsPlugin = Settings . PLUGINS . rulesets
@@ -288,7 +295,7 @@ ${this.results.reduce((x, y) => {
288
295
}
289
296
}
290
297
291
- async updateRepos ( repo ) {
298
+ async updateRepos ( repo ) {
292
299
this . subOrgConfigs = this . subOrgConfigs || await this . getSubOrgConfigs ( )
293
300
let repoConfig = this . config . repository
294
301
if ( repoConfig ) {
@@ -354,15 +361,15 @@ ${this.results.reduce((x, y) => {
354
361
}
355
362
}
356
363
357
- async updateAll ( ) {
364
+ async updateAll ( ) {
358
365
// this.subOrgConfigs = this.subOrgConfigs || await this.getSubOrgConfigs(this.github, this.repo, this.log)
359
366
// this.repoConfigs = this.repoConfigs || await this.getRepoConfigs(this.github, this.repo, this.log)
360
367
return this . eachRepositoryRepos ( this . github , this . config . restrictedRepos , this . log ) . then ( res => {
361
368
this . appendToResults ( res )
362
369
} )
363
370
}
364
371
365
- getSubOrgConfig ( repoName ) {
372
+ getSubOrgConfig ( repoName ) {
366
373
if ( this . subOrgConfigs ) {
367
374
for ( const k of Object . keys ( this . subOrgConfigs ) ) {
368
375
const repoPattern = new Glob ( k )
@@ -375,13 +382,13 @@ ${this.results.reduce((x, y) => {
375
382
}
376
383
377
384
// Remove Org specific configs from the repo config
378
- returnRepoSpecificConfigs ( config ) {
385
+ returnRepoSpecificConfigs ( config ) {
379
386
const newConfig = Object . assign ( { } , config ) // clone
380
387
delete newConfig . rulesets
381
388
return newConfig
382
389
}
383
390
384
- childPluginsList ( repo ) {
391
+ childPluginsList ( repo ) {
385
392
const repoName = repo . repo
386
393
const subOrgOverrideConfig = this . getSubOrgConfig ( repoName )
387
394
this . log . debug ( `suborg config for ${ repoName } is ${ JSON . stringify ( subOrgOverrideConfig ) } ` )
@@ -413,7 +420,7 @@ ${this.results.reduce((x, y) => {
413
420
return childPlugins
414
421
}
415
422
416
- validate ( section , baseConfig , overrideConfig ) {
423
+ validate ( section , baseConfig , overrideConfig ) {
417
424
const configValidator = this . configvalidators [ section ]
418
425
if ( configValidator ) {
419
426
this . log . debug ( `Calling configvalidator for key ${ section } ` )
@@ -432,7 +439,7 @@ ${this.results.reduce((x, y) => {
432
439
}
433
440
}
434
441
435
- isRestricted ( repoName ) {
442
+ isRestricted ( repoName ) {
436
443
const restrictedRepos = this . config . restrictedRepos
437
444
// Skip configuring any restricted repos
438
445
if ( Array . isArray ( restrictedRepos ) ) {
@@ -464,11 +471,11 @@ ${this.results.reduce((x, y) => {
464
471
return false
465
472
}
466
473
467
- includesRepo ( repoName , restrictedRepos ) {
474
+ includesRepo ( repoName , restrictedRepos ) {
468
475
return restrictedRepos . filter ( ( restrictedRepo ) => { return RegExp ( restrictedRepo ) . test ( repoName ) } ) . length > 0
469
476
}
470
477
471
- async eachRepositoryRepos ( github , restrictedRepos , log ) {
478
+ async eachRepositoryRepos ( github , restrictedRepos , log ) {
472
479
log . debug ( 'Fetching repositories' )
473
480
return github . paginate ( 'GET /installation/repositories' ) . then ( repositories => {
474
481
return Promise . all ( repositories . map ( repository => {
@@ -489,7 +496,7 @@ ${this.results.reduce((x, y) => {
489
496
* @param params Params to fetch the file with
490
497
* @return The parsed YAML file
491
498
*/
492
- async loadConfigMap ( params ) {
499
+ async loadConfigMap ( params ) {
493
500
try {
494
501
this . log . debug ( ` In loadConfigMap ${ JSON . stringify ( params ) } ` )
495
502
const response = await this . github . repos . getContent ( params ) . catch ( e => {
@@ -536,7 +543,7 @@ ${this.results.reduce((x, y) => {
536
543
* @param params Params to fetch the file with
537
544
* @return The parsed YAML file
538
545
*/
539
- async getRepoConfigMap ( ) {
546
+ async getRepoConfigMap ( ) {
540
547
try {
541
548
this . log . debug ( ` In getRepoConfigMap ${ JSON . stringify ( this . repo ) } ` )
542
549
// GitHub getContent api has a hard limit of returning 1000 entries without
@@ -603,7 +610,7 @@ ${this.results.reduce((x, y) => {
603
610
* @param params Params to fetch the file with
604
611
* @return The parsed YAML file
605
612
*/
606
- async getSubOrgConfigMap ( ) {
613
+ async getSubOrgConfigMap ( ) {
607
614
try {
608
615
this . log . debug ( ` In getSubOrgConfigMap ${ JSON . stringify ( this . repo ) } ` )
609
616
const repo = { owner : this . repo . owner , repo : env . ADMIN_REPO }
@@ -630,7 +637,7 @@ ${this.results.reduce((x, y) => {
630
637
* @param {* } repo repo param
631
638
* @returns repoConfigs object
632
639
*/
633
- async getRepoConfigs ( repo ) {
640
+ async getRepoConfigs ( repo ) {
634
641
try {
635
642
const overridePaths = await this . getRepoConfigMap ( )
636
643
const repoConfigs = { }
@@ -682,12 +689,11 @@ ${this.results.reduce((x, y) => {
682
689
* @param params Params to fetch the file with
683
690
* @return The parsed YAML file
684
691
*/
685
- async getSubOrgConfigs ( ) {
692
+ async getSubOrgConfigs ( ) {
686
693
try {
687
- if ( this . subOrgConfigMap ) {
688
- this . log . debug ( `SubOrg config was changed and the associated overridePaths is = ${ JSON . stringify ( this . subOrgConfigMap ) } ` )
689
- }
690
- const overridePaths = this . subOrgConfigMap || await this . getSubOrgConfigMap ( )
694
+ // Get all suborg configs even though we might be here becuase of a suborg config change
695
+ // we will filter them out if request is due to a suborg config change
696
+ const overridePaths = await this . getSubOrgConfigMap ( )
691
697
const subOrgConfigs = { }
692
698
693
699
for ( const override of overridePaths ) {
@@ -699,7 +705,19 @@ ${this.results.reduce((x, y) => {
699
705
subOrgConfigs [ override . name ] = data
700
706
if ( data . suborgrepos ) {
701
707
data . suborgrepos . forEach ( repository => {
702
- subOrgConfigs [ repository ] = data
708
+ this . storeSubOrgConfigIfNoConflicts ( subOrgConfigs , override . path , repository , data )
709
+
710
+ // In case support for multiple suborg configs for the same repo is required, merge the configs.
711
+ //
712
+ // Planned for the future to support multiple suborgrepos for the same repo
713
+ //
714
+ // if (existingConfigForRepo) {
715
+ // subOrgConfigs[repository] = this.mergeDeep.mergeDeep({}, existingConfigForRepo, data)
716
+ // } else {
717
+ // subOrgConfigs[repository] = data
718
+ // }
719
+
720
+ subOrgConfigs [ repository ] = Object . assign ( { } , data , { source : override . path } )
703
721
} )
704
722
}
705
723
if ( data . suborgteams ) {
@@ -709,7 +727,7 @@ ${this.results.reduce((x, y) => {
709
727
await Promise . all ( promises ) . then ( res => {
710
728
res . forEach ( r => {
711
729
r . forEach ( e => {
712
- subOrgConfigs [ e . name ] = data
730
+ this . storeSubOrgConfigIfNoConflicts ( subOrgConfigs , override . path , e . name , data )
713
731
} )
714
732
} )
715
733
} )
@@ -721,12 +739,26 @@ ${this.results.reduce((x, y) => {
721
739
await Promise . all ( promises ) . then ( res => {
722
740
res . forEach ( r => {
723
741
r . forEach ( e => {
724
- subOrgConfigs [ e . repository_name ] = data
742
+ this . storeSubOrgConfigIfNoConflicts ( subOrgConfigs , override . path , e . repository_name , data )
725
743
} )
726
744
} )
727
745
} )
728
746
}
729
747
}
748
+
749
+ // If this was result of a suborg config change, only return the repos that are part of the suborg config
750
+ if ( this . subOrgConfigMap ) {
751
+ this . log . debug ( `SubOrg config was changed and the associated overridePaths is = ${ JSON . stringify ( this . subOrgConfigMap ) } ` )
752
+ // enumerate the properties of the subOrgConfigs object and delete the ones that are not part of the suborg
753
+ for ( const [ key , value ] of Object . entries ( subOrgConfigs ) ) {
754
+ if ( ! this . subOrgConfigMap . some ( ( overridePath ) => {
755
+ return overridePath . path === value . source
756
+ }
757
+ ) ) {
758
+ delete subOrgConfigs [ key ]
759
+ }
760
+ }
761
+ }
730
762
return subOrgConfigs
731
763
} catch ( e ) {
732
764
if ( this . nop ) {
@@ -740,13 +772,21 @@ ${this.results.reduce((x, y) => {
740
772
}
741
773
}
742
774
775
+ storeSubOrgConfigIfNoConflicts ( subOrgConfigs , overridePath , repoName , data ) {
776
+ const existingConfigForRepo = subOrgConfigs [ repoName ]
777
+ if ( existingConfigForRepo && existingConfigForRepo . source !== overridePath ) {
778
+ throw new Error ( `Multiple suborg configs for ${ repoName } in ${ overridePath } and ${ existingConfigForRepo ?. source } ` )
779
+ }
780
+ subOrgConfigs [ repoName ] = Object . assign ( { } , data , { source : overridePath } )
781
+ }
782
+
743
783
/**
744
784
* Loads a file from GitHub
745
785
*
746
786
* @param params Params to fetch the file with
747
787
* @return The parsed YAML file
748
788
*/
749
- async loadYaml ( filePath ) {
789
+ async loadYaml ( filePath ) {
750
790
try {
751
791
const repo = { owner : this . repo . owner , repo : env . ADMIN_REPO }
752
792
const params = Object . assign ( repo , { path : filePath , ref : this . ref } )
@@ -783,13 +823,16 @@ ${this.results.reduce((x, y) => {
783
823
}
784
824
}
785
825
786
- appendToResults ( res ) {
826
+ appendToResults ( res ) {
787
827
if ( this . nop ) {
788
- this . results = this . results . concat ( res . flat ( 3 ) )
828
+ //Remove nulls and undefined from the results
829
+ const results = res . flat ( 3 ) . filter ( r => r )
830
+
831
+ this . results = this . results . concat ( results )
789
832
}
790
833
}
791
834
792
- async getReposForTeam ( teamslug ) {
835
+ async getReposForTeam ( teamslug ) {
793
836
const options = this . github . rest . teams . listReposInOrg . endpoint . merge ( {
794
837
org : this . repo . owner ,
795
838
team_slug : teamslug ,
@@ -798,20 +841,19 @@ ${this.results.reduce((x, y) => {
798
841
return this . github . paginate ( options )
799
842
}
800
843
801
- async getReposForCustomProperty ( customPropertyTuple ) {
802
- const name = Object . keys ( customPropertyTuple ) [ 0 ]
844
+ async getReposForCustomProperty ( customPropertyTuple ) {
845
+ const name = Object . keys ( customPropertyTuple ) [ 0 ]
803
846
let q = `props.${ name } :${ customPropertyTuple [ name ] } `
804
847
q = encodeURIComponent ( q )
805
848
const options = this . github . request . endpoint ( ( `/orgs/${ this . repo . owner } /properties/values?repository_query=${ q } ` ) )
806
849
return this . github . paginate ( options )
807
850
}
808
851
809
-
810
- isObject ( item ) {
852
+ isObject ( item ) {
811
853
return ( item && typeof item === 'object' && ! Array . isArray ( item ) )
812
854
}
813
855
814
- isIterable ( obj ) {
856
+ isIterable ( obj ) {
815
857
// checks for null and undefined
816
858
if ( obj == null ) {
817
859
return false
0 commit comments