From 9a19673c4f5a6899418c91b71b65d42033409b6b Mon Sep 17 00:00:00 2001 From: dessant Date: Tue, 12 Dec 2017 01:13:50 +0200 Subject: [PATCH] chore: populate repository --- .env.example | 9 +++++ .gitignore | 11 ++++++ .nvmrc | 1 + LICENSE | 21 ++++++++++++ README.md | 69 ++++++++++++++++++++++++++++++++++++++ assets/app-description.md | 28 ++++++++++++++++ assets/screenshot.png | Bin 0 -> 19397 bytes docs/deploy.md | 11 ++++++ package.json | 30 +++++++++++++++++ src/defaults.js | 7 ++++ src/index.js | 21 ++++++++++++ src/lock.js | 68 +++++++++++++++++++++++++++++++++++++ 12 files changed, 276 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 .nvmrc create mode 100644 LICENSE create mode 100644 README.md create mode 100644 assets/app-description.md create mode 100755 assets/screenshot.png create mode 100644 docs/deploy.md create mode 100644 package.json create mode 100644 src/defaults.js create mode 100644 src/index.js create mode 100644 src/lock.js diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..5d457e9 --- /dev/null +++ b/.env.example @@ -0,0 +1,9 @@ +# The ID of your GitHub App +APP_ID= +WEBHOOK_SECRET=development + +# Uncomment this to get verbose logging +# LOG_LEVEL=trace # or `info` to show less + +# Subdomain to use for localtunnel server. Defaults to your local username. +# SUBDOMAIN= diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..97cdd9c --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +.assets/ +.data/ +.env +*.pem + +node_modules/ + +npm-debug.log +yarn-debug.log +yarn-error.log +.yarn-integrity diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..e5c1510 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +8.9.0 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..78d58fc --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..0c6dee5 --- /dev/null +++ b/README.md @@ -0,0 +1,69 @@ +# Lock Threads + +Lock Threads is a GitHub App inspired by [Stale](https://github.com/probot/stale) +and built with [Probot](https://github.com/probot/probot) +that locks closed issues and pull requests after a period of inactivity. + +![](assets/screenshot.png) + +## Supporting the Project + +Lock Threads is an MIT-licensed open source project. Its ongoing +development is made possible thanks to the support of awesome backers. +If you'd like to join them, please consider contributing with +[Patreon](https://goo.gl/UtCEg4), [PayPal](https://goo.gl/BfygYx) +or [Bitcoin](https://goo.gl/uJUAaU). + +## Usage + +1. **[Install the GitHub App](https://github.com/apps/lock)** +2. Create `.github/lock.yml` based on the template below +3. It will start scanning for closed issues and/or pull requests within an hour + +#### Configuration + +Create `.github/lock.yml` in the default branch to enable the app. +The file can be empty, or it can override any of these default settings: + +```yml +# Configuration for lock-threads - https://github.com/dessant/lock-threads + +# Number of days of inactivity before a closed issue or pull request is locked +daysUntilLock: 365 +# Comment to post before locking. Set to `false` to disable +lockComment: > + This thread has been automatically locked because it has not had recent + activity. Please open a new issue for related bugs and link to relevant + comments in this thread. +# Limit to only `issues` or `pulls` +# only: issues +``` + +## How are issues and pull requests determined to be inactive? + +The app uses GitHub's [updated](https://git.io/vbR9z) search qualifier +to determine inactivity. Any change to an issue or pull request +is considered an update, including comments, changing labels, +applying or removing milestones, or pushing commits. + +An easy way to check and see which issues or pull requests will initially +be locked is to add the `updated` search qualifier to either the issue +or pull request page filter for your repository: `is:closed updated:<2016-12-20`. +Adjust the date to be 365 days ago (or whatever you set for `daysUntilLock`) +to see which issues or pull requests will be locked. + +## Why are only some issues and pull requests locked? + +To avoid triggering abuse prevention mechanisms on GitHub, only 30 issues +and pull requests will be locked per hour. If your repository has more +than that, it will just take a few hours or days to lock them all. + +## Deployment + +See [docs/deploy.md](docs/deploy.md) if you would like to run your own +instance of this app. + +## License + +Lock Threads is released under the terms of the MIT License. +Please refer to the [LICENSE](LICENSE) file. diff --git a/assets/app-description.md b/assets/app-description.md new file mode 100644 index 0000000..e12c868 --- /dev/null +++ b/assets/app-description.md @@ -0,0 +1,28 @@ +A GitHub App that locks closed issues and pull requests. + +![](https://raw.githubusercontent.com/dessant/lock-threads/master/assets/screenshot.png) + +## Usage + +1. **[Install the GitHub App](https://github.com/apps/lock)** +2. Create `.github/lock.yml` based on the template below +3. It will start scanning for closed issues and/or pull requests within an hour + +#### Configuration + +Create `.github/lock.yml` in the default branch to enable the app. +The file can be empty, or it can override any of these default settings: + +```yml +# Configuration for lock-threads - https://github.com/dessant/lock-threads + +# Number of days of inactivity before a closed issue or pull request is locked +daysUntilLock: 365 +# Comment to post before locking. Set to `false` to disable +lockComment: > + This thread has been automatically locked because it has not had recent + activity. Please open a new issue for related bugs and link to relevant + comments in this thread. +# Limit to only `issues` or `pulls` +# only: issues +``` diff --git a/assets/screenshot.png b/assets/screenshot.png new file mode 100755 index 0000000000000000000000000000000000000000..0654c1df561c5c63b25be84ef8ff5270eb28c4aa GIT binary patch literal 19397 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 literal 0 HcmV?d00001 diff --git a/docs/deploy.md b/docs/deploy.md new file mode 100644 index 0000000..d6b48ef --- /dev/null +++ b/docs/deploy.md @@ -0,0 +1,11 @@ +# 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** +- Pull requests - **Read & Write** +- Single File - **Read-only** + - Path: `.github/lock.yml` diff --git a/package.json b/package.json new file mode 100644 index 0000000..14a877d --- /dev/null +++ b/package.json @@ -0,0 +1,30 @@ +{ + "name": "lock-threads", + "version": "0.1.0", + "description": + "A GitHub App built with Probot that locks closed issues and pull requests.", + "author": "Armin Sebastian", + "license": "MIT", + "repository": "https://github.com/dessant/lock-threads", + "scripts": { + "start": "probot run ./src/index.js", + "watch": "nodemon --exec 'npm start'", + "update": "ncu --upgrade --upgradeAll && yarn", + "push": "git push --follow-tags origin master", + "release": "standard-version && yarn run push" + }, + "dependencies": { + "probot": "^3.0.3", + "probot-scheduler": "^1.0.3" + }, + "devDependencies": { + "localtunnel": "^1.8.3", + "nodemon": "1.12.5", + "npm-check-updates": "^2.13.0", + "standard-version": "^4.2.0" + }, + "engines": { + "node": "8.9.0", + "npm": "5.5.1" + } +} diff --git a/src/defaults.js b/src/defaults.js new file mode 100644 index 0000000..58a34c8 --- /dev/null +++ b/src/defaults.js @@ -0,0 +1,7 @@ +module.exports = { + daysUntilLock: 365, + lockComment: + 'This thread has been automatically locked because ' + + 'it has not had recent activity. Please open a new issue ' + + 'for related bugs and link to relevant comments in this thread.' +}; diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..2e70032 --- /dev/null +++ b/src/index.js @@ -0,0 +1,21 @@ +const createScheduler = require('probot-scheduler'); + +const App = require('./lock'); + +module.exports = robot => { + scheduler = createScheduler(robot); + + robot.on('schedule.repository', async context => { + const app = await getApp(context); + if (app) { + await app.lock(); + } + }); + + async function getApp(context) { + let config = await context.config('lock.yml'); + if (config) { + return new App(context, config, robot.log); + } + } +}; diff --git a/src/lock.js b/src/lock.js new file mode 100644 index 0000000..c0d94c6 --- /dev/null +++ b/src/lock.js @@ -0,0 +1,68 @@ +const defaults = require('./defaults'); + +module.exports = class Lock { + constructor(context, config, logger) { + this.context = context; + this.config = Object.assign({}, defaults, config); + this.logger = logger; + } + + async lock() { + const {owner, repo} = this.context.repo(); + const {lockComment} = this.config; + + const issues = await this.getLockableIssues(); + for (const issue of issues) { + const issueUrl = `${owner}/${repo}/issues/${issue.number}`; + if (lockComment) { + this.logger.info(`[${issueUrl}] Commenting`); + await this.context.github.issues.createComment({ + owner, + repo, + number: issue.number, + body: lockComment + }); + } + + this.logger.info(`[${issueUrl}] Locking`); + await this.context.github.issues.lock({ + owner, + repo, + number: issue.number + }); + } + } + + async getLockableIssues() { + const results = await this.search(); + return results.data.items.filter(issue => !issue.locked); + } + + search() { + const {owner, repo} = this.context.repo(); + const {daysUntilLock, only} = this.config; + const timestamp = this.since(daysUntilLock) + .toISOString() + .replace(/\.\d{3}\w$/, ''); + + let query = `repo:${owner}/${repo} is:closed updated:<${timestamp}`; + if (only === 'issues') { + query += ` is:issue`; + } else if (only === 'pulls') { + query += ` is:pr`; + } + + this.logger.info(`[${owner}/${repo}] Searching`); + return this.context.github.search.issues({ + q: query, + sort: 'updated', + order: 'desc', + per_page: 30 + }); + } + + since(days) { + const ttl = days * 24 * 60 * 60 * 1000; + return new Date(new Date() - ttl); + } +};