@repository-settings/app 2.2.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +227 -0
- package/index.js +51 -0
- package/lib/mergeArrayByName.js +28 -0
- package/lib/plugins/branches.js +33 -0
- package/lib/plugins/collaborators.js +51 -0
- package/lib/plugins/diffable.js +56 -0
- package/lib/plugins/environments.js +180 -0
- package/lib/plugins/labels.js +49 -0
- package/lib/plugins/milestones.js +50 -0
- package/lib/plugins/repository.js +68 -0
- package/lib/plugins/teams.js +50 -0
- package/lib/settings.js +47 -0
- package/package.json +68 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2017, Brandon Keepers
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# GitHub Settings
|
|
2
|
+
|
|
3
|
+
<!--status-badges start -->
|
|
4
|
+
|
|
5
|
+
[![Powered by Vercel][vercel-badge]][vercel-link]
|
|
6
|
+
|
|
7
|
+
[![Node CI Workflow Status][github-actions-ci-badge]][github-actions-ci-link]
|
|
8
|
+
[![Renovate][renovate-badge]][renovate-link]
|
|
9
|
+
|
|
10
|
+
<!--status-badges end -->
|
|
11
|
+
|
|
12
|
+
This GitHub App syncs repository settings defined in `.github/settings.yml` to GitHub, enabling Pull Requests for repository settings.
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
1. __[Install the app](https://github.com/apps/settings)__.
|
|
17
|
+
1. Create a `.github/settings.yml` file in your repository. Changes to this file on the default branch will be synced to GitHub.
|
|
18
|
+
|
|
19
|
+
All top-level settings are optional. Some plugins do have required fields.
|
|
20
|
+
|
|
21
|
+
```yaml
|
|
22
|
+
# These settings are synced to GitHub by https://probot.github.io/apps/settings/
|
|
23
|
+
|
|
24
|
+
repository:
|
|
25
|
+
# See https://docs.github.com/en/rest/reference/repos#update-a-repository for all available settings.
|
|
26
|
+
|
|
27
|
+
# The name of the repository. Changing this will rename the repository
|
|
28
|
+
name: repo-name
|
|
29
|
+
|
|
30
|
+
# A short description of the repository that will show up on GitHub
|
|
31
|
+
description: description of repo
|
|
32
|
+
|
|
33
|
+
# A URL with more information about the repository
|
|
34
|
+
homepage: https://example.github.io/
|
|
35
|
+
|
|
36
|
+
# A comma-separated list of topics to set on the repository
|
|
37
|
+
topics: github, probot
|
|
38
|
+
|
|
39
|
+
# Either `true` to make the repository private, or `false` to make it public.
|
|
40
|
+
private: false
|
|
41
|
+
|
|
42
|
+
# Either `true` to enable issues for this repository, `false` to disable them.
|
|
43
|
+
has_issues: true
|
|
44
|
+
|
|
45
|
+
# Either `true` to enable projects for this repository, or `false` to disable them.
|
|
46
|
+
# If projects are disabled for the organization, passing `true` will cause an API error.
|
|
47
|
+
has_projects: true
|
|
48
|
+
|
|
49
|
+
# Either `true` to enable the wiki for this repository, `false` to disable it.
|
|
50
|
+
has_wiki: true
|
|
51
|
+
|
|
52
|
+
# Either `true` to enable downloads for this repository, `false` to disable them.
|
|
53
|
+
has_downloads: true
|
|
54
|
+
|
|
55
|
+
# Updates the default branch for this repository.
|
|
56
|
+
default_branch: master
|
|
57
|
+
|
|
58
|
+
# Either `true` to allow squash-merging pull requests, or `false` to prevent
|
|
59
|
+
# squash-merging.
|
|
60
|
+
allow_squash_merge: true
|
|
61
|
+
|
|
62
|
+
# Either `true` to allow merging pull requests with a merge commit, or `false`
|
|
63
|
+
# to prevent merging pull requests with merge commits.
|
|
64
|
+
allow_merge_commit: true
|
|
65
|
+
|
|
66
|
+
# Either `true` to allow rebase-merging pull requests, or `false` to prevent
|
|
67
|
+
# rebase-merging.
|
|
68
|
+
allow_rebase_merge: true
|
|
69
|
+
|
|
70
|
+
# Either `true` to enable automatic deletion of branches on merge, or `false` to disable
|
|
71
|
+
delete_branch_on_merge: true
|
|
72
|
+
|
|
73
|
+
# Either `true` to enable automated security fixes, or `false` to disable
|
|
74
|
+
# automated security fixes.
|
|
75
|
+
enable_automated_security_fixes: true
|
|
76
|
+
|
|
77
|
+
# Either `true` to enable vulnerability alerts, or `false` to disable
|
|
78
|
+
# vulnerability alerts.
|
|
79
|
+
enable_vulnerability_alerts: true
|
|
80
|
+
|
|
81
|
+
# Labels: define labels for Issues and Pull Requests
|
|
82
|
+
labels:
|
|
83
|
+
- name: bug
|
|
84
|
+
color: CC0000
|
|
85
|
+
description: An issue with the system 🐛.
|
|
86
|
+
|
|
87
|
+
- name: feature
|
|
88
|
+
# If including a `#`, make sure to wrap it with quotes!
|
|
89
|
+
color: '#336699'
|
|
90
|
+
description: New functionality.
|
|
91
|
+
|
|
92
|
+
- name: Help Wanted
|
|
93
|
+
# Provide a new name to rename an existing label
|
|
94
|
+
new_name: first-timers-only
|
|
95
|
+
|
|
96
|
+
# Milestones: define milestones for Issues and Pull Requests
|
|
97
|
+
milestones:
|
|
98
|
+
- title: milestone-title
|
|
99
|
+
description: milestone-description
|
|
100
|
+
# The state of the milestone. Either `open` or `closed`
|
|
101
|
+
state: open
|
|
102
|
+
|
|
103
|
+
# Collaborators: give specific users access to this repository.
|
|
104
|
+
# See https://docs.github.com/en/rest/reference/repos#add-a-repository-collaborator for available options
|
|
105
|
+
collaborators:
|
|
106
|
+
# - username: bkeepers
|
|
107
|
+
# permission: push
|
|
108
|
+
# - username: hubot
|
|
109
|
+
# permission: pull
|
|
110
|
+
|
|
111
|
+
# Note: `permission` is only valid on organization-owned repositories.
|
|
112
|
+
# The permission to grant the collaborator. Can be one of:
|
|
113
|
+
# * `pull` - can pull, but not push to or administer this repository.
|
|
114
|
+
# * `push` - can pull and push, but not administer this repository.
|
|
115
|
+
# * `admin` - can pull, push and administer this repository.
|
|
116
|
+
# * `maintain` - Recommended for project managers who need to manage the repository without access to sensitive or destructive actions.
|
|
117
|
+
# * `triage` - Recommended for contributors who need to proactively manage issues and pull requests without write access.
|
|
118
|
+
|
|
119
|
+
# See https://docs.github.com/en/rest/deployments/environments#create-or-update-an-environment for available options
|
|
120
|
+
# Note: deployment_branch_policy differs from the API for ease of use. Either protected_branches (boolean) OR custom_branches (array of strings) can be provided; this will manage the API requirements under the hood. See https://docs.github.com/en/rest/deployments/branch-policies for documentation of custom_branches. If both are provided in an unexpected manner, protected_branches will be used.
|
|
121
|
+
# Either removing or simply not setting deployment_branch_policy will restore the default 'All branches' setting.
|
|
122
|
+
environments:
|
|
123
|
+
- name: production
|
|
124
|
+
wait_timer: 5
|
|
125
|
+
reviewers:
|
|
126
|
+
- id: 1
|
|
127
|
+
type: 'Team'
|
|
128
|
+
- id: 2
|
|
129
|
+
type: 'User'
|
|
130
|
+
deployment_branch_policy:
|
|
131
|
+
protected_branches: true
|
|
132
|
+
- name: development
|
|
133
|
+
deployment_branch_policy:
|
|
134
|
+
custom_branches:
|
|
135
|
+
- main
|
|
136
|
+
- dev/*
|
|
137
|
+
|
|
138
|
+
# See https://docs.github.com/en/rest/reference/teams#add-or-update-team-repository-permissions for available options
|
|
139
|
+
teams:
|
|
140
|
+
- name: core
|
|
141
|
+
# The permission to grant the team. Can be one of:
|
|
142
|
+
# * `pull` - can pull, but not push to or administer this repository.
|
|
143
|
+
# * `push` - can pull and push, but not administer this repository.
|
|
144
|
+
# * `admin` - can pull, push and administer this repository.
|
|
145
|
+
# * `maintain` - Recommended for project managers who need to manage the repository without access to sensitive or destructive actions.
|
|
146
|
+
# * `triage` - Recommended for contributors who need to proactively manage issues and pull requests without write access.
|
|
147
|
+
permission: admin
|
|
148
|
+
- name: docs
|
|
149
|
+
permission: push
|
|
150
|
+
|
|
151
|
+
branches:
|
|
152
|
+
- name: master
|
|
153
|
+
# https://docs.github.com/en/rest/reference/repos#update-branch-protection
|
|
154
|
+
# Branch Protection settings. Set to null to disable
|
|
155
|
+
protection:
|
|
156
|
+
# Required. Require at least one approving review on a pull request, before merging. Set to null to disable.
|
|
157
|
+
required_pull_request_reviews:
|
|
158
|
+
# The number of approvals required. (1-6)
|
|
159
|
+
required_approving_review_count: 1
|
|
160
|
+
# Dismiss approved reviews automatically when a new commit is pushed.
|
|
161
|
+
dismiss_stale_reviews: true
|
|
162
|
+
# Blocks merge until code owners have reviewed.
|
|
163
|
+
require_code_owner_reviews: true
|
|
164
|
+
# Specify which users and teams can dismiss pull request reviews. Pass an empty dismissal_restrictions object to disable. User and team dismissal_restrictions are only available for organization-owned repositories. Omit this parameter for personal repositories.
|
|
165
|
+
dismissal_restrictions:
|
|
166
|
+
users: []
|
|
167
|
+
teams: []
|
|
168
|
+
# Required. Require status checks to pass before merging. Set to null to disable
|
|
169
|
+
required_status_checks:
|
|
170
|
+
# Required. Require branches to be up to date before merging.
|
|
171
|
+
strict: true
|
|
172
|
+
# Required. The list of status checks to require in order to merge into this branch
|
|
173
|
+
contexts: []
|
|
174
|
+
# Required. Enforce all configured restrictions for administrators. Set to true to enforce required status checks for repository administrators. Set to null to disable.
|
|
175
|
+
enforce_admins: true
|
|
176
|
+
# Prevent merge commits from being pushed to matching branches
|
|
177
|
+
required_linear_history: true
|
|
178
|
+
# Required. Restrict who can push to this branch. Team and user restrictions are only available for organization-owned repositories. Set to null to disable.
|
|
179
|
+
restrictions:
|
|
180
|
+
apps: []
|
|
181
|
+
users: []
|
|
182
|
+
teams: []
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Notes
|
|
186
|
+
|
|
187
|
+
1. Label color can also start with `#`, e.g. `color: '#F341B2'`. Make sure to wrap it with quotes!
|
|
188
|
+
1. Each top-level element under branch protection must be filled (eg: `required_pull_request_reviews`, `required_status_checks`, `enforce_admins` and `restrictions`). If you don't want to use one of them you must set it to `null` (see comments in the example above). Otherwise, none of the settings will be applied.
|
|
189
|
+
|
|
190
|
+
### Inheritance
|
|
191
|
+
|
|
192
|
+
This app is built with [probot](https://github.com/probot/probot), and thus uses the [octokit-plugin-config](https://github.com/probot/octokit-plugin-config). This means you can inherit settings from another repo, and only override what you want to change.
|
|
193
|
+
|
|
194
|
+
Individual settings in the arrays listed under `labels`, `teams` (once it is supported) and `branches` will be merged with the base repo if the `name` of an element in the array matches the `name` of an element in the corresponding array in the base repo. A possible future enhancement would be to make that work for the other settings arrays based on `username`, or `title`. This is not currently supported.
|
|
195
|
+
|
|
196
|
+
To further clarify: Inheritance within the Protected Branches plugin allows you to override specific settings per branch. For example, your `.github` repo may set default protection on the `master` branch. You can then include `master` in your `branches` array, and only override the `required_approving_review_count`.
|
|
197
|
+
Alternatively, you might only have a branch like `develop` in your `branches` array, and would still get `master` protection from your base repo.
|
|
198
|
+
|
|
199
|
+
## Security Implications
|
|
200
|
+
|
|
201
|
+
__WARNING:__ Note that this app inherently _escalates anyone with `push` permissions to the __admin__ role_, since they can push config settings to the `master` branch, which will be synced. In a future, we may add restrictions to allow changes to the config file to be merged only by specific people/teams, or those with __admin__ access _(via a combination of protected branches, required statuses, and branch restrictions)_. Until then, use caution when merging PRs and adding collaborators.
|
|
202
|
+
|
|
203
|
+
Until restrictions are added in this app, one way to preserve admin/push permissions is to utilize the [GitHub CodeOwners feature](https://help.github.com/articles/about-codeowners/) to set one or more administrative users as the code owner of the `.github/settings.yml` file, and turn on "require code owner review" for the master branch. This does have the side effect of requiring code owner review for the entire branch, but helps preserve permission levels.
|
|
204
|
+
|
|
205
|
+
## Deployment
|
|
206
|
+
|
|
207
|
+
<!--consumer-badges start -->
|
|
208
|
+
|
|
209
|
+
![node][node-badge]
|
|
210
|
+
|
|
211
|
+
<!--consumer-badges end -->
|
|
212
|
+
|
|
213
|
+
See [docs/deploy.md](docs/deploy.md) if you would like to run your own instance of this plugin.
|
|
214
|
+
|
|
215
|
+
[github-actions-ci-link]: https://github.com/repository-settings/app/actions?query=workflow%3A%22Node.js+CI%22+branch%3Amaster
|
|
216
|
+
|
|
217
|
+
[github-actions-ci-badge]: https://github.com/repository-settings/app/workflows/Node.js%20CI/badge.svg
|
|
218
|
+
|
|
219
|
+
[renovate-link]: https://renovatebot.com
|
|
220
|
+
|
|
221
|
+
[renovate-badge]: https://img.shields.io/badge/renovate-enabled-brightgreen.svg?logo=renovatebot
|
|
222
|
+
|
|
223
|
+
[node-badge]: https://img.shields.io/node/v/probot-settings?logo=node.js
|
|
224
|
+
|
|
225
|
+
[vercel-badge]: https://github.com/repository-settings/app/raw/master/assets/powered-by-vercel.svg
|
|
226
|
+
|
|
227
|
+
[vercel-link]: https://vercel.com?utm_source=repository-settings&utm_campaign=oss
|
package/index.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const mergeArrayByName = require('./lib/mergeArrayByName')
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {import('probot').Probot} robot
|
|
5
|
+
*/
|
|
6
|
+
module.exports = (robot, _, Settings = require('./lib/settings')) => {
|
|
7
|
+
async function syncSettings (context, repo = context.repo()) {
|
|
8
|
+
const config = await context.config('settings.yml', {}, { arrayMerge: mergeArrayByName })
|
|
9
|
+
return Settings.sync(context.octokit, repo, config)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
robot.on('push', async context => {
|
|
13
|
+
const { payload } = context
|
|
14
|
+
const { repository } = payload
|
|
15
|
+
|
|
16
|
+
const defaultBranch = payload.ref === 'refs/heads/' + repository.default_branch
|
|
17
|
+
if (!defaultBranch) {
|
|
18
|
+
robot.log.debug('Not working on the default branch, returning...')
|
|
19
|
+
return
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const settingsModified = payload.commits.find(commit => {
|
|
23
|
+
return commit.added.includes(Settings.FILE_NAME) || commit.modified.includes(Settings.FILE_NAME)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
if (!settingsModified) {
|
|
27
|
+
robot.log.debug(`No changes in '${Settings.FILE_NAME}' detected, returning...`)
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return syncSettings(context)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
robot.on('repository.edited', async context => {
|
|
35
|
+
const { payload } = context
|
|
36
|
+
const { changes, repository } = payload
|
|
37
|
+
|
|
38
|
+
if (!Object.prototype.hasOwnProperty.call(changes, 'default_branch')) {
|
|
39
|
+
robot.log.debug('Repository configuration was edited but the default branch was not affected, returning...')
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
robot.log.debug(`Default branch changed from '${changes.default_branch.from}' to '${repository.default_branch}'`)
|
|
44
|
+
|
|
45
|
+
return syncSettings(context)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
robot.on('repository.created', async context => {
|
|
49
|
+
return syncSettings(context)
|
|
50
|
+
})
|
|
51
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// https://github.com/KyleAMathews/deepmerge#arraymerge
|
|
2
|
+
|
|
3
|
+
const merge = require('deepmerge')
|
|
4
|
+
|
|
5
|
+
function findMatchingIndex (sourceItem, target) {
|
|
6
|
+
if (Object.prototype.hasOwnProperty.call(sourceItem, 'name')) {
|
|
7
|
+
return target
|
|
8
|
+
.filter(targetItem => Object.prototype.hasOwnProperty.call(targetItem, 'name'))
|
|
9
|
+
.findIndex(targetItem => sourceItem.name === targetItem.name)
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function mergeByName (target, source, options) {
|
|
14
|
+
const destination = target.slice()
|
|
15
|
+
|
|
16
|
+
source.forEach(sourceItem => {
|
|
17
|
+
const matchingIndex = findMatchingIndex(sourceItem, target)
|
|
18
|
+
if (matchingIndex > -1) {
|
|
19
|
+
destination[matchingIndex] = merge(target[matchingIndex], sourceItem, options)
|
|
20
|
+
} else {
|
|
21
|
+
destination.push(sourceItem)
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
return destination
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
module.exports = mergeByName
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const previewHeaders = {
|
|
2
|
+
accept:
|
|
3
|
+
'application/vnd.github.hellcat-preview+json,application/vnd.github.luke-cage-preview+json,application/vnd.github.zzzax-preview+json'
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
module.exports = class Branches {
|
|
7
|
+
constructor (github, repo, settings) {
|
|
8
|
+
this.github = github
|
|
9
|
+
this.repo = repo
|
|
10
|
+
this.branches = settings
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
sync () {
|
|
14
|
+
return Promise.all(
|
|
15
|
+
this.branches
|
|
16
|
+
.filter(branch => branch.protection !== undefined)
|
|
17
|
+
.map(branch => {
|
|
18
|
+
const params = Object.assign(this.repo, { branch: branch.name })
|
|
19
|
+
|
|
20
|
+
if (this.isEmpty(branch.protection)) {
|
|
21
|
+
return this.github.repos.deleteBranchProtection(params)
|
|
22
|
+
} else {
|
|
23
|
+
Object.assign(params, branch.protection, { headers: previewHeaders })
|
|
24
|
+
return this.github.repos.updateBranchProtection(params)
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
isEmpty (maybeEmpty) {
|
|
31
|
+
return maybeEmpty === null || Object.keys(maybeEmpty).length === 0
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const Diffable = require('./diffable')
|
|
2
|
+
|
|
3
|
+
module.exports = class Collaborators extends Diffable {
|
|
4
|
+
constructor (...args) {
|
|
5
|
+
super(...args)
|
|
6
|
+
|
|
7
|
+
if (this.entries) {
|
|
8
|
+
// Force all usernames to lowercase to avoid comparison issues.
|
|
9
|
+
this.entries.forEach(collaborator => {
|
|
10
|
+
collaborator.username = collaborator.username.toLowerCase()
|
|
11
|
+
})
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
find () {
|
|
16
|
+
return this.github.repos
|
|
17
|
+
.listCollaborators({ repo: this.repo.repo, owner: this.repo.owner, affiliation: 'direct' })
|
|
18
|
+
.then(res => {
|
|
19
|
+
return res.data.map(user => {
|
|
20
|
+
return {
|
|
21
|
+
// Force all usernames to lowercase to avoid comparison issues.
|
|
22
|
+
username: user.login.toLowerCase(),
|
|
23
|
+
permission:
|
|
24
|
+
(user.permissions.admin && 'admin') ||
|
|
25
|
+
(user.permissions.push && 'push') ||
|
|
26
|
+
(user.permissions.pull && 'pull')
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
comparator (existing, attrs) {
|
|
33
|
+
return existing.username === attrs.username
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
changed (existing, attrs) {
|
|
37
|
+
return existing.permission !== attrs.permission
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
update (existing, attrs) {
|
|
41
|
+
return this.add(attrs)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
add (attrs) {
|
|
45
|
+
return this.github.repos.addCollaborator(Object.assign({}, attrs, this.repo))
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
remove (existing) {
|
|
49
|
+
return this.github.repos.removeCollaborator(Object.assign({ username: existing.username }, this.repo))
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Base class to make it easy to check for changes to a list of items
|
|
2
|
+
//
|
|
3
|
+
// class Thing extends Diffable {
|
|
4
|
+
// find() {
|
|
5
|
+
// }
|
|
6
|
+
//
|
|
7
|
+
// comparator(existing, attrs) {
|
|
8
|
+
// }
|
|
9
|
+
//
|
|
10
|
+
// changed(existing, attrs) {
|
|
11
|
+
// }
|
|
12
|
+
//
|
|
13
|
+
// update(existing, attrs) {
|
|
14
|
+
// }
|
|
15
|
+
//
|
|
16
|
+
// add(attrs) {
|
|
17
|
+
// }
|
|
18
|
+
//
|
|
19
|
+
// remove(existing) {
|
|
20
|
+
// }
|
|
21
|
+
// }
|
|
22
|
+
module.exports = class Diffable {
|
|
23
|
+
constructor (github, repo, entries) {
|
|
24
|
+
this.github = github
|
|
25
|
+
this.repo = repo
|
|
26
|
+
this.entries = entries
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
sync () {
|
|
30
|
+
if (this.entries) {
|
|
31
|
+
return this.find().then(existingRecords => {
|
|
32
|
+
const changes = []
|
|
33
|
+
|
|
34
|
+
this.entries.forEach(attrs => {
|
|
35
|
+
const existing = existingRecords.find(record => {
|
|
36
|
+
return this.comparator(record, attrs)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
if (!existing) {
|
|
40
|
+
changes.push(this.add(attrs))
|
|
41
|
+
} else if (this.changed(existing, attrs)) {
|
|
42
|
+
changes.push(this.update(existing, attrs))
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
existingRecords.forEach(x => {
|
|
47
|
+
if (!this.entries.find(y => this.comparator(x, y))) {
|
|
48
|
+
changes.push(this.remove(x))
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
return Promise.all(changes)
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
const Diffable = require('./diffable')
|
|
2
|
+
|
|
3
|
+
const environmentRepoEndpoint = '/repos/:org/:repo/environments/:environment_name'
|
|
4
|
+
|
|
5
|
+
module.exports = class Environments extends Diffable {
|
|
6
|
+
constructor (...args) {
|
|
7
|
+
super(...args)
|
|
8
|
+
|
|
9
|
+
if (this.entries) {
|
|
10
|
+
// Force all names to lowercase to avoid comparison issues.
|
|
11
|
+
this.entries.forEach(environment => {
|
|
12
|
+
environment.name = environment.name.toLowerCase()
|
|
13
|
+
})
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async find () {
|
|
18
|
+
const {
|
|
19
|
+
data: { environments }
|
|
20
|
+
} = await this.github.request('GET /repos/:org/:repo/environments', {
|
|
21
|
+
org: this.repo.owner,
|
|
22
|
+
repo: this.repo.repo
|
|
23
|
+
})
|
|
24
|
+
return Promise.all(
|
|
25
|
+
environments.map(async environment => {
|
|
26
|
+
if (environment.deployment_branch_policy) {
|
|
27
|
+
if (environment.deployment_branch_policy.custom_branch_policies) {
|
|
28
|
+
const branchPolicies = await this.getDeploymentBranchPolicies(
|
|
29
|
+
this.repo.owner,
|
|
30
|
+
this.repo.repo,
|
|
31
|
+
environment.name
|
|
32
|
+
)
|
|
33
|
+
environment.deployment_branch_policy = {
|
|
34
|
+
custom_branches: branchPolicies.map(_ => _.name)
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
environment.deployment_branch_policy = {
|
|
38
|
+
protected_branches: true
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
...environment,
|
|
44
|
+
// Force all names to lowercase to avoid comparison issues.
|
|
45
|
+
name: environment.name.toLowerCase()
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
comparator (existing, attrs) {
|
|
52
|
+
return existing.name === attrs.name
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
changed (existing, attrs) {
|
|
56
|
+
if (!attrs.wait_timer) attrs.wait_timer = 0
|
|
57
|
+
return (
|
|
58
|
+
(existing.wait_timer || 0) !== attrs.wait_timer ||
|
|
59
|
+
this.reviewersToString(existing.reviewers) !== this.reviewersToString(attrs.reviewers) ||
|
|
60
|
+
this.deploymentBranchPolicyToString(existing.deployment_branch_policy) !==
|
|
61
|
+
this.deploymentBranchPolicyToString(attrs.deployment_branch_policy)
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async update (existing, attrs) {
|
|
66
|
+
if (existing.deployment_branch_policy && existing.deployment_branch_policy.custom_branches) {
|
|
67
|
+
const branchPolicies = await this.getDeploymentBranchPolicies(this.repo.owner, this.repo.repo, existing.name)
|
|
68
|
+
await Promise.all(
|
|
69
|
+
branchPolicies.map(branchPolicy =>
|
|
70
|
+
this.github.request(
|
|
71
|
+
'DELETE /repos/:org/:repo/environments/:environment_name/deployment-branch-policies/:id',
|
|
72
|
+
{
|
|
73
|
+
org: this.repo.owner,
|
|
74
|
+
repo: this.repo.repo,
|
|
75
|
+
environment_name: existing.name,
|
|
76
|
+
id: branchPolicy.id
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
)
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
return this.add(attrs)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async add (attrs) {
|
|
86
|
+
await this.github.request(`PUT ${environmentRepoEndpoint}`, this.toParams({ name: attrs.name }, attrs))
|
|
87
|
+
if (attrs.deployment_branch_policy && attrs.deployment_branch_policy.custom_branches) {
|
|
88
|
+
await Promise.all(
|
|
89
|
+
attrs.deployment_branch_policy.custom_branches.map(name =>
|
|
90
|
+
this.github.request(`POST /repos/:org/:repo/environments/:environment_name/deployment-branch-policies`, {
|
|
91
|
+
org: this.repo.owner,
|
|
92
|
+
repo: this.repo.repo,
|
|
93
|
+
environment_name: attrs.name,
|
|
94
|
+
name
|
|
95
|
+
})
|
|
96
|
+
)
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
remove (existing) {
|
|
102
|
+
return this.github.request(`DELETE ${environmentRepoEndpoint}`, {
|
|
103
|
+
environment_name: existing.name,
|
|
104
|
+
repo: this.repo.repo,
|
|
105
|
+
org: this.repo.owner
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
reviewersToString (attrs) {
|
|
110
|
+
if (attrs === null || attrs === undefined) {
|
|
111
|
+
return ''
|
|
112
|
+
} else {
|
|
113
|
+
attrs.sort((a, b) => {
|
|
114
|
+
if (a.id < b.id) return -1
|
|
115
|
+
if (a.id > b.id) return 1
|
|
116
|
+
if (a.type < b.type) return -1
|
|
117
|
+
if (a.type > b.type) return 1
|
|
118
|
+
return 0
|
|
119
|
+
})
|
|
120
|
+
return JSON.stringify(
|
|
121
|
+
attrs.map(reviewer => {
|
|
122
|
+
return {
|
|
123
|
+
id: reviewer.id,
|
|
124
|
+
type: reviewer.type
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
deploymentBranchPolicyToString (attrs) {
|
|
132
|
+
if (attrs === null || attrs === undefined) {
|
|
133
|
+
return ''
|
|
134
|
+
} else {
|
|
135
|
+
return JSON.stringify(
|
|
136
|
+
this.shouldUseProtectedBranches(attrs.protected_branches, attrs.custom_branches)
|
|
137
|
+
? { protected_branches: true }
|
|
138
|
+
: { custom_branches: attrs.custom_branches.sort() }
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async getDeploymentBranchPolicies (owner, repo, environmentName) {
|
|
144
|
+
const {
|
|
145
|
+
data: { branch_policies: branchPolicies }
|
|
146
|
+
} = await this.github.request('GET /repos/:org/:repo/environments/:environment_name/deployment-branch-policies', {
|
|
147
|
+
org: owner,
|
|
148
|
+
repo,
|
|
149
|
+
environment_name: environmentName
|
|
150
|
+
})
|
|
151
|
+
return branchPolicies
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
toParams (existing, attrs) {
|
|
155
|
+
const deploymentBranchPolicy = attrs.deployment_branch_policy
|
|
156
|
+
? this.shouldUseProtectedBranches(
|
|
157
|
+
attrs.deployment_branch_policy.protected_branches,
|
|
158
|
+
attrs.deployment_branch_policy.custom_branches
|
|
159
|
+
)
|
|
160
|
+
? { protected_branches: true, custom_branch_policies: false }
|
|
161
|
+
: { protected_branches: false, custom_branch_policies: true }
|
|
162
|
+
: null
|
|
163
|
+
return {
|
|
164
|
+
environment_name: existing.name,
|
|
165
|
+
repo: this.repo.repo,
|
|
166
|
+
org: this.repo.owner,
|
|
167
|
+
wait_timer: attrs.wait_timer,
|
|
168
|
+
reviewers: attrs.reviewers,
|
|
169
|
+
deployment_branch_policy: deploymentBranchPolicy
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
shouldUseProtectedBranches (protectedBranches, customBranchPolicies) {
|
|
174
|
+
if (protectedBranches || customBranchPolicies === undefined || customBranchPolicies === null) {
|
|
175
|
+
return true // Returning booleans like this to avoid unexpected datatypes that result in truthy values
|
|
176
|
+
} else {
|
|
177
|
+
return false
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const Diffable = require('./diffable')
|
|
2
|
+
const previewHeaders = { accept: 'application/vnd.github.symmetra-preview+json' }
|
|
3
|
+
|
|
4
|
+
module.exports = class Labels extends Diffable {
|
|
5
|
+
constructor (...args) {
|
|
6
|
+
super(...args)
|
|
7
|
+
|
|
8
|
+
if (this.entries) {
|
|
9
|
+
this.entries.forEach(label => {
|
|
10
|
+
// Force color to string since some hex colors can be numerical (e.g. 999999)
|
|
11
|
+
if (label.color) {
|
|
12
|
+
label.color = String(label.color).replace(/^#/, '')
|
|
13
|
+
if (label.color.length < 6) {
|
|
14
|
+
label.color = label.color.padStart(6, '0')
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
find () {
|
|
22
|
+
const options = this.github.issues.listLabelsForRepo.endpoint.merge(this.wrapAttrs({ per_page: 100 }))
|
|
23
|
+
return this.github.paginate(options)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
comparator (existing, attrs) {
|
|
27
|
+
return existing.name === attrs.name
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
changed (existing, attrs) {
|
|
31
|
+
return 'new_name' in attrs || existing.color !== attrs.color || existing.description !== attrs.description
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
update (existing, attrs) {
|
|
35
|
+
return this.github.issues.updateLabel(this.wrapAttrs(attrs))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
add (attrs) {
|
|
39
|
+
return this.github.issues.createLabel(this.wrapAttrs(attrs))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
remove (existing) {
|
|
43
|
+
return this.github.issues.deleteLabel(this.wrapAttrs({ name: existing.name }))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
wrapAttrs (attrs) {
|
|
47
|
+
return Object.assign({}, attrs, this.repo, { headers: previewHeaders })
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const Diffable = require('./diffable')
|
|
2
|
+
|
|
3
|
+
module.exports = class Milestones extends Diffable {
|
|
4
|
+
constructor (...args) {
|
|
5
|
+
super(...args)
|
|
6
|
+
|
|
7
|
+
if (this.entries) {
|
|
8
|
+
this.entries.forEach(milestone => {
|
|
9
|
+
if (milestone.due_on) {
|
|
10
|
+
delete milestone.due_on
|
|
11
|
+
}
|
|
12
|
+
})
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
find () {
|
|
17
|
+
const options = this.github.issues.listMilestones.endpoint.merge(
|
|
18
|
+
Object.assign({ per_page: 100, state: 'all' }, this.repo)
|
|
19
|
+
)
|
|
20
|
+
return this.github.paginate(options)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
comparator (existing, attrs) {
|
|
24
|
+
return existing.title === attrs.title
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
changed (existing, attrs) {
|
|
28
|
+
return existing.description !== attrs.description || existing.state !== attrs.state
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
update (existing, attrs) {
|
|
32
|
+
const { owner, repo } = this.repo
|
|
33
|
+
|
|
34
|
+
return this.github.issues.updateMilestone(
|
|
35
|
+
Object.assign({ milestone_number: existing.number }, attrs, { owner, repo })
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
add (attrs) {
|
|
40
|
+
const { owner, repo } = this.repo
|
|
41
|
+
|
|
42
|
+
return this.github.issues.createMilestone(Object.assign({}, attrs, { owner, repo }))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
remove (existing) {
|
|
46
|
+
const { owner, repo } = this.repo
|
|
47
|
+
|
|
48
|
+
return this.github.issues.deleteMilestone(Object.assign({ milestone_number: existing.number }, { owner, repo }))
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const enableAutomatedSecurityFixes = ({ github, settings, enabled }) => {
|
|
2
|
+
if (enabled === undefined) {
|
|
3
|
+
return Promise.resolve()
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const args = {
|
|
7
|
+
owner: settings.owner,
|
|
8
|
+
repo: settings.repo,
|
|
9
|
+
mediaType: {
|
|
10
|
+
previews: ['london']
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
const methodName = enabled ? 'enableAutomatedSecurityFixes' : 'disableAutomatedSecurityFixes'
|
|
14
|
+
|
|
15
|
+
return github.repos[methodName](args)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const enableVulnerabilityAlerts = ({ github, settings, enabled }) => {
|
|
19
|
+
if (enabled === undefined) {
|
|
20
|
+
return Promise.resolve()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const args = {
|
|
24
|
+
owner: settings.owner,
|
|
25
|
+
repo: settings.repo,
|
|
26
|
+
mediaType: {
|
|
27
|
+
previews: ['dorian']
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const methodName = enabled ? 'enableVulnerabilityAlerts' : 'disableVulnerabilityAlerts'
|
|
31
|
+
|
|
32
|
+
return github.repos[methodName](args)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = class Repository {
|
|
36
|
+
constructor (github, repo, settings) {
|
|
37
|
+
this.github = github
|
|
38
|
+
this.settings = Object.assign({ mediaType: { previews: ['baptiste'] } }, settings, repo)
|
|
39
|
+
this.topics = this.settings.topics
|
|
40
|
+
delete this.settings.topics
|
|
41
|
+
|
|
42
|
+
this.enableVulnerabilityAlerts = this.settings.enable_vulnerability_alerts
|
|
43
|
+
delete this.settings.enable_vulnerability_alerts
|
|
44
|
+
|
|
45
|
+
this.enableAutomatedSecurityFixes = this.settings.enable_automated_security_fixes
|
|
46
|
+
delete this.settings.enable_automated_security_fixes
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
sync () {
|
|
50
|
+
this.settings.name = this.settings.name || this.settings.repo
|
|
51
|
+
return this.github.repos
|
|
52
|
+
.update(this.settings)
|
|
53
|
+
.then(() => {
|
|
54
|
+
if (this.topics) {
|
|
55
|
+
return this.github.repos.replaceAllTopics({
|
|
56
|
+
owner: this.settings.owner,
|
|
57
|
+
repo: this.settings.repo,
|
|
58
|
+
names: this.topics.split(/\s*,\s*/),
|
|
59
|
+
mediaType: {
|
|
60
|
+
previews: ['mercy']
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
.then(() => enableVulnerabilityAlerts({ enabled: this.enableVulnerabilityAlerts, ...this }))
|
|
66
|
+
.then(() => enableAutomatedSecurityFixes({ enabled: this.enableAutomatedSecurityFixes, ...this }))
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const Diffable = require('./diffable')
|
|
2
|
+
|
|
3
|
+
// it is necessary to use this endpoint until GitHub Enterprise supports
|
|
4
|
+
// the modern version under /orgs
|
|
5
|
+
const teamRepoEndpoint = '/teams/:team_id/repos/:owner/:repo'
|
|
6
|
+
|
|
7
|
+
module.exports = class Teams extends Diffable {
|
|
8
|
+
find () {
|
|
9
|
+
return this.github.repos.listTeams(this.repo).then(res => res.data)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
comparator (existing, attrs) {
|
|
13
|
+
return existing.slug === attrs.name
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
changed (existing, attrs) {
|
|
17
|
+
return existing.permission !== attrs.permission
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
update (existing, attrs) {
|
|
21
|
+
return this.github.request(`PUT ${teamRepoEndpoint}`, this.toParams(existing, attrs))
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async add (attrs) {
|
|
25
|
+
const { data: existing } = await this.github.request('GET /orgs/:org/teams/:team_slug', {
|
|
26
|
+
org: this.repo.owner,
|
|
27
|
+
team_slug: attrs.name
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
return this.github.request(`PUT ${teamRepoEndpoint}`, this.toParams(existing, attrs))
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
remove (existing) {
|
|
34
|
+
return this.github.request(`DELETE ${teamRepoEndpoint}`, {
|
|
35
|
+
team_id: existing.id,
|
|
36
|
+
...this.repo,
|
|
37
|
+
org: this.repo.owner
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
toParams (existing, attrs) {
|
|
42
|
+
return {
|
|
43
|
+
team_id: existing.id,
|
|
44
|
+
owner: this.repo.owner,
|
|
45
|
+
repo: this.repo.repo,
|
|
46
|
+
org: this.repo.owner,
|
|
47
|
+
permission: attrs.permission
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
package/lib/settings.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
class Settings {
|
|
2
|
+
static sync (github, repo, config) {
|
|
3
|
+
return new Settings(github, repo, config).update()
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
constructor (github, repo, config) {
|
|
7
|
+
this.github = github
|
|
8
|
+
this.repo = repo
|
|
9
|
+
this.config = config
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
update () {
|
|
13
|
+
const { branches, ...rest } = this.config
|
|
14
|
+
|
|
15
|
+
return Promise.all(
|
|
16
|
+
Object.entries(rest).map(([section, config]) => {
|
|
17
|
+
return this.processSection(section, config)
|
|
18
|
+
})
|
|
19
|
+
).then(() => {
|
|
20
|
+
if (branches) {
|
|
21
|
+
return this.processSection('branches', branches)
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
processSection (section, config) {
|
|
27
|
+
const debug = { repo: this.repo }
|
|
28
|
+
debug[section] = config
|
|
29
|
+
|
|
30
|
+
const Plugin = Settings.PLUGINS[section]
|
|
31
|
+
return new Plugin(this.github, this.repo, config).sync()
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
Settings.FILE_NAME = '.github/settings.yml'
|
|
36
|
+
|
|
37
|
+
Settings.PLUGINS = {
|
|
38
|
+
repository: require('./plugins/repository'),
|
|
39
|
+
labels: require('./plugins/labels'),
|
|
40
|
+
collaborators: require('./plugins/collaborators'),
|
|
41
|
+
environments: require('./plugins/environments'),
|
|
42
|
+
teams: require('./plugins/teams'),
|
|
43
|
+
milestones: require('./plugins/milestones'),
|
|
44
|
+
branches: require('./plugins/branches')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = Settings
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@repository-settings/app",
|
|
3
|
+
"version": "2.2.0-beta.2",
|
|
4
|
+
"description": "Pull Requests for GitHub repository settings",
|
|
5
|
+
"repository": "github:repository-settings/app",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "nodemon",
|
|
9
|
+
"start": "probot run ./index.js",
|
|
10
|
+
"test": "npm-run-all --print-label --parallel lint:* --parallel test:*",
|
|
11
|
+
"lint:js": "prettier-standard --lint --check",
|
|
12
|
+
"lint:js:fix": "prettier-standard --format --lint",
|
|
13
|
+
"lint:lockfile": "lockfile-lint --path package-lock.json --type npm --validate-https --allowed-hosts npm",
|
|
14
|
+
"lint:engines": "ls-engines",
|
|
15
|
+
"lint:peer": "npm ls >/dev/null",
|
|
16
|
+
"test:unit": "jest 'test/unit/'",
|
|
17
|
+
"test:unit:watch": "npm run test:unit -- --watch",
|
|
18
|
+
"test:integration": "jest 'test/integration/'",
|
|
19
|
+
"test:integration:debug": "LOG_LEVEL=debug DEBUG=nock.* run-s test:integration"
|
|
20
|
+
},
|
|
21
|
+
"author": "Brandon Keepers",
|
|
22
|
+
"license": "ISC",
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"deepmerge": "4.3.1",
|
|
25
|
+
"js-yaml": "4.1.0",
|
|
26
|
+
"probot": "12.3.3"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@travi/any": "3.0.2",
|
|
30
|
+
"http-status-codes": "2.3.0",
|
|
31
|
+
"jest": "29.7.0",
|
|
32
|
+
"jest-when": "3.6.0",
|
|
33
|
+
"lockfile-lint": "4.12.1",
|
|
34
|
+
"ls-engines": "0.9.1",
|
|
35
|
+
"nock": "13.4.0",
|
|
36
|
+
"nodemon": "3.0.2",
|
|
37
|
+
"npm-run-all2": "6.1.1",
|
|
38
|
+
"prettier-standard": "16.4.1",
|
|
39
|
+
"smee-client": "2.0.0",
|
|
40
|
+
"standard": "17.1.0"
|
|
41
|
+
},
|
|
42
|
+
"standard": {
|
|
43
|
+
"env": [
|
|
44
|
+
"jest"
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=16"
|
|
49
|
+
},
|
|
50
|
+
"jest": {
|
|
51
|
+
"testEnvironment": "node"
|
|
52
|
+
},
|
|
53
|
+
"nodemonConfig": {
|
|
54
|
+
"exec": "npm start",
|
|
55
|
+
"watch": [
|
|
56
|
+
".env",
|
|
57
|
+
"."
|
|
58
|
+
]
|
|
59
|
+
},
|
|
60
|
+
"files": [
|
|
61
|
+
"index.js",
|
|
62
|
+
"lib/"
|
|
63
|
+
],
|
|
64
|
+
"publishConfig": {
|
|
65
|
+
"access": "public",
|
|
66
|
+
"provenance": true
|
|
67
|
+
}
|
|
68
|
+
}
|