diff --git a/scripts/assign-new-pr-to-review.coffee b/scripts/assign-new-pr-to-review.coffee deleted file mode 100644 index 8548e9c..0000000 --- a/scripts/assign-new-pr-to-review.coffee +++ /dev/null @@ -1,110 +0,0 @@ -# Description: -# Script that listens to new GitHub pull requests -# and assigns them to the REVIEW column on the "Pipeline for QA" project -# -# Dependencies: -# github: "^13.1.0" -# hubot-github-webhook-listener: "^0.9.1" -# hubot-slack: "^4.4.0" -# -# Notes: -# The hard-coded names for the project board and review column are just below. -# These could be read from a config file (e.g. YAML) -# TODO: Rewrite this file with ES6 to benefit from async/await -# -# Author: -# PombeirP - -projectBoardName = "Pipeline for QA" -reviewColumnName = "REVIEW" -notifyRoomName = "core" - -module.exports = (robot) -> - - context = require('./github-context.coffee') - - robot.on "github-repo-event", (repo_event) -> - githubPayload = repo_event.payload - - switch(repo_event.eventType) - when "pull_request" - context.initialize(robot, robot.brain.get "github-app_id") - # Make sure we don't listen to our own messages - return if context.equalsRobotName(robot, githubPayload.pull_request.user.login) - - action = githubPayload.action - if action == "opened" - # A new PR was opened - assignPullRequestToReview context.github(), githubPayload, robot - -assignPullRequestToReview = (github, githubPayload, robot) -> - ownerName = githubPayload.repository.owner.login - repoName = githubPayload.repository.name - prNumber = githubPayload.pull_request.number - robot.logger.info "assignPullRequestToReview - " + - "Handling Pull Request ##{prNumber} on repo #{ownerName}/#{repoName}" - - # Fetch repo projects - # TODO: The repo project and project column info should be cached - # in order to improve performance and reduce roundtrips - github.projects.getRepoProjects { - owner: ownerName, - repo: repoName, - state: "open" - }, (err, ghprojects) -> - if err - robot.logger.error "Couldn't fetch the github projects for repo: #{err}", - ownerName, repoName - return - - # Find "Pipeline for QA" project - project = findProject ghprojects.data, projectBoardName - if !project - robot.logger.warn "Couldn't find project #{projectBoardName}" + - " in repo #{ownerName}/#{repoName}" - return - - robot.logger.debug "Fetched #{project.name} project (#{project.id})" - - # Fetch REVIEW column ID - github.projects.getProjectColumns { project_id: project.id }, (err, ghcolumns) -> - if err - robot.logger.error "Couldn't fetch the github columns for project: #{err}", - ownerName, repoName, project.id - return - - column = findColumn ghcolumns.data, reviewColumnName - if !column - robot.logger.warn "Couldn't find #{projectBoardName} column" + - " in project #{project.name}" - return - - robot.logger.debug "Fetched #{column.name} column (#{column.id})" - - # Create project card for the PR in the REVIEW column - github.projects.createProjectCard { - column_id: column.id, - content_type: 'PullRequest', - content_id: githubPayload.pull_request.id - }, (err, ghcard) -> - if err - robot.logger.error "Couldn't create project card for the PR: #{err}", - column.id, githubPayload.pull_request.id - return - - robot.logger.debug "Created card: #{ghcard.data.url}", ghcard.data.id - - # Send message to Slack - robot.messageRoom notifyRoomName, - "Moved PR #{githubPayload.pull_request.number} to " + - "#{reviewColumnName} in #{projectBoardName} project" - -findProject = (projects, name) -> - for idx, project of projects - return project if project.name == name - return null - -findColumn = (columns, name) -> - for idx, column of columns - return column if column.name == name - return null diff --git a/scripts/assign-new-pr-to-review.js b/scripts/assign-new-pr-to-review.js new file mode 100644 index 0000000..b5cee58 --- /dev/null +++ b/scripts/assign-new-pr-to-review.js @@ -0,0 +1,105 @@ +// Description: +// Script that listens to new GitHub pull requests +// and assigns them to the REVIEW column on the "Pipeline for QA" project +// +// Dependencies: +// github: "^13.1.0" +// hubot-github-webhook-listener: "^0.9.1" +// hubot-slack: "^4.4.0" +// +// Notes: +// The hard-coded names for the project board and review column are just below. +// These could be read from a config file (e.g. YAML) +// +// Author: +// PombeirP + +const projectBoardName = "Pipeline for QA"; +const reviewColumnName = "REVIEW"; +const notifyRoomName = "core"; + +module.exports = function(robot) { + + const context = require('./github-context.js'); + + return robot.on("github-repo-event", function(repo_event) { + const githubPayload = repo_event.payload; + + switch(repo_event.eventType) { + case "pull_request": + context.initialize(robot, robot.brain.get("github-app_id")); + + // Make sure we don't listen to our own messages + if (context.equalsRobotName(robot, githubPayload.pull_request.user.login)) { return; } + + var { action } = githubPayload; + if (action === "opened") { + // A new PR was opened + return assignPullRequestToReview(context.github(), githubPayload, robot); + } + break; + } + }); +}; + +async function assignPullRequestToReview(github, githubPayload, robot) { + const ownerName = githubPayload.repository.owner.login; + const repoName = githubPayload.repository.name; + const prNumber = githubPayload.pull_request.number; + + robot.logger.info(`assignPullRequestToReview - Handling Pull Request #${prNumber} on repo ${ownerName}/${repoName}`); + + // Fetch repo projects + // TODO: The repo project and project column info should be cached + // in order to improve performance and reduce roundtrips + try { + ghprojects = await github.projects.getRepoProjects({ + owner: ownerName, + repo: repoName, + state: "open" + }); + + // Find "Pipeline for QA" project + const project = ghprojects.data.find(function(p) { return p.name === projectBoardName }); + if (!project) { + robot.logger.warn(`Couldn't find project ${projectBoardName} in repo ${ownerName}/${repoName}`); + return; + } + + robot.logger.debug(`Fetched ${project.name} project (${project.id})`); + + // Fetch REVIEW column ID + try { + ghcolumns = await github.projects.getProjectColumns({ project_id: project.id }); + + const column = ghcolumns.data.find(function(c) { return c.name === reviewColumnName }); + if (!column) { + robot.logger.warn(`Couldn't find ${projectBoardName} column in project ${project.name}`); + return; + } + + robot.logger.debug(`Fetched ${column.name} column (${column.id})`); + + // Create project card for the PR in the REVIEW column + try { + ghcard = await github.projects.createProjectCard({ + column_id: column.id, + content_type: 'PullRequest', + content_id: githubPayload.pull_request.id + }); + + robot.logger.debug(`Created card: ${ghcard.data.url}`, ghcard.data.id); + + // Send message to Slack + robot.messageRoom(notifyRoomName, `Moved PR ${githubPayload.pull_request.number} to ${reviewColumnName} in ${projectBoardName} project` + ); + } catch (err) { + robot.logger.error(`Couldn't create project card for the PR: ${err}`, column.id, githubPayload.pull_request.id); + } + } catch (err) { + robot.logger.error(`Couldn't fetch the github columns for project: ${err}`, ownerName, repoName, project.id); + } + } catch (err) { + robot.logger.error(`Couldn't fetch the github projects for repo: ${err}`, ownerName, repoName); + } +}; diff --git a/scripts/example.coffee b/scripts/example.coffee deleted file mode 100644 index 7c9839c..0000000 --- a/scripts/example.coffee +++ /dev/null @@ -1,106 +0,0 @@ -# Description: -# Example scripts for you to examine and try out. -# -# Notes: -# They are commented out by default, because most of them are pretty silly and -# wouldn't be useful and amusing enough for day to day huboting. -# Uncomment the ones you want to try and experiment with. -# -# These are from the scripting documentation: https://github.com/github/hubot/blob/master/docs/scripting.md - -module.exports = (robot) -> - - # robot.hear /badger/i, (res) -> - # res.send "Badgers? BADGERS? WE DON'T NEED NO STINKIN BADGERS" - # - # robot.respond /open the (.*) doors/i, (res) -> - # doorType = res.match[1] - # if doorType is "pod bay" - # res.reply "I'm afraid I can't let you do that." - # else - # res.reply "Opening #{doorType} doors" - # - # robot.hear /I like pie/i, (res) -> - # res.emote "makes a freshly baked pie" - # - # lulz = ['lol', 'rofl', 'lmao'] - # - # robot.respond /lulz/i, (res) -> - # res.send res.random lulz - # - # robot.topic (res) -> - # res.send "#{res.message.text}? That's a Paddlin'" - # - # - # enterReplies = ['Hi', 'Target Acquired', 'Firing', 'Hello friend.', 'Gotcha', 'I see you'] - # leaveReplies = ['Are you still there?', 'Target lost', 'Searching'] - # - # robot.enter (res) -> - # res.send res.random enterReplies - # robot.leave (res) -> - # res.send res.random leaveReplies - # - # answer = process.env.HUBOT_ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING - # - # robot.respond /what is the answer to the ultimate question of life/, (res) -> - # unless answer? - # res.send "Missing HUBOT_ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING in environment: please set and try again" - # return - # res.send "#{answer}, but what is the question?" - # - # robot.respond /you are a little slow/, (res) -> - # setTimeout () -> - # res.send "Who you calling 'slow'?" - # , 60 * 1000 - # - # annoyIntervalId = null - # - # robot.respond /annoy me/, (res) -> - # if annoyIntervalId - # res.send "AAAAAAAAAAAEEEEEEEEEEEEEEEEEEEEEEEEIIIIIIIIHHHHHHHHHH" - # return - # - # res.send "Hey, want to hear the most annoying sound in the world?" - # annoyIntervalId = setInterval () -> - # res.send "AAAAAAAAAAAEEEEEEEEEEEEEEEEEEEEEEEEIIIIIIIIHHHHHHHHHH" - # , 1000 - # - # robot.respond /unannoy me/, (res) -> - # if annoyIntervalId - # res.send "GUYS, GUYS, GUYS!" - # clearInterval(annoyIntervalId) - # annoyIntervalId = null - # else - # res.send "Not annoying you right now, am I?" - # - # - # robot.router.post '/hubot/chatsecrets/:room', (req, res) -> - # room = req.params.room - # data = JSON.parse req.body.payload - # secret = data.secret - # - # robot.messageRoom room, "I have a secret: #{secret}" - # - # res.send 'OK' - # - # robot.error (err, res) -> - # robot.logger.error "DOES NOT COMPUTE" - # - # if res? - # res.reply "DOES NOT COMPUTE" - # - # robot.respond /have a soda/i, (res) -> - # # Get number of sodas had (coerced to a number). - # sodasHad = robot.brain.get('totalSodas') * 1 or 0 - # - # if sodasHad > 4 - # res.reply "I'm too fizzy.." - # - # else - # res.reply 'Sure!' - # - # robot.brain.set 'totalSodas', sodasHad+1 - # - # robot.respond /sleep it off/i, (res) -> - # robot.brain.set 'totalSodas', 0 - # res.reply 'zzzzz' diff --git a/scripts/github-context.coffee b/scripts/github-context.coffee deleted file mode 100644 index bdbff63..0000000 --- a/scripts/github-context.coffee +++ /dev/null @@ -1,81 +0,0 @@ -# Description: -# Script that keeps GitHub-related context to be shared among scripts -# -# Dependencies: -# github: "^13.1.0" -# jwt-simple: "^0.5.1" -# -# Author: -# PombeirP - -GitHubApi = require('github') - -RegExp cachedRobotNameRegex = null -initialized = false -githubAPI = new GitHubApi { version: "3.0.0" } - -module.exports = { - - github: -> githubAPI - - initialize: (robot, integrationID) -> - return if initialized - - token = robot.brain.get('github-token') - if token - initialized = true - process.env.HUBOT_GITHUB_TOKEN = token - robot.logger.debug "Reused cached GitHub token" - githubAPI.authenticate({ type: 'token', token: token }) - return - - pemFilePath = './status-github-bot.pem' - - jwt = require('jwt-simple') - - # Private key contents - privateKey = '' - try - fs = require('fs') - privateKey = fs.readFileSync pemFilePath - catch err - robot.logger.error "Couldn't read #{pemFilePath} file contents: #{err}" - return - - now = Math.round(Date.now() / 1000) - # Generate the JWT - payload = { - # issued at time - iat: now, - # JWT expiration time (10 minute maximum) - exp: now + (1 * 60), - # GitHub App's identifier - iss: integrationID - } - - jwt = jwt.encode(payload, privateKey, 'RS256') - githubAPI.authenticate({ - type: 'integration', - token: jwt - }) - robot.logger.debug "Configured integration authentication with JWT", jwt - - initialized = true - - equalsRobotName: (robot, str) -> - return getRegexForRobotName(robot).test(str) -} - -getRegexForRobotName = (robot) -> - # This comes straight out of Hubot's Robot.coffee - # - they didn't get a nice way of extracting that method though - if !cachedRobotNameRegex - name = robot.name.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') - - if robot.alias - alias = robot.alias.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') - namePattern = "^\\s*[@]?(?:#{alias}|#{name})" - else - namePattern = "^\\s*[@]?#{name}" - cachedRobotNameRegex = new RegExp(namePattern, 'i') - return cachedRobotNameRegex diff --git a/scripts/github-context.js b/scripts/github-context.js new file mode 100644 index 0000000..c6df174 --- /dev/null +++ b/scripts/github-context.js @@ -0,0 +1,91 @@ +// Description: +// Script that keeps GitHub-related context to be shared among scripts +// +// Dependencies: +// github: "^13.1.0" +// +// Author: +// PombeirP + +const GitHubApi = require('github'); + +let cachedRobotNameRegex; +let initialized = false; +const githubAPI = new GitHubApi({ + timeout: 15000, + requestMedia: 'application/vnd.github.v3+json' +}); + +module.exports = { + + github() { return githubAPI; }, + + initialize(robot, integrationID) { + if (initialized) { return; } + + const token = robot.brain.get('github-token'); + if (token) { + initialized = true; + process.env.HUBOT_GITHUB_TOKEN = token; + robot.logger.debug("Reused cached GitHub token"); + githubAPI.authenticate({ type: 'token', token }); + return; + } + + const pemFilePath = './status-github-bot.pem'; + + const jwtLib = require('jwt-simple'); + + // Private key contents + let privateKey = ''; + try { + const fs = require('fs'); + privateKey = fs.readFileSync(pemFilePath); + } catch (err) { + robot.logger.error(`Couldn't read ${pemFilePath} file contents: ${err}`); + return; + } + + const now = Math.round(Date.now() / 1000); + // Generate the JWT + const payload = { + // issued at time + iat: now, + // JWT expiration time (10 minute maximum) + exp: now + (1 * 60), + // GitHub App's identifier + iss: integrationID + }; + + jwt = jwtLib.encode(payload, privateKey, 'RS256'); + githubAPI.authenticate({ + type: 'integration', + token: jwt + }); + robot.logger.debug("Configured integration authentication with JWT", jwt); + + initialized = true; + }, + + equalsRobotName(robot, str) { + return getRegexForRobotName(robot).test(str); + } +}; + +function getRegexForRobotName(robot) { + // This comes straight out of Hubot's Robot.coffee + // - they didn't get a nice way of extracting that method though + if (!cachedRobotNameRegex) { + let namePattern; + const name = robot.name.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); + + if (robot.alias) { + const alias = robot.alias.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); + namePattern = `^\\s*[@]?(?:${alias}|${name})`; + } else { + namePattern = `^\\s*[@]?${name}`; + } + cachedRobotNameRegex = new RegExp(namePattern, 'i'); + } + return cachedRobotNameRegex; +}; diff --git a/scripts/github-installation.coffee b/scripts/github-installation.coffee deleted file mode 100644 index a36155a..0000000 --- a/scripts/github-installation.coffee +++ /dev/null @@ -1,76 +0,0 @@ -# Description: -# Script that handles the installation of the GitHub app -# -# Dependencies: -# github: "^13.1.0" -# hubot-github-webhook-listener: "^0.9.1" -# -# Author: -# PombeirP - -module.exports = (robot) -> - - context = require('./github-context.coffee') - - robot.on "github-repo-event", (repo_event) -> - githubPayload = repo_event.payload - - switch(repo_event.eventType) - when "integration_installation" - # Make sure we don't listen to our own messages - return if context.equalsRobotName(robot, githubPayload.sender.login) - - action = githubPayload.action - switch action - when "created" - # App was installed on an organization - robot.logger.info "Initializing installation for app with ID " + - "#{githubPayload.installation.app_id} and " + - "installation ID #{githubPayload.installation.id}" - - robot.brain.set 'github-app_install-payload', JSON.stringify(githubPayload) - robot.brain.set 'github-app_id', githubPayload.installation.app_id - robot.brain.set 'github-app_repositories', - (x.full_name for x in githubPayload.repositories).join() - - context.initialize(robot, githubPayload.installation.app_id) - - perms = githubPayload.installation.permissions - robot.logger.error formatPermMessage('repository_projects', 'write') unless perms.repository_projects == 'write' - robot.logger.error formatPermMessage('metadata', 'read') unless perms.metadata == 'read' - robot.logger.error formatPermMessage('issues', 'read') unless perms.issues == 'read' - robot.logger.error formatPermMessage('pull_requests', 'write') unless perms.pull_requests == 'write' - - robot.logger.error "Please enable 'pull_request' events " + - "in the app configuration on github.com" unless 'pull_request' in githubPayload.installation.events - - createAccessToken robot, context.github(), githubPayload.installation.id - when "deleted" - # App was uninstalled from an organization - robot.logger.info "Removing installation for app " + - "with ID #{githubPayload.installation.app_id} and " + - "installation ID #{githubPayload.installation.id}" - - robot.brain.set 'github-app_id', null - robot.brain.set 'github-app_install-payload', null - robot.brain.set 'github-app_repositories', null - robot.brain.set 'github-token', null - process.env.HUBOT_GITHUB_TOKEN = null - -createAccessToken = (robot, github, id) -> - github.apps.createInstallationToken { installation_id: id }, (err, response) -> - if err - robot.logger.error "Couldn't create installation token: #{err}", id - return - - console.error response.data.token - robot.brain.set 'github-token', response.data.token - # TODO: Set Redis expiration date to value from response.data.expires_at - process.env.HUBOT_GITHUB_TOKEN = response.data.token - github.authenticate({ - type: 'token', - token: response.data.token - }) - -formatPermMessage = (permName, perm) -> - "Please enable '#{permName}' #{perm} permission in the app configuration on github.com" \ No newline at end of file diff --git a/scripts/github-installation.js b/scripts/github-installation.js new file mode 100644 index 0000000..cad2e7b --- /dev/null +++ b/scripts/github-installation.js @@ -0,0 +1,81 @@ +// Description: +// Script that handles the installation of the GitHub app +// +// Dependencies: +// github: "^13.1.0" +// hubot-github-webhook-listener: "^0.9.1" +// +// Author: +// PombeirP + +module.exports = function(robot) { + + const context = require('./github-context.js'); + + return robot.on("github-repo-event", async function(repo_event) { + const githubPayload = repo_event.payload; + + robot.logger.debug(`Received ${repo_event.eventType}/${githubPayload.action} event from GitHub`); + + switch(repo_event.eventType) { + case "integration_installation": + // Make sure we don't listen to our own messages + if (context.equalsRobotName(robot, githubPayload.sender.login)) { return; } + + var { action } = githubPayload; + switch (action) { + case "created": + // App was installed on an organization + robot.logger.info(`Initializing installation for app with ID ${githubPayload.installation.app_id} and installation ID ${githubPayload.installation.id}`); + + robot.brain.set('github-app_install-payload', JSON.stringify(githubPayload)); + robot.brain.set('github-app_id', githubPayload.installation.app_id); + robot.brain.set('github-app_repositories', githubPayload.repositories.map((x) => x.full_name).join()); + + context.initialize(robot, githubPayload.installation.app_id); + + var perms = githubPayload.installation.permissions; + if (perms.repository_projects !== 'write') { robot.logger.error(formatPermMessage('repository_projects', 'write')); } + if (perms.metadata !== 'read') { robot.logger.error(formatPermMessage('metadata', 'read')); } + if (perms.issues !== 'read') { robot.logger.error(formatPermMessage('issues', 'read')); } + if (perms.pull_requests !== 'write') { robot.logger.error(formatPermMessage('pull_requests', 'write')); } + + if (!githubPayload.installation.events.includes('pull_request')) { + robot.logger.error("Please enable 'pull_request' events in the app configuration on github.com"); + } + + await createAccessToken(robot, context.github(), githubPayload.installation.id); + break; + case "deleted": + // App was uninstalled from an organization + robot.logger.info(`Removing installation for app with ID ${githubPayload.installation.app_id} and installation ID ${githubPayload.installation.id}`); + + robot.brain.set('github-app_id', null); + robot.brain.set('github-app_install-payload', null); + robot.brain.set('github-app_repositories', null); + robot.brain.set('github-token', null); + process.env.HUBOT_GITHUB_TOKEN = null; + break; + } + break; + } + }); +}; + +async function createAccessToken(robot, github, id) { + try { + response = await github.apps.createInstallationToken({ installation_id: id }); + + robot.brain.set('github-token', response.data.token); + // TODO: Set Redis expiration date to value from response.data.expires_at + process.env.HUBOT_GITHUB_TOKEN = response.data.token; + github.authenticate({ + type: 'token', + token: response.data.token + }); +} catch (err) { + robot.logger.error(`Couldn't create installation token: ${err}`, id); + } +} + +var formatPermMessage = (permName, perm) => `Please enable '${permName}' ${perm} permission in the app configuration on github.com`; \ No newline at end of file diff --git a/scripts/greet-new-contributor.coffee b/scripts/greet-new-contributor.coffee deleted file mode 100644 index bf21c4e..0000000 --- a/scripts/greet-new-contributor.coffee +++ /dev/null @@ -1,69 +0,0 @@ -# Description: -# Script that listens to new GitHub pull requests -# and greets the user if it is their first PR on the repo -# -# Dependencies: -# github: "^13.1.0" -# hubot-github-webhook-listener: "^0.9.1" -# -# Notes: -# TODO: Rewrite this file with ES6 to benefit from async/await -# -# Author: -# PombeirP - -module.exports = (robot) -> - - context = require('./github-context.coffee') - - robot.on "github-repo-event", (repo_event) -> - githubPayload = repo_event.payload - - switch(repo_event.eventType) - when "pull_request" - context.initialize(robot, robot.brain.get "github-app_id") - # Make sure we don't listen to our own messages - return if context.equalsRobotName(robot, githubPayload.pull_request.user.login) - - action = githubPayload.action - if action == "opened" - # A new PR was opened - greetNewContributor context.github(), githubPayload, robot - -greetNewContributor = (github, githubPayload, robot) -> - # TODO: Read the welcome message from a (per-repo?) file (e.g. status-react.welcome-msg.md) - welcomeMessage = "Thanks for making your first PR here!" - ownerName = githubPayload.repository.owner.login - repoName = githubPayload.repository.name - prNumber = githubPayload.pull_request.number - robot.logger.info "greetNewContributor - " + - "Handling Pull Request ##{prNumber} on repo #{ownerName}/#{repoName}" - - github.issues.getForRepo { - owner: ownerName, - repo: repoName - state: 'all', - creator: githubPayload.pull_request.user.login - }, (err, ghissues) -> - if err - robot.logger.error "Couldn't fetch the user's github issues for repo: #{err}", - ownerName, repoName - return - - userPullRequests = ghissues.data.filter (issue) -> issue.pull_request - if userPullRequests.length == 1 - github.issues.createComment { - owner: ownerName, - repo: repoName, - number: prNumber, - body: welcomeMessage - }, (err, result) -> - if err - robot.logger.error("Couldn't fetch the github projects for repo: #{err}", - ownerName, repoName) unless err.code == 404 - return - robot.logger.info "Commented on PR with welcome message", ownerName, repoName - else - robot.logger.debug( - "This is not the user's first PR on the repo, ignoring", - ownerName, repoName, githubPayload.pull_request.user.login) \ No newline at end of file diff --git a/scripts/greet-new-contributor.js b/scripts/greet-new-contributor.js new file mode 100644 index 0000000..cc06110 --- /dev/null +++ b/scripts/greet-new-contributor.js @@ -0,0 +1,72 @@ +// Description: +// Script that listens to new GitHub pull requests +// and greets the user if it is their first PR on the repo +// +// Dependencies: +// github: "^13.1.0" +// hubot-github-webhook-listener: "^0.9.1" +// +// Author: +// PombeirP + +module.exports = function(robot) { + + const context = require('./github-context.js'); + + return robot.on("github-repo-event", function(repo_event) { + const githubPayload = repo_event.payload; + + switch(repo_event.eventType) { + case "pull_request": + context.initialize(robot, robot.brain.get("github-app_id")); + // Make sure we don't listen to our own messages + if (context.equalsRobotName(robot, githubPayload.pull_request.user.login)) { return; } + + var { action } = githubPayload; + if (action === "opened") { + // A new PR was opened + return greetNewContributor(context.github(), githubPayload, robot); + } + break; + } + }); +}; + +async function greetNewContributor(github, githubPayload, robot) { + // TODO: Read the welcome message from a (per-repo?) file (e.g. status-react.welcome-msg.md) + const welcomeMessage = "Thanks for making your first PR here!"; + const ownerName = githubPayload.repository.owner.login; + const repoName = githubPayload.repository.name; + const prNumber = githubPayload.pull_request.number; + + robot.logger.info(`greetNewContributor - Handling Pull Request #${prNumber} on repo ${ownerName}/${repoName}`); + + try { + ghissues = await github.issues.getForRepo({ + owner: ownerName, + repo: repoName, + state: 'all', + creator: githubPayload.pull_request.user.login + }) + + const userPullRequests = ghissues.data.filter(issue => issue.pull_request); + if (userPullRequests.length === 1) { + try { + await github.issues.createComment({ + owner: ownerName, + repo: repoName, + number: prNumber, + body: welcomeMessage + }) + } catch (err) { + if (err.code !== 404) { + robot.logger.error(`Couldn't create comment on PR: ${err}`, ownerName, repoName); + } + } + } else { + robot.logger.debug("This is not the user's first PR on the repo, ignoring", ownerName, repoName, githubPayload.pull_request.user.login); + } + } catch (err) { + robot.logger.error(`Couldn't fetch the user's github issues for repo: ${err}`, ownerName, repoName); + } +}; \ No newline at end of file