Skip to content

Commit f534c6e

Browse files
committed
add initial code for preventing multiple suborg config for repos
1 parent f6c8d43 commit f534c6e

File tree

3 files changed

+200
-58
lines changed

3 files changed

+200
-58
lines changed

.eslintrc.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
{
22
"env": {
3-
"browser": true,
3+
"node": true,
44
"commonjs": true,
55
"es2021": true
66
},
77
"extends": [
8-
"standard"
8+
"eslint:recommended"
99
],
1010
"parserOptions": {
1111
"ecmaVersion": 12

lib/settings.js

Lines changed: 68 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class Settings {
2424
}
2525
}
2626

27-
static async syncSubOrgs (nop, context, suborg, repo, config, ref) {
27+
static async syncSubOrgs(nop, context, suborg, repo, config, ref) {
2828
const settings = new Settings(nop, context, repo, config, ref, suborg)
2929
try {
3030
await settings.loadConfigs()
@@ -36,7 +36,7 @@ class Settings {
3636
}
3737
}
3838

39-
static async sync (nop, context, repo, config, ref) {
39+
static async sync(nop, context, repo, config, ref) {
4040
const settings = new Settings(nop, context, repo, config, ref)
4141
try {
4242
await settings.loadConfigs(repo)
@@ -51,13 +51,13 @@ class Settings {
5151
}
5252
}
5353

54-
static async handleError (nop, context, repo, config, ref, nopcommand) {
54+
static async handleError(nop, context, repo, config, ref, nopcommand) {
5555
const settings = new Settings(nop, context, repo, config, ref)
5656
settings.appendToResults([nopcommand])
5757
await settings.handleResults()
5858
}
5959

60-
constructor (nop, context, repo, config, ref, suborg) {
60+
constructor(nop, context, repo, config, ref, suborg) {
6161
this.ref = ref
6262
this.context = context
6363
this.installation_id = context.payload.installation.id
@@ -96,7 +96,7 @@ class Settings {
9696
}
9797

9898
// Create a check in the Admin repo for safe-settings.
99-
async createCheckRun () {
99+
async createCheckRun() {
100100
const startTime = new Date()
101101
let conclusion = 'success'
102102
let details = `Run on: \`${new Date().toISOString()}\``
@@ -142,7 +142,7 @@ class Settings {
142142
})
143143
}
144144

145-
logError (msg) {
145+
logError(msg) {
146146
this.log.error(msg)
147147
this.errors.push({
148148
owner: this.repo.owner,
@@ -152,7 +152,7 @@ class Settings {
152152
})
153153
}
154154

155-
async handleResults () {
155+
async handleResults() {
156156
const { payload } = this.context
157157

158158
// Create a checkrun if not in nop mode
@@ -226,23 +226,23 @@ class Settings {
226226
#### :robot: Safe-Settings config changes detected:
227227
228228
${this.results.reduce((x, y) => {
229-
if (!y) {
230-
return x
231-
}
232-
if (y.type === 'ERROR') {
233-
error = true
234-
return `${x}
229+
if (!y) {
230+
return x
231+
}
232+
if (y.type === 'ERROR') {
233+
error = true
234+
return `${x}
235235
<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>`
236-
} else if (y.action.additions === null && y.action.deletions === null && y.action.modifications === null) {
237-
return `${x}`
238-
} else {
239-
if (y.action === undefined) {
240-
return `${x}`
241-
}
242-
return `${x}
236+
} else if (y.action.additions === null && y.action.deletions === null && y.action.modifications === null) {
237+
return `${x}`
238+
} else {
239+
if (y.action === undefined) {
240+
return `${x}`
241+
}
242+
return `${x}
243243
<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>`
244-
}
245-
}, table)}
244+
}
245+
}, table)}
246246
`
247247

248248
const pullRequest = payload.check_run.check_suite.pull_requests[0]
@@ -272,12 +272,12 @@ ${this.results.reduce((x, y) => {
272272
await this.github.checks.update(params)
273273
}
274274

275-
async loadConfigs (repo) {
275+
async loadConfigs(repo) {
276276
this.subOrgConfigs = await this.getSubOrgConfigs()
277277
this.repoConfigs = await this.getRepoConfigs(repo)
278278
}
279279

280-
async updateOrg () {
280+
async updateOrg() {
281281
const rulesetsConfig = this.config.rulesets
282282
if (rulesetsConfig) {
283283
const RulesetsPlugin = Settings.PLUGINS.rulesets
@@ -287,7 +287,7 @@ ${this.results.reduce((x, y) => {
287287
}
288288
}
289289

290-
async updateRepos (repo) {
290+
async updateRepos(repo) {
291291
this.subOrgConfigs = this.subOrgConfigs || await this.getSubOrgConfigs()
292292
let repoConfig = this.config.repository
293293
if (repoConfig) {
@@ -353,15 +353,15 @@ ${this.results.reduce((x, y) => {
353353
}
354354
}
355355

356-
async updateAll () {
356+
async updateAll() {
357357
// this.subOrgConfigs = this.subOrgConfigs || await this.getSubOrgConfigs(this.github, this.repo, this.log)
358358
// this.repoConfigs = this.repoConfigs || await this.getRepoConfigs(this.github, this.repo, this.log)
359359
return this.eachRepositoryRepos(this.github, this.config.restrictedRepos, this.log).then(res => {
360360
this.appendToResults(res)
361361
})
362362
}
363363

364-
getSubOrgConfig (repoName) {
364+
getSubOrgConfig(repoName) {
365365
if (this.subOrgConfigs) {
366366
for (const k of Object.keys(this.subOrgConfigs)) {
367367
const repoPattern = new Glob(k)
@@ -374,13 +374,13 @@ ${this.results.reduce((x, y) => {
374374
}
375375

376376
// Remove Org specific configs from the repo config
377-
returnRepoSpecificConfigs (config) {
377+
returnRepoSpecificConfigs(config) {
378378
const newConfig = Object.assign({}, config) // clone
379379
delete newConfig.rulesets
380380
return newConfig
381381
}
382382

383-
childPluginsList (repo) {
383+
childPluginsList(repo) {
384384
const repoName = repo.repo
385385
const subOrgOverrideConfig = this.getSubOrgConfig(repoName)
386386
this.log.debug(`suborg config for ${repoName} is ${JSON.stringify(subOrgOverrideConfig)}`)
@@ -412,7 +412,7 @@ ${this.results.reduce((x, y) => {
412412
return childPlugins
413413
}
414414

415-
validate (section, baseConfig, overrideConfig) {
415+
validate(section, baseConfig, overrideConfig) {
416416
const configValidator = this.configvalidators[section]
417417
if (configValidator) {
418418
this.log.debug(`Calling configvalidator for key ${section} `)
@@ -431,7 +431,7 @@ ${this.results.reduce((x, y) => {
431431
}
432432
}
433433

434-
isRestricted (repoName) {
434+
isRestricted(repoName) {
435435
const restrictedRepos = this.config.restrictedRepos
436436
// Skip configuring any restricted repos
437437
if (Array.isArray(restrictedRepos)) {
@@ -463,11 +463,11 @@ ${this.results.reduce((x, y) => {
463463
return false
464464
}
465465

466-
includesRepo (repoName, restrictedRepos) {
466+
includesRepo(repoName, restrictedRepos) {
467467
return restrictedRepos.filter((restrictedRepo) => { return RegExp(restrictedRepo).test(repoName) }).length > 0
468468
}
469469

470-
async eachRepositoryRepos (github, restrictedRepos, log) {
470+
async eachRepositoryRepos(github, restrictedRepos, log) {
471471
log.debug('Fetching repositories')
472472
return github.paginate('GET /installation/repositories').then(repositories => {
473473
return Promise.all(repositories.map(repository => {
@@ -488,7 +488,7 @@ ${this.results.reduce((x, y) => {
488488
* @param params Params to fetch the file with
489489
* @return The parsed YAML file
490490
*/
491-
async loadConfigMap (params) {
491+
async loadConfigMap(params) {
492492
try {
493493
this.log.debug(` In loadConfigMap ${JSON.stringify(params)}`)
494494
const response = await this.github.repos.getContent(params).catch(e => {
@@ -535,7 +535,7 @@ ${this.results.reduce((x, y) => {
535535
* @param params Params to fetch the file with
536536
* @return The parsed YAML file
537537
*/
538-
async getRepoConfigMap () {
538+
async getRepoConfigMap() {
539539
try {
540540
this.log.debug(` In getRepoConfigMap ${JSON.stringify(this.repo)}`)
541541
// GitHub getContent api has a hard limit of returning 1000 entries without
@@ -602,7 +602,7 @@ ${this.results.reduce((x, y) => {
602602
* @param params Params to fetch the file with
603603
* @return The parsed YAML file
604604
*/
605-
async getSubOrgConfigMap () {
605+
async getSubOrgConfigMap() {
606606
try {
607607
this.log.debug(` In getSubOrgConfigMap ${JSON.stringify(this.repo)}`)
608608
const repo = { owner: this.repo.owner, repo: env.ADMIN_REPO }
@@ -629,7 +629,7 @@ ${this.results.reduce((x, y) => {
629629
* @param {*} repo repo param
630630
* @returns repoConfigs object
631631
*/
632-
async getRepoConfigs (repo) {
632+
async getRepoConfigs(repo) {
633633
try {
634634
const overridePaths = await this.getRepoConfigMap()
635635
const repoConfigs = {}
@@ -681,7 +681,7 @@ ${this.results.reduce((x, y) => {
681681
* @param params Params to fetch the file with
682682
* @return The parsed YAML file
683683
*/
684-
async getSubOrgConfigs () {
684+
async getSubOrgConfigs() {
685685
try {
686686
if (this.subOrgConfigMap) {
687687
this.log.debug(`SubOrg config was changed and the associated overridePaths is = ${JSON.stringify(this.subOrgConfigMap)}`)
@@ -698,7 +698,19 @@ ${this.results.reduce((x, y) => {
698698
subOrgConfigs[override.name] = data
699699
if (data.suborgrepos) {
700700
data.suborgrepos.forEach(repository => {
701-
subOrgConfigs[repository] = data
701+
this.storeSubOrgConfig(subOrgConfigs, override.path, repository, data)
702+
703+
// In case support for multiple suborg configs for the same repo is required, merge the configs.
704+
//
705+
// Planned for the future to support multiple suborgrepos for the same repo
706+
//
707+
// if (existingConfigForRepo) {
708+
// subOrgConfigs[repository] = this.mergeDeep.mergeDeep({}, existingConfigForRepo, data)
709+
// } else {
710+
// subOrgConfigs[repository] = data
711+
// }
712+
713+
subOrgConfigs[repository] = Object.assign({}, data, { source: override.path })
702714
})
703715
}
704716
if (data.suborgteams) {
@@ -708,7 +720,7 @@ ${this.results.reduce((x, y) => {
708720
await Promise.all(promises).then(res => {
709721
res.forEach(r => {
710722
r.forEach(e => {
711-
subOrgConfigs[e.name] = data
723+
this.storeSubOrgConfig(subOrgConfigs, override.path, e.name, data)
712724
})
713725
})
714726
})
@@ -720,7 +732,7 @@ ${this.results.reduce((x, y) => {
720732
await Promise.all(promises).then(res => {
721733
res.forEach(r => {
722734
r.forEach(e => {
723-
subOrgConfigs[e.repository_name] = data
735+
this.storeSubOrgConfig(subOrgConfigs, override.path, e.repository_name, data)
724736
})
725737
})
726738
})
@@ -739,13 +751,21 @@ ${this.results.reduce((x, y) => {
739751
}
740752
}
741753

754+
storeSubOrgConfig(subOrgConfigs, overridePath, repoName, data) {
755+
const existingConfigForRepo = subOrgConfigs[repoName]
756+
if (existingConfigForRepo && existingConfigForRepo.source !== overridePath) {
757+
throw new Error(`Multiple suborg configs for ${repoName} in ${overridePath} and ${existingConfigForRepo?.source}`)
758+
}
759+
subOrgConfigs[repoName] = Object.assign({}, data, { source: overridePath })
760+
}
761+
742762
/**
743763
* Loads a file from GitHub
744764
*
745765
* @param params Params to fetch the file with
746766
* @return The parsed YAML file
747767
*/
748-
async loadYaml (filePath) {
768+
async loadYaml(filePath) {
749769
try {
750770
const repo = { owner: this.repo.owner, repo: env.ADMIN_REPO }
751771
const params = Object.assign(repo, { path: filePath, ref: this.ref })
@@ -782,13 +802,13 @@ ${this.results.reduce((x, y) => {
782802
}
783803
}
784804

785-
appendToResults (res) {
805+
appendToResults(res) {
786806
if (this.nop) {
787807
this.results = this.results.concat(res.flat(3))
788808
}
789809
}
790810

791-
async getReposForTeam (teamslug) {
811+
async getReposForTeam(teamslug) {
792812
const options = this.github.rest.teams.listReposInOrg.endpoint.merge({
793813
org: this.repo.owner,
794814
team_slug: teamslug,
@@ -797,20 +817,19 @@ ${this.results.reduce((x, y) => {
797817
return this.github.paginate(options)
798818
}
799819

800-
async getReposForCustomProperty (customPropertyTuple) {
801-
const name=Object.keys(customPropertyTuple)[0]
820+
async getReposForCustomProperty(customPropertyTuple) {
821+
const name = Object.keys(customPropertyTuple)[0]
802822
let q = `props.${name}:${customPropertyTuple[name]}`
803823
q = encodeURIComponent(q)
804824
const options = this.github.request.endpoint((`/orgs/${this.repo.owner}/properties/values?repository_query=${q}`))
805825
return this.github.paginate(options)
806826
}
807827

808-
809-
isObject (item) {
828+
isObject(item) {
810829
return (item && typeof item === 'object' && !Array.isArray(item))
811830
}
812831

813-
isIterable (obj) {
832+
isIterable(obj) {
814833
// checks for null and undefined
815834
if (obj == null) {
816835
return false

0 commit comments

Comments
 (0)