chore: add content

This commit is contained in:
dessant
2019-01-23 13:53:21 +02:00
commit 8a448b8a6f
15 changed files with 5735 additions and 0 deletions

9
.env.example Normal file
View File

@@ -0,0 +1,9 @@
# The ID of your GitHub App
APP_ID=
WEBHOOK_SECRET=development
# Use `trace` to get verbose logging or `info` to show less
LOG_LEVEL=debug
# Go to https://smee.io/new and set this to the URL that you are redirected to
# WEBHOOK_PROXY_URL=

10
.gitignore vendored Normal file
View File

@@ -0,0 +1,10 @@
.data/
.env
*.pem
node_modules/
npm-debug.log
yarn-debug.log
yarn-error.log
.yarn-integrity

1
.nvmrc Normal file
View File

@@ -0,0 +1 @@
8.12.0

1
.prettierignore Normal file
View File

@@ -0,0 +1 @@
package.json

2
.prettierrc Normal file
View File

@@ -0,0 +1,2 @@
singleQuote: true
bracketSpacing: false

12
.travis.yml Normal file
View File

@@ -0,0 +1,12 @@
language: node_js
cache: yarn
script: ""
deploy:
- provider: npm
email: $NPM_EMAIL
api_key: $NPM_TOKEN
on:
tags: true

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Armin Sebastian
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

102
README.md Normal file
View File

