-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Create `lock` GitHub Action * Fix typo in README.md * Do not lock already locked issues * Update README.md
- Loading branch information
Showing
12 changed files
with
454 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package-lock.json | ||
node_modules | ||
__tests__/runner/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"printWidth": 80, | ||
"tabWidth": 2, | ||
"useTabs": false, | ||
"semi": true, | ||
"singleQuote": true, | ||
"trailingComma": "none", | ||
"bracketSpacing": false, | ||
"arrowParens": "avoid", | ||
"parser": "typescript" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
FROM node:slim | ||
|
||
COPY . . | ||
|
||
RUN npm install --production | ||
|
||
ENTRYPOINT ["node", "/lib/main.js"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# Lock | ||
|
||
An action for locking closed, inactive issues and pull requests. | ||
|
||
# Usage | ||
|
||
See [action.yml](action.yml) | ||
|
||
```yaml | ||
name: Lock closed, inactive issues and pull requests | ||
on: | ||
schedule: | ||
- cron: "0 0 * * *" | ||
|
||
jobs: | ||
lock: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: fastlane/github-actions/lock@latest | ||
with: | ||
repo-token: ${{ secrets.GITHUB_TOKEN }} | ||
``` | ||
# License | ||
The scripts and documentation in this project are released under the [MIT License](LICENSE) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
const path = require('path'); | ||
const nock = require('nock'); | ||
|
||
describe('action test suite', () => { | ||
it(`It locks a closed issue when the issue was not updated in a given timespan`, async () => { | ||
process.env['INPUT_REPO-TOKEN'] = 'token'; | ||
process.env['INPUT_DAYS-BEFORE-LOCK'] = '60'; | ||
process.env['INPUT_OPERATIONS-PER-RUN'] = '2'; | ||
|
||
process.env['GITHUB_REPOSITORY'] = 'foo/bar'; | ||
|
||
var date = new Date(); | ||
date.setDate(date.getDate() - 61); | ||
|
||
const api = nock('https://api.github.com') | ||
.persist() | ||
.get('/repos/foo/bar/issues?state=closed&per_page=100&page=1') | ||
.reply( | ||
200, | ||
JSON.parse( | ||
`[{"id": 1, "title": "Issue to be locked", "number": 1347, "locked": false, "updated_at": "${date.toISOString()}"}]` | ||
) | ||
) | ||
.put('/repos/foo/bar/issues/1347/lock') | ||
.reply(204); | ||
|
||
const main = require('../src/main'); | ||
await main.run(); | ||
|
||
expect(api.isDone()).toBeTruthy(); | ||
nock.cleanAll(); | ||
}); | ||
|
||
it(`It does not lock an issue, when the required time has not passed`, async () => { | ||
process.env['INPUT_REPO-TOKEN'] = 'token'; | ||
process.env['INPUT_DAYS-BEFORE-LOCK'] = '60'; | ||
process.env['INPUT_OPERATIONS-PER-RUN'] = '2'; | ||
|
||
process.env['GITHUB_REPOSITORY'] = 'foo/bar'; | ||
|
||
var date = new Date(); | ||
date.setDate(date.getDate() - 1); // yesterday | ||
|
||
const api = nock('https://api.github.com') | ||
.persist() | ||
.get('/repos/foo/bar/issues?state=closed&per_page=100&page=1') | ||
.reply( | ||
200, | ||
JSON.parse( | ||
`[{"id": 1, "title": "The issue cannot be locked yet", "number": 1347, "locked": false, "updated_at": "${date.toISOString()}"}]` | ||
) | ||
) | ||
.put('/repos/foo/bar/issues/1347/lock') | ||
.reply(204); | ||
|
||
const main = require('../src/main'); | ||
await main.run(); | ||
|
||
expect(api.isDone()).not.toBeTruthy(); | ||
nock.cleanAll(); | ||
}); | ||
|
||
it(`It does not lock an issue, when the issue is already locked`, async () => { | ||
process.env['INPUT_REPO-TOKEN'] = 'token'; | ||
process.env['INPUT_DAYS-BEFORE-LOCK'] = '60'; | ||
process.env['INPUT_OPERATIONS-PER-RUN'] = '2'; | ||
|
||
process.env['GITHUB_REPOSITORY'] = 'foo/bar'; | ||
|
||
var date = new Date(); | ||
date.setDate(date.getDate() - 61); | ||
|
||
const api = nock('https://api.github.com') | ||
.persist() | ||
.get('/repos/foo/bar/issues?state=closed&per_page=100&page=1') | ||
.reply( | ||
200, | ||
JSON.parse( | ||
`[{"id": 1, "title": "Locked issue", "number": 1347, "locked": true, "updated_at": "${date.toISOString()}"}]` | ||
) | ||
) | ||
.put('/repos/foo/bar/issues/1347/lock') | ||
.reply(204); | ||
|
||
const main = require('../src/main'); | ||
await main.run(); | ||
|
||
expect(api.isDone()).not.toBeTruthy(); | ||
nock.cleanAll(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
name: 'Lock' | ||
description: 'An action for locking closed, inactive issues and pull requests' | ||
author: 'fastlane' | ||
inputs: | ||
repo-token: | ||
description: 'Token for the repo. Can be passed in using {{ secrets.GITHUB_TOKEN }}' | ||
required: true | ||
days-before-lock: | ||
description: 'The number of days to wait to lock an issue or pull request after it being closed' | ||
default: 60 | ||
operations-per-run: | ||
description: 'The maximum number of operations per run, used to control rate limiting' | ||
default: 30 | ||
runs: | ||
using: 'docker' | ||
image: 'Dockerfile' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
module.exports = { | ||
clearMocks: true, | ||
moduleFileExtensions: ['js', 'ts'], | ||
testEnvironment: 'node', | ||
testMatch: ['**/*.test.ts'], | ||
testRunner: 'jest-circus/runner', | ||
transform: { | ||
'^.+\\.ts$': 'ts-jest' | ||
}, | ||
verbose: true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||
result["default"] = mod; | ||
return result; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const core = __importStar(require("@actions/core")); | ||
const github = __importStar(require("@actions/github")); | ||
function run() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
try { | ||
const args = getAndValidateArgs(); | ||
const client = new github.GitHub(args.repoToken); | ||
yield processIssues(client, args.daysBeforeLock, args.operationsPerRun); | ||
} | ||
catch (error) { | ||
core.error(error); | ||
core.setFailed(error.message); | ||
} | ||
}); | ||
} | ||
exports.run = run; | ||
function getAndValidateArgs() { | ||
const args = { | ||
repoToken: core.getInput('repo-token', { required: true }), | ||
daysBeforeLock: parseInt(core.getInput('days-before-lock', { required: true })), | ||
operationsPerRun: parseInt(core.getInput('operations-per-run', { required: true })) | ||
}; | ||
for (var numberInput of ['days-before-lock', 'operations-per-run']) { | ||
if (isNaN(parseInt(core.getInput(numberInput)))) { | ||
throw Error(`input ${numberInput} did not parse to a valid integer`); | ||
} | ||
} | ||
return args; | ||
} | ||
function processIssues(client, daysBeforeLock, operationsLeft, page = 1) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const issues = yield client.issues.listForRepo({ | ||
owner: github.context.repo.owner, | ||
repo: github.context.repo.repo, | ||
state: 'closed', | ||
per_page: 100, | ||
page: page | ||
}); | ||
operationsLeft -= 1; | ||
if (issues.data.length === 0 || operationsLeft === 0) { | ||
return operationsLeft; | ||
} | ||
for (var issue of issues.data.values()) { | ||
core.debug(`Found issue: "${issue.title}", last updated ${issue.updated_at}`); | ||
if (wasLastUpdatedBefore(issue, daysBeforeLock) && !issue.locked) { | ||
operationsLeft -= yield lock(client, issue); | ||
} | ||
if (operationsLeft <= 0) { | ||
return 0; | ||
} | ||
} | ||
return yield processIssues(client, daysBeforeLock, operationsLeft, page + 1); | ||
}); | ||
} | ||
function wasLastUpdatedBefore(issue, days) { | ||
const daysInMillis = 1000 * 60 * 60 * 24 * days; | ||
const millisSinceLastUpdated = new Date().getTime() - new Date(issue.updated_at).getTime(); | ||
return millisSinceLastUpdated >= daysInMillis; | ||
} | ||
function lock(client, issue) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
core.debug(`Locking issue "${issue.title}"`); | ||
yield client.issues.lock({ | ||
owner: github.context.repo.owner, | ||
repo: github.context.repo.repo, | ||
issue_number: issue.number, | ||
headers: { 'Content-Length': 0 } // if you choose not to pass any parameters, you'll need to set Content-Length to zero when calling out to this endpoint. For more info see https://developer.github.com/v3/#http-verbs | ||
}); | ||
return 1; // the number of API operations performed | ||
}); | ||
} | ||
run(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
{ | ||
"name": "lock", | ||
"version": "0.0.0", | ||
"private": true, | ||
"description": "An action for locking closed issues and pull requests", | ||
"main": "lib/main.js", | ||
"scripts": { | ||
"build": "tsc", | ||
"format": "prettier --write **/*.ts", | ||
"test": "jest" | ||
}, | ||
"keywords": [ | ||
"actions", | ||
"fastlane" | ||
], | ||
"author": "fastlane", | ||
"license": "MIT", | ||
"dependencies": { | ||
"@actions/core": "^1.0.0", | ||
"@actions/github": "^1.0.0" | ||
}, | ||
"devDependencies": { | ||
"@types/jest": "^24.0.13", | ||
"@types/node": "^12.0.4", | ||
"jest": "^24.8.0", | ||
"jest-circus": "^24.7.1", | ||
"nock": "^10.0.6", | ||
"prettier": "^1.17.1", | ||
"ts-jest": "^24.0.2", | ||
"typescript": "^3.5.1" | ||
} | ||
} |
Oops, something went wrong.