Skip to content

Commit adb3469

Browse files
authored
Merge branch 'main-enterprise' into github-action-doc
2 parents b405eb9 + cabd7c2 commit adb3469

26 files changed

Lines changed: 867 additions & 368 deletions

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
> [!NOTE]
3131
> The `suborg` and `repo` level settings directory structure cannot be customized.
3232
>
33-
> Settings files must have a `.yml` extension only. For now, the `.yaml` extension is ignored.
3433
3534

3635
## How it works

docs/github-settings/6. deployment-environments.md

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,14 @@ environments:
2727
- type: User
2828
id: 139262123
2929
deployment_branch_policy:
30-
protected_branches: true
31-
custom_branch_policies: false
30+
protected_branches: false
31+
custom_branch_policies:
32+
- names: ['main','dev']
33+
type: branch
34+
- names: ['v*.*.*']
35+
type: tag
3236
deployment_protection_rules:
33-
- app_id: 25112
37+
- app_id: 25112
3438
variables:
3539
- name: MY_AWESOME_VAR
3640
value: '845705'
@@ -43,7 +47,8 @@ environments:
4347
>[!TIP]
4448
>GitHub's API documentation defines these inputs and types:
4549
>1. [Create or update an environment](https://docs.github.com/en/rest/deployments/environments?apiVersion=2022-11-28#create-or-update-an-environment)
46-
>2. [Create an environment variable](https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#create-an-environment-variable)
50+
>2. [Create a deployment branch policy](https://docs.github.com/en/rest/deployments/branch-policies?apiVersion=2022-11-28#create-a-deployment-branch-policy)
51+
>3. [Create an environment variable](https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#create-an-environment-variable)
4752
4853
<table>
4954
<tr><td>
@@ -126,11 +131,11 @@ environments:
126131

127132
<details><summary>Properties of <code>deployment_branch_policy</code></summary>
128133
<br>
129-
<p>&emsp;<code>protected_branches</code><span style="color:gray;">&emsp;<i>string</i>&emsp;</span><span style="color:orange;">${\text{\color{orange}Required}}$</span></p>
130-
<p>&emsp;&emsp;Whether only branches with branch protection rules can deploy<br>&emsp;&emsp;to this environment. If <code>protected_branches</code> is <code>true</code>,<br>&emsp;&emsp;<code>custom_branch_policies</code> must be <code>false</code>; if <code>protected_branches</code><br>&emsp;&emsp;is <code>false</code>, <code>custom_branch_policies</code> must be <code>true</code>.</p>
134+
<p>&emsp;<code>protected_branches</code><span style="color:gray;">&emsp;<i>boolean</i>&emsp;</span><span style="color:orange;">${\text{\color{orange}Required}}$</span></p>
135+
<p>&emsp;&emsp;Whether only branches with branch protection rules can deploy<br>&emsp;&emsp;to this environment. If <code>protected_branches</code> is <code>true</code>,<br>&emsp;&emsp;<code>custom_branch_policies</code> must be <code>false</code>; if <code>protected_branches</code><br>&emsp;&emsp;is <code>false</code>, <code>custom_branch_policies</code> must be an object.</p>
131136

132-
<p>&emsp;<code>id</code><span style="color:gray;">&emsp;<i>integer</i>&emsp;</span></p>
133-
<p>&emsp;&emsp;Whether only branches that match the specified name patterns<br>&emsp;&emsp;can deploy to this environment. If <code>custom_branch_policies</code><br>&emsp;&emsp;is <code>true</code>, <code>protected_branches</code> must be <code>false</code>; if<br>&emsp;&emsp;<code>custom_branch_policies</code> is <code>false</code>, <code>protected_branches</code><br>&emsp;&emsp;must be <code>true</code>.</p>
137+
<p>&emsp;<code>custom_branch_policies</code><span style="color:gray;">&emsp;<i>boolean or object</i>&emsp;</span></p>
138+
<p>&emsp;&emsp;Whether only branches that match the specified name patterns<br>&emsp;&emsp;can deploy to this environment. If <code>custom_branch_policies</code><br>&emsp;&emsp;is <code>false</code>, <code>protected_branches</code> must be <code>true</code>; if<br>&emsp;&emsp;<code>custom_branch_policies</code> is an object, <code>protected_branches</code><br>&emsp;&emsp;must be <code>false</code>.</p>
134139

135140
</details>
136141

@@ -142,8 +147,12 @@ environments:
142147
- name: production
143148
...
144149
deployment_branch_policy:
145-
protected_branches: true
146-
custom_branch_policies: false
150+
protected_branches: false
151+
custom_branch_policies:
152+
- names: ['main','dev']
153+
type: branch
154+
- names: ['v*.*.*']
155+
type: tag
147156
...
148157
```
149158

full-sync.js

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,28 @@
1-
const { createProbot } = require('probot')
21
const appFn = require('./')
2+
const { FULL_SYNC_NOP } = require('./lib/env')
3+
const { createProbot } = require('probot')
4+
5+
async function performFullSync (appFn, nop) {
6+
const probot = createProbot()
7+
probot.log.info(`Starting full sync with NOP=${nop}`)
38

4-
const probot = createProbot()
5-
probot.log.info('Starting full sync.')
6-
const app = appFn(probot, {})
7-
app.syncInstallation()
8-
.then(settings => {
9-
if (settings.errors.length > 0) {
9+
try {
10+
const app = appFn(probot, {})
11+
const settings = await app.syncInstallation(nop)
12+
13+
if (settings.errors && settings.errors.length > 0) {
1014
probot.log.error('Errors occurred during full sync.')
1115
process.exit(1)
12-
} else {
13-
probot.log.info('Done with full sync.')
1416
}
15-
})
16-
.catch(error => {
17+
18+
probot.log.info('Full sync completed successfully.')
19+
} catch (error) {
1720
process.stdout.write(`Unexpected error during full sync: ${error}\n`)
1821
process.exit(1)
19-
})
22+
}
23+
}
24+
25+
performFullSync(appFn, FULL_SYNC_NOP).catch((error) => {
26+
console.error('Fatal error during full sync:', error)
27+
process.exit(1)
28+
})

index.js

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ module.exports = (robot, { getRouter }, Settings = require('./lib/settings')) =>
2828
if (nop) {
2929
let filename = env.SETTINGS_FILE_PATH
3030
if (!deploymentConfig) {
31-
filename = env.DEPLOYMENT_CONFIG_FILE
31+
filename = env.DEPLOYMENT_CONFIG_FILE_PATH
3232
deploymentConfig = {}
3333
}
3434
const nopcommand = new NopCommand(filename, repo, null, e, 'ERROR')
@@ -53,7 +53,7 @@ module.exports = (robot, { getRouter }, Settings = require('./lib/settings')) =>
5353
if (nop) {
5454
let filename = env.SETTINGS_FILE_PATH
5555
if (!deploymentConfig) {
56-
filename = env.DEPLOYMENT_CONFIG_FILE
56+
filename = env.DEPLOYMENT_CONFIG_FILE_PATH
5757
deploymentConfig = {}
5858
}
5959
const nopcommand = new NopCommand(filename, repo, null, e, 'ERROR')
@@ -78,7 +78,7 @@ module.exports = (robot, { getRouter }, Settings = require('./lib/settings')) =>
7878
if (nop) {
7979
let filename = env.SETTINGS_FILE_PATH
8080
if (!deploymentConfig) {
81-
filename = env.DEPLOYMENT_CONFIG_FILE
81+
filename = env.DEPLOYMENT_CONFIG_FILE_PATH
8282
deploymentConfig = {}
8383
}
8484
const nopcommand = new NopCommand(filename, repo, null, e, 'ERROR')
@@ -104,7 +104,7 @@ module.exports = (robot, { getRouter }, Settings = require('./lib/settings')) =>
104104
if (nop) {
105105
let filename = env.SETTINGS_FILE_PATH
106106
if (!deploymentConfig) {
107-
filename = env.DEPLOYMENT_CONFIG_FILE
107+
filename = env.DEPLOYMENT_CONFIG_FILE_PATH
108108
deploymentConfig = {}
109109
}
110110
const nopcommand = new NopCommand(filename, repo, null, e, 'ERROR')
@@ -123,7 +123,7 @@ module.exports = (robot, { getRouter }, Settings = require('./lib/settings')) =>
123123
*/
124124
async function loadYamlFileSystem () {
125125
if (deploymentConfig === undefined) {
126-
const deploymentConfigPath = env.DEPLOYMENT_CONFIG_FILE
126+
const deploymentConfigPath = env.DEPLOYMENT_CONFIG_FILE_PATH
127127
if (fs.existsSync(deploymentConfigPath)) {
128128
deploymentConfig = yaml.load(fs.readFileSync(deploymentConfigPath))
129129
} else {
@@ -134,7 +134,7 @@ module.exports = (robot, { getRouter }, Settings = require('./lib/settings')) =>
134134
}
135135

136136
function getAllChangedSubOrgConfigs (payload) {
137-
const settingPattern = new Glob(`${env.CONFIG_PATH}/suborgs/*.yml`)
137+
const settingPattern = Settings.SUB_ORG_PATTERN
138138
// Changes will be an array of files that were added
139139
const added = payload.commits.map(c => {
140140
return (c.added.filter(s => {
@@ -158,7 +158,7 @@ module.exports = (robot, { getRouter }, Settings = require('./lib/settings')) =>
158158
}
159159

160160
function getAllChangedRepoConfigs (payload, owner) {
161-
const settingPattern = new Glob(`${env.CONFIG_PATH}/repos/*.yml`)
161+
const settingPattern = Settings.REPO_PATTERN
162162
// Changes will be an array of files that were added
163163
const added = payload.commits.map(c => {
164164
return (c.added.filter(s => {
@@ -230,7 +230,7 @@ module.exports = (robot, { getRouter }, Settings = require('./lib/settings')) =>
230230
}
231231
}
232232

233-
async function syncInstallation () {
233+
async function syncInstallation (nop = false) {
234234
robot.log.trace('Fetching installations')
235235
const github = await robot.auth()
236236

@@ -249,7 +249,7 @@ module.exports = (robot, { getRouter }, Settings = require('./lib/settings')) =>
249249
log: robot.log,
250250
repo: () => { return { repo: env.ADMIN_REPO, owner: installation.account.login } }
251251
}
252-
return syncAllSettings(false, context)
252+
return syncAllSettings(nop, context)
253253
}
254254
return null
255255
}
@@ -270,11 +270,11 @@ module.exports = (robot, { getRouter }, Settings = require('./lib/settings')) =>
270270
}
271271

272272
const settingsModified = payload.commits.find(commit => {
273-
return commit.added.includes(Settings.FILE_NAME) ||
274-
commit.modified.includes(Settings.FILE_NAME)
273+
return commit.added.includes(Settings.FILE_PATH) ||
274+
commit.modified.includes(Settings.FILE_PATH)
275275
})
276276
if (settingsModified) {
277-
robot.log.debug(`Changes in '${Settings.FILE_NAME}' detected, doing a full synch...`)
277+
robot.log.debug(`Changes in '${Settings.FILE_PATH}' detected, doing a full synch...`)
278278
return syncAllSettings(false, context)
279279
}
280280

@@ -292,7 +292,7 @@ module.exports = (robot, { getRouter }, Settings = require('./lib/settings')) =>
292292
}))
293293
}
294294

295-
robot.log.debug(`No changes in '${Settings.FILE_NAME}' detected, returning...`)
295+
robot.log.debug(`No changes in '${Settings.FILE_PATH}' detected, returning...`)
296296
})
297297

298298
robot.on('create', async context => {
@@ -597,21 +597,21 @@ module.exports = (robot, { getRouter }, Settings = require('./lib/settings')) =>
597597
const changes = await context.octokit.repos.compareCommitsWithBasehead(params)
598598
const files = changes.data.files.map(f => { return f.filename })
599599

600-
const settingsModified = files.includes(Settings.FILE_NAME)
600+
const settingsModified = files.includes(Settings.FILE_PATH)
601601

602602
if (settingsModified) {
603-
robot.log.debug(`Changes in '${Settings.FILE_NAME}' detected, doing a full synch...`)
603+
robot.log.debug(`Changes in '${Settings.FILE_PATH}' detected, doing a full synch...`)
604604
return syncAllSettings(true, context, context.repo(), pull_request.head.ref)
605605
}
606606

607-
const repoChanges = getChangedRepoConfigName(new Glob(`${env.CONFIG_PATH}/repos/*.yml`), files, context.repo().owner)
607+
const repoChanges = getChangedRepoConfigName(Settings.REPO_PATTERN, files, context.repo().owner)
608608
if (repoChanges.length > 0) {
609609
return Promise.all(repoChanges.map(repo => {
610610
return syncSettings(true, context, repo, pull_request.head.ref)
611611
}))
612612
}
613613

614-
const subOrgChanges = getChangedSubOrgConfigName(new Glob(`${env.CONFIG_PATH}/suborgs/*.yml`), files, context.repo().owner)
614+
const subOrgChanges = getChangedSubOrgConfigName(Settings.SUB_ORG_PATTERN, files, context.repo().owner)
615615
if (subOrgChanges.length) {
616616
return Promise.all(subOrgChanges.map(suborg => {
617617
return syncSubOrgSettings(true, context, suborg, context.repo(), pull_request.head.ref)

lib/deploymentConfig.js

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,23 @@ class DeploymentConfig {
1313
static overridevalidators = {}
1414

1515
static {
16-
const deploymentConfigPath = process.env.DEPLOYMENT_CONFIG_FILE ? process.env.DEPLOYMENT_CONFIG_FILE : 'deployment-settings.yml'
16+
const deploymentConfigPath = env.DEPLOYMENT_CONFIG_FILE_PATH
1717
if (fs.existsSync(deploymentConfigPath)) {
18-
this.config = yaml.load(fs.readFileSync(deploymentConfigPath))
18+
this.config = yaml.load(fs.readFileSync(deploymentConfigPath)) || {}
1919
} else {
2020
this.config = { restrictedRepos: ['admin', '.github', 'safe-settings'] }
2121
}
2222

23-
const overridevalidators = this.config.overridevalidators
24-
if (this.isIterable(overridevalidators)) {
23+
const overridevalidators = this.config.overridevalidators || []
24+
if (this.isNonEmptyArray(overridevalidators)) {
2525
for (const validator of overridevalidators) {
2626
// eslint-disable-next-line no-new-func
2727
const f = new Function('baseconfig', 'overrideconfig', 'githubContext', validator.script)
2828
this.overridevalidators[validator.plugin] = { canOverride: f, error: validator.error }
2929
}
3030
}
31-
const configvalidators = this.config.configvalidators
32-
if (this.isIterable(configvalidators)) {
31+
const configvalidators = this.config.configvalidators || []
32+
if (this.isNonEmptyArray(configvalidators)) {
3333
for (const validator of configvalidators) {
3434
// eslint-disable-next-line no-new-func
3535
const f = new Function('baseconfig', 'githubContext', validator.script)
@@ -38,12 +38,8 @@ class DeploymentConfig {
3838
}
3939
}
4040

41-
static isIterable (obj) {
42-
// checks for null and undefined
43-
if (obj == null) {
44-
return false
45-
}
46-
return typeof obj[Symbol.iterator] === 'function'
41+
static isNonEmptyArray (obj) {
42+
return Array.isArray(obj) && obj.length > 0
4743
}
4844

4945
// eslint-disable-next-line no-useless-constructor

lib/env.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ module.exports = {
22
ADMIN_REPO: process.env.ADMIN_REPO || 'admin',
33
CONFIG_PATH: process.env.CONFIG_PATH || '.github',
44
SETTINGS_FILE_PATH: process.env.SETTINGS_FILE_PATH || 'settings.yml',
5-
DEPLOYMENT_CONFIG_FILE: process.env.DEPLOYMENT_CONFIG_FILE || 'deployment-settings.yml',
5+
DEPLOYMENT_CONFIG_FILE_PATH: process.env.DEPLOYMENT_CONFIG_FILE || 'deployment-settings.yml',
66
CREATE_PR_COMMENT: process.env.CREATE_PR_COMMENT || 'true',
77
CREATE_ERROR_ISSUE: process.env.CREATE_ERROR_ISSUE || 'true',
8-
BLOCK_REPO_RENAME_BY_HUMAN: process.env.BLOCK_REPO_RENAME_BY_HUMAN || 'false'
8+
BLOCK_REPO_RENAME_BY_HUMAN: process.env.BLOCK_REPO_RENAME_BY_HUMAN || 'false',
9+
FULL_SYNC_NOP: process.env.FULL_SYNC_NOP === 'true'
910
}

lib/plugins/branches.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ module.exports = class Branches extends ErrorStash {
3333
let p = Object.assign(this.repo, { branch: branch.name })
3434
if (branch.name === 'default') {
3535
p = Object.assign(this.repo, { branch: currentRepo.data.default_branch })
36-
this.log(`Deleting default branch protection for branch ${currentRepo.data.default_branch}`)
36+
this.log.debug(`Deleting default branch protection for branch ${currentRepo.data.default_branch}`)
3737
}
3838
// Hack to handle closures and keep params from changing
3939
const params = Object.assign({}, p)
@@ -50,7 +50,7 @@ module.exports = class Branches extends ErrorStash {
5050
let p = Object.assign(this.repo, { branch: branch.name })
5151
if (branch.name === 'default') {
5252
p = Object.assign(this.repo, { branch: currentRepo.data.default_branch })
53-
// this.log(`Setting default branch protection for branch ${currentRepo.data.default_branch}`)
53+
// this.log.debug(`Setting default branch protection for branch ${currentRepo.data.default_branch}`)
5454
}
5555
// Hack to handle closures and keep params from changing
5656
const params = Object.assign({}, p)
@@ -80,7 +80,7 @@ module.exports = class Branches extends ErrorStash {
8080
return Promise.resolve(resArray)
8181
}
8282
this.log.debug(`Adding branch protection ${JSON.stringify(params)}`)
83-
return this.github.repos.updateBranchProtection(params).then(res => this.log(`Branch protection applied successfully ${JSON.stringify(res.url)}`)).catch(e => { this.logError(`Error applying branch protection ${JSON.stringify(e)}`); return [] })
83+
return this.github.repos.updateBranchProtection(params).then(res => this.log.debug(`Branch protection applied successfully ${JSON.stringify(res.url)}`)).catch(e => { this.logError(`Error applying branch protection ${JSON.stringify(e)}`); return [] })
8484
}).catch((e) => {
8585
if (e.status === 404) {
8686
Object.assign(params, Overrides.removeOverrides(overrides, branch.protection, {}), { headers: previewHeaders })
@@ -89,7 +89,7 @@ module.exports = class Branches extends ErrorStash {
8989
return Promise.resolve(resArray)
9090
}
9191
this.log.debug(`Adding branch protection ${JSON.stringify(params)}`)
92-
return this.github.repos.updateBranchProtection(params).then(res => this.log(`Branch protection applied successfully ${JSON.stringify(res.url)}`)).catch(e => { this.logError(`Error applying branch protection ${JSON.stringify(e)}`); return [] })
92+
return this.github.repos.updateBranchProtection(params).then(res => this.log.debug(`Branch protection applied successfully ${JSON.stringify(res.url)}`)).catch(e => { this.logError(`Error applying branch protection ${JSON.stringify(e)}`); return [] })
9393
} else {
9494
this.logError(e)
9595
if (this.nop) {

0 commit comments

Comments
 (0)