@@ -0,0 +1,102 @@
# Label Actions
[![Build Status](https://img.shields.io/travis/com/dessant/label-actions/master.svg)](https://travis-ci.com/dessant/label-actions)
[![Version](https://img.shields.io/npm/v/label-actions.svg?colorB=007EC6)](https://www.npmjs.com/package/label-actions)
Label Actions is a GitHub App built with [Probot](https://github.com/probot/probot)
that performs actions when issues or pull requests are labeled or unlabeled.
## Supporting the Project
The continued development of Label Actions is made possible
thanks to the support of awesome backers. If you'd like to join them,
please consider contributing with [Patreon](https://www.patreon.com/dessant),
[PayPal](https://www.paypal.me/ArminSebastian) or [Bitcoin](https://goo.gl/uJUAaU).
## How It Works
The app performs certain actions when an issue or pull request
is labeled or unlabeled. No action is taken by default and the app
must be configured. The following actions are supported:
* Post a comment (`comment` option)
* Close (`close` option)
* Reopen (`open` option)
* Lock with an optional lock reason (`lock` and `lockReason` options)
* Unlock (`unlock` option)
## Usage
1. **[Install the GitHub App](https://github.com/apps/label-actions)**
for the intended repositories
2. Create `.github/label-actions.yml` based on the template below
3. Start labeling issues and pull requests
⚠️ **If possible, install the app only for select repositories.
Do not leave the `All repositories` option selected, unless you intend
to use the app for all current and future repositories.**
#### Configuration
Create `.github/label-actions.yml` in the default branch to enable the app,
or add it at the same file path to a special repository named `.github`.
Configure the app by editing the following template:
```yaml
# Configuration for Label Actions - https://github.com/dessant/label-actions
# Specify actions for issues and pull requests
actions:
# Actions taken when the `heated` label is added
heated:
# Post a comment
comment: >
The thread has been temporarily locked.
Please follow our community guidelines.
# Lock the thread
lock: true
# Set a lock reason, such as `off-topic`, `too heated`, `resolved` or `spam`
lockReason: "too heated"
# Actions taken when the `heated` label is removed
-heated:
# Unlock the thread
unlock: true
# Optionally, specify configuration settings just for issues
issues:
actions:
feature:
# Post a comment, `{issue-author}` is an optional placeholder
comment: >
:wave: @{issue-author}, please use our idea board to request new features.
# Close the issue
close: true
-wontfix:
# Reopen the issue
open: true
# Optionally, specify configuration settings just for pull requests
pulls:
actions:
pizzazz:
comment: >
![](https://i.imgur.com/WuduJNk.jpg)
# Limit to only `issues` or `pulls`
# only: issues
# Repository to extend settings from
# _extends: repo
```
## Deployment
See [docs/deploy.md](docs/deploy.md) if you would like to run your own
instance of this app.
## License
Copyright (c) 2019 Armin Sebastian
This software is released under the terms of the MIT License.
See the [LICENSE](LICENSE) file for further information.

74
assets/app-description.md Normal file
View File

@@ -0,0 +1,74 @@
A GitHub App that performs actions when issues or pull requests are labeled or unlabeled.
## How It Works
The app performs certain actions when an issue or pull request is labeled or unlabeled. No action is taken by default and the app must be configured. The following actions are supported:
* Post a comment (`comment` option)
* Close (`close` option)
* Reopen (`open` option)
* Lock with an optional lock reason (`lock` and `lockReason` options)
* Unlock (`unlock` option)
## Usage
1. **[Install the GitHub App](https://github.com/apps/label-actions)** for the intended repositories
2. Create `.github/label-actions.yml` based on the template below
3. Start labeling issues and pull requests
⚠️ **If possible, install the app only for select repositories. Do not leave the `All repositories` option selected, unless you intend to use the app for all current and future repositories.**
#### Configuration
Create `.github/label-actions.yml` in the default branch to enable the app, or add it at the same file path to a special repository named `.github`. Configure the app by editing the following template:
```yaml
# Configuration for Label Actions - https://github.com/dessant/label-actions
# Specify actions for issues and pull requests
actions:
# Actions taken when the `heated` label is added
heated:
# Post a comment
comment: >
The thread has been temporarily locked.
Please follow our community guidelines.
# Lock the thread
lock: true
# Set a lock reason, such as `off-topic`, `too heated`, `resolved` or `spam`
lockReason: "too heated"
# Actions taken when the `heated` label is removed
-heated:
# Unlock the thread
unlock: true
# Optionally, specify configuration settings just for issues
issues:
actions:
feature:
# Post a comment, `{issue-author}` is an optional placeholder
comment: >
:wave: @{issue-author}, please use our idea board to request new features.
# Close the issue
close: true
-wontfix:
# Reopen the issue
open: true
# Optionally, specify configuration settings just for pull requests
pulls:
actions:
pizzazz:
comment: >
![](https://i.imgur.com/WuduJNk.jpg)
# Limit to only `issues` or `pulls`
# only: issues
# Repository to extend settings from
# _extends: repo
```
## Supporting the Project
The continued development of Label Actions is made possible thanks to the support of awesome backers. If you'd like to join them, please consider contributing with [Patreon](https://www.patreon.com/dessant), [PayPal](https://www.paypal.me/ArminSebastian) or [Bitcoin](https://goo.gl/uJUAaU).

14
docs/deploy.md Normal file
View File

@@ -0,0 +1,14 @@
# Deploying
If you would like to run your own instance of this app, see the
[docs for deployment](https://probot.github.io/docs/deployment/).
This app requires these **Permissions & events** for the GitHub App:
- Issues - **Read & Write**
- [x] Check the box for **Issues** events
- Pull requests - **Read & Write**
- [x] Check the box for **Pull request** events
- Repository metadata - **Read-only**
- Single File - **Read-only**
- Path: `.github/label-actions.yml`

58
package.json Normal file
View File

@@ -0,0 +1,58 @@
{
"name": "label-actions",
"version": "0.1.0",
"description": "A GitHub App built with Probot that performs actions when issues or pull requests are labeled or unlabeled.",
"author": "Armin Sebastian",
"license": "MIT",
"repository": {
"url": "https://github.com/dessant/label-actions.git",
"type": "git"
},
"bugs": "https://github.com/dessant/label-actions/issues",
"main": "src/index.js",
"scripts": {
"start": "probot run ./src/index.js",
"watch": "nodemon --exec 'yarn run start'",
"update": "ncu --upgrade --upgradeAll && yarn",
"push": "git push --follow-tags origin master",
"release": "standard-version && yarn run push"
},
"files": [
"docs",
"src"
],
"dependencies": {
"joi": "^14.3.1",
"probot": "^7.5.0",
"probot-config": "^1.0.1",
"probot-messages": "^0.1.2",
"uuid": "^3.3.2"
},
"devDependencies": {
"nodemon": "^1.18.9",
"npm-check-updates": "^2.15.0",
"prettier": "^1.16.0",
"smee-client": "^1.0.2",
"standard-version": "^4.4.0"
},
"engines": {
"node": ">=8.12.0"
},
"keywords": [
"github",
"issues",
"pull requests",
"label",
"comment",
"close",
"lock",
"label issues",
"close issues",
"lock issues",
"automation",
"github app",
"probot",
"probot app",
"bot"
]
}

113
src/action.js Normal file
View File

@@ -0,0 +1,113 @@
module.exports = class Action {
constructor(context, config, logger) {
this.context = context;
this.config = config;
this.log = logger;
}
async action() {
const {payload, github} = this.context;
const issue = this.context.issue();
const {only} = this.config;
const type = payload.issue ? 'issues' : 'pulls';
if (only && only !== type) {
return;
}
let label = payload.label.name;
if (payload.action === 'unlabeled') {
label = `-${label}`;
}
const actions = this.getLabelActions(type, label);
if (!actions) {
return;
}
const {comment, open, close, lock, unlock, lockReason} = actions;
const targetPayload = payload.issue || payload.pull_request;
if (comment) {
this.log.info(issue, 'Commenting');
const commentBody = comment.replace(
/{issue-author}/,
targetPayload.user.login
);
await this.ensureUnlock(issue, {active: targetPayload.locked}, () =>
github.issues.createComment({...issue, body: commentBody})
);
}
if (open && targetPayload.state === 'closed') {
this.log.info(issue, 'Reopening');
await github.issues.edit({...issue, state: 'open'});
}
if (close && targetPayload.state === 'open') {
this.log.info(issue, 'Closing');
await github.issues.edit({...issue, state: 'closed'});
}
if (lock && !targetPayload.locked) {
this.log.info(issue, 'Locking');
let params;
if (lockReason) {
params = {
...issue,
lock_reason: lockReason,
headers: {
Accept: 'application/vnd.github.sailor-v-preview+json'
}
};
} else {
params = issue;
}
await github.issues.lock(params);
}
if (unlock && targetPayload.locked) {
this.log.info(issue, 'Unlocking');
github.issues.unlock(issue);
}
}
async ensureUnlock(issue, lock, action) {
const github = this.context.github;
if (lock.active) {
if (!lock.hasOwnProperty('reason')) {
const {data: issueData} = await github.issues.get({
...issue,
headers: {
Accept: 'application/vnd.github.sailor-v-preview+json'
}
});
lock.reason = issueData.active_lock_reason;
}
await github.issues.unlock(issue);
await action();
if (lock.reason) {
issue = {
...issue,
lock_reason: lock.reason,
headers: {
Accept: 'application/vnd.github.sailor-v-preview+json'
}
};
}
await github.issues.lock(issue);
} else {
await action();
}
}
getLabelActions(type, label) {
if (
this.config[type] &&
this.config[type].actions &&
this.config[type].actions[label]
) {
return this.config[type].actions[label];
}
return this.config.actions[label];
}
};

64
src/index.js Normal file
View File

@@ -0,0 +1,64 @@
const uuidV4 = require('uuid/v4');
const getMergedConfig = require('probot-config');
const sendMessage = require('probot-messages');
const App = require('./action');
const schema = require('./schema');
module.exports = async robot => {
const github = await robot.auth();
const appName = (await github.apps.get({})).data.name;
robot.on(
[
'issues.labeled',
'issues.unlabeled',
'pull_request.labeled',
'pull_request.unlabeled'
],
async context => {
const app = await getApp(context);
if (app) {
await app.action();
}
}
);
async function getApp(context) {
const logger = context.log.child({appName, session: uuidV4()});
const config = await getConfig(context, logger);
if (config && config.perform) {
return new App(context, config, logger);
}
}
async function getConfig(context, log, file = 'label-actions.yml') {
let config;
const repo = context.repo();
try {
let repoConfig = await getMergedConfig(context, file);
if (!repoConfig) {
repoConfig = {perform: false};
}
const {error, value} = schema.validate(repoConfig);
if (error) {
throw error;
}
config = value;
} catch (err) {
log.warn({err: new Error(err), repo, file}, 'Invalid config');
if (['YAMLException', 'ValidationError'].includes(err.name)) {
await sendMessage(
robot,
context,
'[{appName}] Configuration error',
'[{appName}]({appUrl}) has encountered a configuration error in ' +
`\`${file}\`.\n\`\`\`\n${err.toString()}\n\`\`\``,
{update: 'The configuration error is still occurring.'}
);
}
}
return config;
}
};

65
src/schema.js Normal file
View File

@@ -0,0 +1,65 @@
const Joi = require('joi');
const fields = {
actions: Joi.object()
.pattern(
Joi.string()
.trim()
.max(50),
Joi.object().keys({
comment: Joi.alternatives()
.try(
Joi.string()
.trim()
.max(10000),
Joi.boolean().only(false)
)
.default(false)
.description(
'Post a comment, `{issue-author}` is an optional placeholder'
),
close: Joi.boolean()
.default(false)
.description('Close the thread'),
open: Joi.boolean()
.default(false)
.description('Reopen the thread'),
lock: Joi.boolean()
.default(false)
.description('Lock the thread'),
unlock: Joi.boolean()
.default(false)
.description('Unlock the thread'),
lockReason: Joi.alternatives()
.try(
Joi.string()
.trim()
.valid('off-topic', 'too heated', 'resolved', 'spam'),
Joi.boolean().only(false)
)
.default(false)
.description(
'Set a lock reason, such as `off-topic`, `too heated`, `resolved` or `spam`'
)
})
)
.max(200)
.description('Specify actions for issues and pull requests')
};
const schema = Joi.object().keys({
actions: fields.actions.default({}),
only: Joi.string()
.trim()
.valid('issues', 'pulls')
.description('Limit to only `issues` or `pulls`'),
pulls: Joi.object().keys(fields),
issues: Joi.object().keys(fields),
_extends: Joi.string()
.trim()
.max(260)
.description('Repository to extend settings from'),
perform: Joi.boolean().default(!process.env.DRY_RUN)
});
module.exports = schema;

5189
yarn.lock Normal file

File diff suppressed because it is too large Load Diff