Skip to content

Commit ba7cb97

Browse files
authored
Merge branch 'main-enterprise' into repo-variables
2 parents 5905e15 + 3e5d4fe commit ba7cb97

File tree

15 files changed

+841
-630
lines changed

15 files changed

+841
-630
lines changed

.github/workflows/create-pre-release.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ jobs:
5858
username: ${{ github.actor }}
5959
password: ${{ secrets.GITHUB_TOKEN }}
6060
- name: Build Docker Image Locally
61-
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4
61+
uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0
6262
with:
6363
context: .
6464
file: ./Dockerfile
@@ -85,7 +85,7 @@ jobs:
8585
commitish: ${{ github.ref }}
8686
- name: Push Docker Image
8787
if: ${{ success() }}
88-
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4
88+
uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0
8989
with:
9090
context: .
9191
file: ./Dockerfile

.github/workflows/create-release.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
username: ${{ github.actor }}
3737
password: ${{ secrets.GITHUB_TOKEN }}
3838
- name: Build Docker Image Locally
39-
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4
39+
uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0
4040
with:
4141
context: .
4242
file: ./Dockerfile
@@ -59,7 +59,7 @@ jobs:
5959
bump: final
6060
- name: Push Docker Image
6161
if: ${{ success() }}
62-
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4
62+
uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0
6363
with:
6464
context: .
6565
file: ./Dockerfile

.github/workflows/deploy-k8s.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ jobs:
2929
steps:
3030
- name: Checkout repository
3131
uses: actions/checkout@v4
32-
- uses: azure/login@a65d910e8af852a8061c627c456678983e180302
32+
- uses: azure/login@a457da9ea143d694b1b9c7c869ebb04ebe844ef5
3333
with:
3434
client-id: ${{ secrets.AZURE_CLIENT_ID }}
3535
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
3636
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
37-
- uses: azure/aks-set-context@feeca6405be94202afcb1c395616ff29b1811b9f
37+
- uses: azure/aks-set-context@c7eb093e5a5d47caa333f64974d5fd1cd4bf069d
3838
with:
3939
resource-group: ${{env.AZURE_RESOURCE_GROUP}}
4040
cluster-name: ${{env.AZURE_AKS_CLUSTER}}

.github/workflows/rc-release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ jobs:
7676
- name: Set up Docker Buildx
7777
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2
7878
- name: Build and push Docker image
79-
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4
79+
uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0
8080
with:
8181
context: .
8282
push: true

NOTICE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -850,7 +850,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
850850
- isexe, 2.0.0, ISC,
851851
- json-stringify-safe, 5.0.1, ISC,
852852
- lru-cache, 6.0.0, ISC,
853-
- minimatch, 3.0.4, ISC,
853+
- minimatch, 10.0.1, ISC,
854854
- octokit-auth-probot, 1.2.3, ISC,
855855
- once, 1.4.0, ISC,
856856
- probot, 11.0.6, ISC,

README.md

Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,40 @@ The App listens to the following webhook events:
6464
If you rename a `<repo.yml>` that corresponds to a repo, safe-settings will rename the repo to the new name. This behavior will take effect whether the env variable `BLOCK_REPO_RENAME_BY_HUMAN` is set or not.
6565

6666
### Restricting `safe-settings` to specific repos
67-
`safe-settings` can be turned on only to a subset of repos by specifying them in the runtime settings file, `deployment-settings.yml`. If no file is specified, then the following repositories - `'admin', '.github', 'safe-settings'` are exempted by default.
68-
A sample of `deployment-settings` file is found [here](docs/sample-settings/sample-deployment-settings.yml).
69-
70-
To apply `safe-settings` __only__ to a specific list of repos, add them to the `restrictedRepos` section as `include` array.
7167

