From 5f06a655bc1abaa84baa8a087fe76d9dc9d3a20b Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Fri, 2 Aug 2019 17:14:26 -0400 Subject: [PATCH] Get it working (#1) * Get it working * Required token * Logging * Debug * Debug * Correct logging * No setNeutral * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * working * logging * logging * logging * logging * logging * logging * logging * logging * logging * logging * debug * debug --- README.md | 4 +- __tests__/main.test.ts | 4 -- action.yml | 14 +++-- jest.config.js | 11 ---- lib/main.js | 82 ++++++++++++++++++++++++-- src/main.ts | 96 +++++++++++++++++++++++++++++-- toolkit/actions-github-0.0.0.tgz | Bin 3106 -> 3118 bytes 7 files changed, 180 insertions(+), 31 deletions(-) delete mode 100644 __tests__/main.test.ts delete mode 100644 jest.config.js diff --git a/README.md b/README.md index cdd991c..e519271 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# Container Action Template +# First Interaction -To get started, click the `Use this template` button on this repository [which will create a new repository based on this template](https://github.blog/2019-06-06-generate-new-repositories-with-repository-templates/). \ No newline at end of file +An action for filtering pull requests and issues from first-time contributors \ No newline at end of file diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts deleted file mode 100644 index 7df9bad..0000000 --- a/__tests__/main.test.ts +++ /dev/null @@ -1,4 +0,0 @@ -describe('TODO - Add a test suite', () => { - it('TODO - Add a test', async () => { - }); -}); diff --git a/action.yml b/action.yml index 84c30ab..c41d374 100644 --- a/action.yml +++ b/action.yml @@ -1,10 +1,14 @@ -name: 'Container Action Template' +name: 'First interaction' description: 'Get started with Container actions' author: 'GitHub' -inputs: - myInput: - description: 'Input to use' - default: 'world' +inputs: + repoToken: + description: 'Token for the repo. Can be passed in using {{ secrets.GITHUB_TOKEN }}' + required: true + issueMessage: + description: 'Comment to post on an individuals first issue' + prMessage: + description: 'Comment to post on an individuals first pull request' runs: using: 'docker' image: 'Dockerfile' \ No newline at end of file diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index 563d4cc..0000000 --- a/jest.config.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - clearMocks: true, - moduleFileExtensions: ['js', 'ts'], - testEnvironment: 'node', - testMatch: ['**/*.test.ts'], - testRunner: 'jest-circus/runner', - transform: { - '^.+\\.ts$': 'ts-jest' - }, - verbose: true -} \ No newline at end of file diff --git a/lib/main.js b/lib/main.js index a72e6ac..5910234 100644 --- a/lib/main.js +++ b/lib/main.js @@ -9,18 +9,92 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }; const core = require('@actions/core'); const github = require('@actions/github'); +const fs = require('fs'); function run() { return __awaiter(this, void 0, void 0, function* () { try { - const myInput = core.getInput('myInput'); - core.debug(`Hello ${myInput} from inside a container`); - // Get github context data + // Get client and context + const client = new github.GitHub(core.getInput('repoToken', { required: true })); const context = github.context; - console.log(`We can even get context data, like the repo: ${context.repo.repo}`); + if (context.payload.action !== 'opened') { + console.log('Nothing was opened'); + return; + } + // Do nothing if its not a pr or issue + const isIssue = !!context.payload.issue; + if (!isIssue && !context.payload.pull_request) { + console.log('Not a pull request or issue'); + return; + } + // Do nothing if its not their first contribution + console.log('Checking if its the users first contribution'); + const sender = context.payload.sender.login; + const issue = context.issue; + const firstContribution = isIssue ? yield isFirstIssue(client, issue.owner, issue.repo, sender, issue.number) : yield isFirstPull(client, issue.owner, issue.repo, sender, issue.number); + if (!firstContribution) { + console.log('Not the users first contribution'); + return; + } + // Do nothing if no message set for this type of contribution + const message = isIssue ? core.getInput('issueMessage') : core.getInput('prMessage'); + if (!message) { + console.log('No message provided for this type of contribution'); + return; + } + // Add a comment to the appropriate place + console.log(`Adding message: ${message}`); + if (isIssue) { + yield client.issues.createComment({ owner: issue.owner, repo: issue.repo, issue_number: issue.number, body: message }); + } + else { + yield client.pulls.createReview({ owner: issue.owner, repo: issue.repo, pull_number: issue.number, body: message, event: 'COMMENT' }); + } } catch (error) { core.setFailed(error.message); + return; } }); } +function isFirstIssue(client, owner, repo, sender, number) { + return __awaiter(this, void 0, void 0, function* () { + console.log(`owner ${owner}, repo ${repo}, creator: ${sender}`); + const { status, data: issues } = yield client.issues.listForRepo({ owner: owner, repo: repo, creator: sender, state: 'all' }); + if (status !== 200) { + throw new Error(`Received API status code ${status}`); + } + if (issues.length === 0) { + return true; + } + for (const issue of issues) { + const issueNumber = issue.number; + console.log(issueNumber, number); + if (issueNumber < number) { + return false; + } + } + return true; + }); +} +// No way to filter pulls by creator +function isFirstPull(client, owner, repo, sender, number, page = 1) { + return __awaiter(this, void 0, void 0, function* () { + console.log('Checking...'); + const { status, data: pulls } = yield client.pulls.list({ owner: owner, repo: repo, per_page: 100, page: page, state: 'all' }); + if (status !== 200) { + throw new Error(`Received API status code ${status}`); + } + if (pulls.length === 0) { + return true; + } + for (const pull of pulls) { + const login = pull.user.login; + const pullNumber = pull.number; + if (login === sender && pullNumber < number) { + return false; + } + } + return yield isFirstPull(client, owner, repo, sender, number, page + 1); + }); +} run(); diff --git a/src/main.ts b/src/main.ts index 757639d..edd230d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,17 +1,103 @@ const core = require('@actions/core'); const github = require('@actions/github'); +const fs = require('fs'); async function run() { try { - const myInput = core.getInput('myInput'); - core.debug(`Hello ${myInput} from inside a container`); - - // Get github context data + // Get client and context + const client = new github.GitHub(core.getInput('repoToken', { required: true })); const context = github.context; - console.log(`We can even get context data, like the repo: ${context.repo.repo}`) + + if (context.payload.action !== 'opened') { + console.log('Nothing was opened'); + return; + } + + // Do nothing if its not a pr or issue + const isIssue = !!context.payload.issue; + if (!isIssue && !context.payload.pull_request) { + console.log('Not a pull request or issue'); + return; + } + + // Do nothing if its not their first contribution + console.log('Checking if its the users first contribution'); + const sender = context.payload.sender.login; + const issue = context.issue; + const firstContribution = isIssue ? await isFirstIssue(client, issue.owner, issue.repo, sender, issue.number) : await isFirstPull(client, issue.owner, issue.repo, sender, issue.number); + if (!firstContribution) { + console.log('Not the users first contribution'); + return; + } + + // Do nothing if no message set for this type of contribution + const message = isIssue ? core.getInput('issueMessage') : core.getInput('prMessage'); + if (!message) { + console.log('No message provided for this type of contribution'); + return; + } + + // Add a comment to the appropriate place + console.log(`Adding message: ${message}`); + if (isIssue) { + await client.issues.createComment({ owner: issue.owner, repo: issue.repo, issue_number: issue.number, body: message }); + } + else { + await client.pulls.createReview({ owner: issue.owner, repo: issue.repo, pull_number: issue.number, body: message, event: 'COMMENT' }); + } + + } catch (error) { core.setFailed(error.message); + return; + } +} + +async function isFirstIssue(client, owner, repo, sender, number): Promise { + console.log(`owner ${owner}, repo ${repo}, creator: ${sender}`); + const {status, data: issues} = await client.issues.listForRepo({owner: owner, repo: repo, creator: sender, state: 'all'}); + + if (status !== 200) { + throw new Error(`Received API status code ${status}`); + } + + if (issues.length === 0) { + return true; } + + for (const issue of issues) { + const issueNumber = issue.number; + console.log(issueNumber, number); + if (issueNumber < number) { + return false; + } + } + + return true; +} + +// No way to filter pulls by creator +async function isFirstPull(client, owner, repo, sender, number, page = 1): Promise { + console.log('Checking...'); + const {status, data: pulls} = await client.pulls.list({owner: owner, repo: repo, per_page: 100, page: page, state: 'all'}); + + if (status !== 200) { + throw new Error(`Received API status code ${status}`); + } + + if (pulls.length === 0) { + return true; + } + + for (const pull of pulls) { + const login = pull.user.login; + const pullNumber = pull.number; + if (login === sender && pullNumber < number) { + return false; + } + } + + return await isFirstPull(client, owner, repo, sender, number, page+1); } run(); diff --git a/toolkit/actions-github-0.0.0.tgz b/toolkit/actions-github-0.0.0.tgz index 4113d11b5a0d930900e3913e58670ec8e44676c4..c05bd00575f689d7fedf2cd143487348e80e0d56 100644 GIT binary patch literal 3118 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 ICjdqO0AmLq6#xJL literal 3106 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