Skip to content

Commit 0c5b35c

Browse files
committed
Updated ruleset overrides based on the latest api change.
1 parent c8926b8 commit 0c5b35c

File tree

5 files changed

+75
-17
lines changed

5 files changed

+75
-17
lines changed

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,15 @@ rulesets:
152152
```
153153

154154
Notes:
155-
- For branch protection rules, contexts defined at the org level are merged together with the sub-org and repo level contexts.
155+
- For the same branch that is covered by multi-level branch protection rules, contexts defined at the org level are merged into the sub-org and repo level contexts, while contexts defined at the sub-org level are merged into the repo level contexts.
156156
- When `{{EXTERNALLY_DEFINED}}` is defined for a new branch protection rule or ruleset configuration, they will be deployed with no status checks.
157157
- When an existing branch protection rule or ruleset configuration is amended with `{{EXTERNALLY_DEFINED}}`, the status checks in the existing rules in GitHub will remain as is.
158158

159-
⚠️ **Warning:**
160-
When `{{EXTERNALLY_DEFINED}}` is removed from an existing branch protection rule or ruleset configuration, the status checks in the existing rules in GitHub will revert to the checks that are defined in safe-settings.
159+
> ⚠️ **Warning:**
160+
When `{{EXTERNALLY_DEFINED}}` is removed from an existing branch protection rule or ruleset configuration, the status checks in the existing rules in GitHub will revert to the checks that are defined in safe-settings. From this point onwards, all status checks configured through the GitHub UI will be reverted back to the safe-settings configuration.
161+
162+
#### Status checks inheritance across scopes
163+
Refer to [Status checks](docs/status-checks.md).
161164

162165
### Performance
163166
When there are 1000s of repos to be managed -- and there is a global settings change -- safe-settings will have to work efficiently and only make the necessary API calls.

lib/plugins/branches.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@ const MergeDeep = require('../mergeDeep')
44
const Overrides = require('./overrides')
55
const ignorableFields = []
66
const previewHeaders = { accept: 'application/vnd.github.hellcat-preview+json,application/vnd.github.luke-cage-preview+json,application/vnd.github.zzzax-preview+json' }
7-
const overrides = [
8-
'contexts',
9-
]
7+
const overrides = {
8+
'contexts': {
9+
'action': 'reset',
10+
'type': 'array'
11+
},
12+
}
1013

1114
module.exports = class Branches extends ErrorStash {
1215
constructor (nop, github, repo, settings, log, errors) {

lib/plugins/overrides.js

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,46 @@ module.exports = class Overrides extends ErrorStash {
1818
return results
1919
}
2020

21+
static findParentObj (source, child, remove = false) {
22+
let parent = null
23+
const traverse = (obj, parentObj = null, parentKey = '') => {
24+
for (const key in obj) {
25+
if (obj[key] === child) {
26+
parent = obj
27+
if (remove && parentObj && parentKey) {
28+
delete parentObj[parentKey]
29+
}
30+
} else if (Array.isArray(obj[key])) {
31+
obj[key].forEach((element, index) => {
32+
if (element === child) {
33+
parent = obj[key]
34+
if (remove) {
35+
obj[key].splice(index, 1)
36+
}
37+
} else {
38+
traverse(element)
39+
}
40+
})
41+
} else if (typeof obj[key] === 'object' && obj[key]) {
42+
traverse(obj[key], obj, key)
43+
}
44+
}
45+
}
46+
traverse(source)
47+
return parent
48+
}
49+
50+
static removeParentObj (source, child, levels) {
51+
let parent = child
52+
for (let i = 0; i < levels; i++) {
53+
if (i + 1 === levels) {
54+
parent = Overrides.findParentObj(source, parent, true)
55+
} else {
56+
parent = Overrides.findParentObj(source, parent, false)
57+
}
58+
}
59+
}
60+
2161
// When {{EXTERNALLY_DEFINED}} is found in the override value, retain the
2262
// existing value from GitHub.
2363
// Note:
@@ -26,20 +66,22 @@ module.exports = class Overrides extends ErrorStash {
2666
// - The PUT method for rulesets (update) allows for multiple overrides.
2767
// - The POST method for rulesets (create) allows for one override only.
2868
static removeOverrides (overrides, source, existing) {
29-
overrides.forEach(override => {
69+
Object.entries(overrides).forEach(([override, props]) => {
3070
let sourceRefs = Overrides.getObjectRef(source, override)
3171
let data = JSON.stringify(sourceRefs)
3272
if (data.includes('{{EXTERNALLY_DEFINED}}')) {
3373
let existingRefs = Overrides.getObjectRef(existing, override)
3474
sourceRefs.forEach(sourceRef => {
3575
if (existingRefs[0]) {
3676
sourceRef[override] = existingRefs[0][override]
37-
} else if (Array.isArray(sourceRef[override])) {
77+
} else if (props['action'] === 'delete') {
78+
Overrides.removeParentObj(source, sourceRef[override], props['parents'])
79+
} else if (props['type'] === 'array') {
3880
sourceRef[override] = []
39-
} else if (typeof sourceRef[override] === 'object' && sourceRef[override]) {
81+
} else if (props['type'] === 'dict') {
4082
sourceRef[override] = {}
4183
} else {
42-
sourceRef[override] = ''
84+
throw new Error(`Unknown type ${props['type']} for ${override}`)
4385
}
4486
})
4587
}

lib/plugins/rulesets.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@ const NopCommand = require('../nopcommand')
33
const MergeDeep = require('../mergeDeep')
44
const Overrides = require('./overrides')
55
const ignorableFields = []
6-
const overrides = [
7-
'required_status_checks',
8-
]
6+
const overrides = {
7+
'required_status_checks': {
8+
'action': 'delete',
9+
'parents': 3,
10+
'type': 'dict'
11+
},
12+
}
913

1014
const version = {
1115
'X-GitHub-Api-Version': '2022-11-28'

test/unit/lib/plugins/rulesets.test.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const org_conditions = {
2323
}
2424

2525
function generateRequestRuleset(id, name, conditions, checks, org=false) {
26-
response = {
26+
request = {
2727
id: id,
2828
name: name,
2929
target: 'branch',
@@ -40,11 +40,14 @@ function generateRequestRuleset(id, name, conditions, checks, org=false) {
4040
]
4141
}
4242
if (org) {
43-
response.source_type = 'Organization'
43+
request.source_type = 'Organization'
4444
} else {
45-
response.source_type = 'Repository'
45+
request.source_type = 'Repository'
4646
}
47-
return response
47+
if (checks.length === 0) {
48+
request.rules = []
49+
}
50+
return request
4851
}
4952

5053
function generateResponseRuleset(id, name, conditions, checks, org=false) {
@@ -73,6 +76,9 @@ function generateResponseRuleset(id, name, conditions, checks, org=false) {
7376
response.owner = 'jitran'
7477
response.repo = 'test'
7578
}
79+
if (checks.length === 0) {
80+
response.rules = []
81+
}
7682
return response
7783
}
7884

0 commit comments

Comments
 (0)