72-
To ignore `safe-settings` for a specific list of repos, add them to the `restrictedRepos` section as `exclude` array.
68+
To restrict which repositories `safe-settings` can manage, create a `deployment-settings.yml` file. This file controls the app's scope through the `restrictedRepos` configuration:
69+
70+
```yml
71+
# Using include/exclude
72+
restrictedRepos:
73+
include:
74+
- api
75+
- core-* # Matches `core-api`, `core-service`, etc.
76+
exclude:
77+
- admin
78+
- .github
79+
- safe-settings
80+
- test-* # Matches `test-repo`, etc.
81+
82+
# Or using simple array syntax for includes
83+
restrictedRepos:
84+
- admin
85+
- .github
86+
# ...
87+
```
7388

7489
> [!NOTE]
75-
> The `include` and `exclude` attributes support as well regular expressions.
76-
> By default they look for regex, Example include: ['SQL'] will look apply to repos with SQL and SQL_ and SQL- etc if you want only SQL repo then use include:['^SQL$']
90+
> Pattern matching uses glob expressions, e.g use * for wildcards.
91+
92+
When using `include` and `exclude`:
93+
94+
- If `include` is specified, will **only** run on repositories that match pattern(s)
95+
- If `exclude` is specified, will run on all repositories **except** those matching pattern(s)
96+
- If both are specified, will run only on included repositories that are'nt excluded
97+
98+
By default, if no configuration file is provided, `safe-settings` will excludes these repos: `admin`, `.github` and `safe-settings`.
99+
100+
See our [deployment-settings.yml sample](docs/sample-settings/sample-deployment-settings.yml).
77101

78102
### Custom rules
79103

@@ -329,24 +353,28 @@ The following can be configured:
329353
- `Rulesets`
330354
- `Environments` - wait timer, required reviewers, prevent self review, protected branches deployment branch policy, custom deployment branch policy, variables, deployment protection rules
331355

332-
> [!important]
333-
> It is possible to provide an `include` or `exclude` settings to restrict the `collaborators`, `teams`, `labels` to a list of repos or exclude a set of repos for a collaborator.
334-
> The include/exclude pattern can also be for glob. For e.g.:
335-
```
336-
teams:
337-
- name: Myteam-admins
338-
permission: admin
339-
- name: Myteam-developers
340-
permission: push
341-
- name: Other-team
342-
permission: push
343-
include:
344-
- '*-config'
345-
```
346-
> Will only add `Other-team` to only `*-config` repos
347-
348356
See [`docs/sample-settings/settings.yml`](docs/sample-settings/settings.yml) for a sample settings file.
349357

358+
> [!note]
359+
> When using `collaborators`, `teams` or `labels`, you can control which repositories they apply to using `include` and `exclude`:
360+
>
361+
> - If `include` is specified, settings will **only** apply to repositories that match those patterns
362+
> - If `exclude` is specified, settings will apply to all repositories **except** those matching the patterns
363+
> - If both are specified, `exclude` takes precedence over `include` but `include` patterns will still be respected
364+
>
365+
> Pattern matching uses glob expressions, e.g use * for wildcards. For example:
366+
>
367+
> ```yml
368+
> teams:
369+
> - name: Myteam-admins
370+
> permission: admin
371+
> - name: Myteam-developers
372+
> permission: push
373+
> - name: Other-team
374+
> permission: push
375+
> include:
376+
> - '*-config'
377+
> ```
350378

351379
### Additional values
352380

docs/sample-settings/sample-deployment-settings.yml

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,27 @@
1+
# This is a sample deployment settings file
2+
# See the documentation for more details on how to use this file
3+
4+
# If no file is specified, the following repositories are excluded by default
5+
# restrictedRepos: ['admin', '.github', 'safe-settings']
6+
17
restrictedRepos:
2-
# You can exclude certain repos from safe-settings processing
3-
# If no file is specified, then the following repositories - 'admin', '.github', 'safe-settings' are exempted by default
4-
exclude: ['^admin$', '^\.github$', '^safe-settings$', '.*-test']
5-
# Alternatively you can only include certain repos
6-
include: ['^test$']
8+
exclude:
9+
- admin
10+
- .github
11+
- safe-settings
12+
- admin-*
13+
include:
14+
- docs
15+
- core-*
16+
717
configvalidators:
818
- plugin: collaborators
919
error: |
1020
`Admin cannot be assigned to collaborators`
1121
script: |
1222
console.log(`baseConfig ${JSON.stringify(baseconfig)}`)
1323
return baseconfig.permission != 'admin'
24+
1425
overridevalidators:
1526
- plugin: branches
1627
error: |

index.js

Lines changed: 46 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -134,75 +134,60 @@ module.exports = (robot, { getRouter }, Settings = require('./lib/settings')) =>
134134
}
135135

136136
function getAllChangedSubOrgConfigs (payload) {
137-
const settingPattern = Settings.SUB_ORG_PATTERN
138-
// Changes will be an array of files that were added
139-
const added = payload.commits.map(c => {
140-
return (c.added.filter(s => {
141-
robot.log.debug(JSON.stringify(s))
142-
return (s.search(settingPattern) >= 0)
143-
}))
144-
}).flat(2)
145-
const modified = payload.commits.map(c => {
146-
return (c.modified.filter(s => {
147-
robot.log.debug(JSON.stringify(s))
148-
return (s.search(settingPattern) >= 0)
149-
}))
150-
}).flat(2)
151-
const changes = added.concat(modified)
152-
const configs = changes.map(file => {
153-
const matches = file.match(settingPattern)
154-
robot.log.debug(`${JSON.stringify(file)} \n ${matches[1]}`)
155-
return { name: matches[1] + '.yml', path: file }
156-
})
157-
return configs
137+
const pattern = Settings.SUB_ORG_PATTERN
138+
139+
const getMatchingFiles = (commits, type) =>
140+
commits.flatMap((c) => c[type].filter((file) => pattern.test(file)))
141+
142+
const changes = [
143+
...getMatchingFiles(payload.commits, 'added'),
144+
...getMatchingFiles(payload.commits, 'modified')
145+
]
146+
147+
return changes.map((file) => ({
148+
repo: file.match(/([^/]+)\.yml$/)[1],
149+
path: file
150+
}))
158151
}
159152

160153
function getAllChangedRepoConfigs (payload, owner) {
161-
const settingPattern = Settings.REPO_PATTERN
162-
// Changes will be an array of files that were added
163-
const added = payload.commits.map(c => {
164-
return (c.added.filter(s => {
165-
robot.log.debug(JSON.stringify(s))
166-
return (s.search(settingPattern) >= 0)
167-
}))
168-
}).flat(2)
169-
const modified = payload.commits.map(c => {
170-
return (c.modified.filter(s => {
171-
robot.log.debug(JSON.stringify(s))
172-
return (s.search(settingPattern) >= 0)
173-
}))
174-
}).flat(2)
175-
const changes = added.concat(modified)
176-
const configs = changes.map(file => {
177-
robot.log.debug(`${JSON.stringify(file)}`)
178-
return { repo: file.match(settingPattern)[1], owner }
179-
})
180-
return configs
181-
}
154+
const pattern = Settings.REPO_PATTERN
182155

183-
function getChangedRepoConfigName (glob, files, owner) {
184-
const modifiedFiles = files.filter(s => {
185-
robot.log.debug(JSON.stringify(s))
186-
return (s.search(glob) >= 0)
187-
})
156+
const getMatchingFiles = (commits, type) =>
157+
commits.flatMap((c) => c[type].filter((file) => pattern.test(file)))
188158

189-
return modifiedFiles.map(modifiedFile => {
190-
return { repo: modifiedFile.match(glob)[1], owner }
191-
})
159+
const changes = [
160+
...getMatchingFiles(payload.commits, 'added'),
161+
...getMatchingFiles(payload.commits, 'modified')
162+
]
163+
164+
return changes.map((file) => ({
165+
repo: file.match(/([^/]+)\.yml$/)[1],
166+
owner
167+
}))
192168
}
193169

194-
function getChangedSubOrgConfigName (glob, files) {
195-
const modifiedFiles = files.filter(s => {
196-
robot.log.debug(JSON.stringify(s))
197-
return (s.search(glob) >= 0)
198-
})
170+
function getChangedRepoConfigName (files, owner) {
171+
const pattern = Settings.REPO_PATTERN
199172

200-
return modifiedFiles.map(modifiedFile => {
201-
robot.log.debug(`${JSON.stringify(modifiedFile)}`)
202-
return { name: modifiedFile.match(glob)[1] + '.yml', path: modifiedFile }
203-
})
173+
const modifiedFiles = files.filter((s) => pattern.test(s))
174+
175+
return modifiedFiles.map((modifiedFile) => ({
176+
repo: modifiedFile.match(/([^/]+)\.yml$/)[1],
177+
owner
178+
}))
204179
}
205180

181+
function getChangedSubOrgConfigName (files) {
182+
const pattern = Settings.SUB_ORG_PATTERN
183+
184+
const modifiedFiles = files.filter((s) => pattern.test(s))
185+
186+
return modifiedFiles.map((modifiedFile) => ({
187+
name: modifiedFile.match(/([^/]+)\.yml$/)[1],
188+
path: modifiedFile
189+
}))
190+
}
206191
async function createCheckRun (context, pull_request, head_sha, head_branch) {
207192
const { payload } = context
208193
// robot.log.debug(`Check suite was requested! for ${context.repo()} ${pull_request.number} ${head_sha} ${head_branch}`)
@@ -604,14 +589,14 @@ module.exports = (robot, { getRouter }, Settings = require('./lib/settings')) =>
604589
return syncAllSettings(true, context, context.repo(), pull_request.head.ref)
605590
}
606591

607-
const repoChanges = getChangedRepoConfigName(Settings.REPO_PATTERN, files, context.repo().owner)
592+
const repoChanges = getChangedRepoConfigName(files, context.repo().owner)
608593
if (repoChanges.length > 0) {
609594
return Promise.all(repoChanges.map(repo => {
610595
return syncSettings(true, context, repo, pull_request.head.ref)
611596
}))
612597
}
613598

614-
const subOrgChanges = getChangedSubOrgConfigName(Settings.SUB_ORG_PATTERN, files, context.repo().owner)
599+
const subOrgChanges = getChangedSubOrgConfigName(files)
615600
if (subOrgChanges.length) {
616601
return Promise.all(subOrgChanges.map(suborg => {
617602
return syncSubOrgSettings(true, context, suborg, context.repo(), pull_request.head.ref)

lib/glob.js

Lines changed: 8 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,14 @@
1-
class Glob {
2-
constructor (glob) {
3-
this.glob = glob
4-
5-
// If not a glob pattern then just match the string.
6-
if (!this.glob.includes('*')) {
7-
this.regexp = new RegExp(`\\b${this.glob}\\b`, 'u')
8-
return
9-
}
10-
this.regexptText = this.globize(this.glob)
11-
this.regexp = new RegExp(`^${this.regexptText}$`, 'u')
12-
}
13-
14-
globize (glob) {
15-
return glob
16-
.replace(/\\/g, '\\\\') // escape backslashes
17-
.replace(/\//g, '\\/') // escape forward slashes
18-
.replace(/\./g, '\\.') // escape periods
19-
.replace(/\?/g, '([^\\/])') // match any single character except /
20-
.replace(/\*\*/g, '.+') // match any character except /, including /
21-
.replace(/\*/g, '([^\\/]*)') // match any character except /
22-
}
23-
24-
toString () {
25-
return this.glob
26-
}
1+
const { minimatch } = require('minimatch')
272

28-
[Symbol.search] (s) {
29-
return s.search(this.regexp)
30-
}
31-
32-
[Symbol.match] (s) {
33-
return s.match(this.regexp)
34-
}
35-
36-
[Symbol.replace] (s, replacement) {
37-
return s.replace(this.regexp, replacement)
3+
class Glob {
4+
constructor (pattern, options = {}) {
5+
this.pattern = pattern
6+
this.options = options
387
}
398

40-
[Symbol.replaceAll] (s, replacement) {
41-
return s.replaceAll(this.regexp, replacement)
9+
test (input) {
10+
return minimatch(input, this.pattern, this.options)
4211
}
4312
}
13+
4414
module.exports = Glob

0 commit comments

Comments
 (0)