From 33de7cb0e068829bb7c264d4879e01e3e92982cf Mon Sep 17 00:00:00 2001 From: Inga Lovinde <52715130+inga-lovinde@users.noreply.github.com> Date: Thu, 9 Oct 2014 12:35:26 +0400 Subject: [PATCH] Implemented head and base names checking --- BuildServer/lib/commenter.js | 206 +++++++++++++++++++++++++----- BuildServer/routes/postreceive.js | 32 +++-- 2 files changed, 191 insertions(+), 47 deletions(-) diff --git a/BuildServer/lib/commenter.js b/BuildServer/lib/commenter.js index 44a93bd..32a4837 100644 --- a/BuildServer/lib/commenter.js +++ b/BuildServer/lib/commenter.js @@ -4,63 +4,199 @@ var fs = require('fs'), builder = require('../lib/builder'), settings = require('../settings'); +var featureNamePattern = /^feature-(\d+)(?:-[a-z0-9]+)+$/; +var versionNamePattern = /^v\d+(\.\d+)*$/; +var masterNamePattern = /^master$/; + +var writeComment = function (options, message, callback) { + return options.github.issues.createComment({ + user: options.baseRepoOptions.owner, + repo: options.baseRepoOptions.reponame, + number: options.number, + body: message + }, callback); +}; + +var closePullRequest = function (options, message, callback) { + return writeComment(options, message, function (err) { + if (err) { + return callback(err); + } + + return options.github.issues.edit({ + user: options.baseRepoOptions.owner, + repo: options.baseRepoOptions.reponame, + number: options.number, + state: "closed" + }, callback); + }); +}; + +var checkHasIssue = function (options, issueNumber, callback) { + return options.github.issues.getRepoIssue({ + user: options.baseRepoOptions.owner, + repo: options.baseRepoOptions.reponame, + number: issueNumber + }, function (err, result) { + if (err && err.code !== 404) { + return callback(err); + } + + if (err || result.number.toString() !== issueNumber) { + return callback(undefined, false); + } + + if (result.pull_request && result.pull_request.url) { + return callback(undefined, false); + } + + return callback(undefined, true, result.title); + }); +}; + +var checkHasReleases = function (options, callback) { + return options.github.releases.listReleases({ + owner: options.baseRepoOptions.owner, + repo: options.baseRepoOptions.reponame, + per_page: 1 + }, function (err, result) { + if (err) { + return callback(err); + } + + return callback(undefined, result && result.length); + }); +}; + +var checkPullRequest = function (options, callback) { + var head = options.headRepoOptions, + base = options.baseRepoOptions; + + if (head.reponame !== base.reponame) { + return closePullRequest(options, "Base and head repository names should match", callback); + } + + if (head.owner === base.owner) { + if (!versionNamePattern.test(head.branchname) || !masterNamePattern.test(base.branchname)) { + return closePullRequest(options, "Only merging from version to master is allowed", callback); + } + + return checkHasReleases(options, function (err, hasReleases) { + if (err) { + return writeComment(options, "Unable to check for releases", callback); + } + + if (!hasReleases) { + return closePullRequest(options, "Merging from version to master is only allowed for repositories with releases", callback); + } + + if (options.action === "opened") { + return writeComment(options, "Switching master branch to " + head.branchname + " release", callback); + } + + return process.nextTick(callback); + }); + } + + if (!featureNamePattern.test(head.branchname)) { + return closePullRequest(options, "Only merging from feature branch is allowed", callback); + } + + if (!versionNamePattern.test(base.branchname) && !masterNamePattern.test(base.branchname)) { + return closePullRequest(options, "Only merging to master or version branch is allowed; merging to '" + base.branchname + "' is not supported", callback); + } + + var issueNumber = featureNamePattern.exec(head.branchname)[1]; + return checkHasIssue(options, issueNumber, function (err, hasIssue, issueTitle) { + if (err) { + return writeComment(options, "Unable to check for issue:\r\n\r\n" + err.message, callback); + } + + if (!hasIssue) { + return closePullRequest(options, "Unable to find issue #" + issueNumber, callback); + } + + var shouldHaveReleases = versionNamePattern.test(base.branchname); + return checkHasReleases(options, function (err, hasReleases) { + if (err) { + return writeComment(options, "Unable to check for releases", callback); + } + + if (shouldHaveReleases && !hasReleases) { + return closePullRequest(options, "Merging from feature to version is only allowed for repositories with releases", callback); + } + + if (!shouldHaveReleases && hasReleases) { + return closePullRequest(options, "Merging from feature to master is only allowed for repositories without releases", callback); + } + + if (options.action === "opened") { + return writeComment(options, "Merging feature #" + issueNumber + " (" + issueTitle + ") to " + base.branchname + (shouldHaveReleases ? " release" : ""), callback); + } + + return process.nextTick(callback); + }); + }); +}; + var getStatusMessageFromRelease = function (app, options, callback) { var releaseDir = app.get('releasepath') + "/" + options.owner + "/" + options.reponame + "/" + options.branch + "/" + options.rev, reportFile = releaseDir + "/report.json"; + options.attemptsGetReport = (options.attemptsGetReport || 0) + 1; + fs.exists(reportFile, function (exists) { if (!exists) { - fs.exists(releaseDir, function (dirExists) { + return fs.exists(releaseDir, function (dirExists) { if (!dirExists) { return callback("Release directory not found. Probably repository hooks are not configured"); } - if (options.lastAttempt) { + if (options.attemptsGetReport > 10) { return callback("Report file not found"); } //maybe it is building right now - options.lastAttempt = true; - setTimeout(function () { + return setTimeout(function () { getStatusMessageFromRelease(app, options, callback); }, 10000); }); } - fs.readFile(reportFile, function (err, dataBuffer) { - if (err) { - return callback(err); - } - var data = dataBuffer.toString(); - if (!data) { - return callback("Report file not found"); - } - var report = JSON.parse(data); + return setTimeout(function () { + return fs.readFile(reportFile, function (err, dataBuffer) { + if (err) { + return callback(err); + } + var data = dataBuffer.toString(); + if (!data) { + return callback("Report file not found"); + } + var report = JSON.parse(data); - if (report.result === "MBSNotFound") { - return callback("mbs.json is not found"); - } - if (report.err) { - return callback("ERR: " + report.err); - } - if ((report.result.warns.$allMessages || []).length > 0) { - return callback("WARN: " + report.result.warns.$allMessages[0].message); - } - if ((report.result.infos.$allMessages || []).length > 0) { - return callback(undefined, report.result.infos.$allMessages[report.result.infos.$allMessages.length-1].message); - } - return callback(undefined, "OK"); - }); + if (report.result === "MBSNotFound") { + return callback("mbs.json is not found"); + } + if (report.err) { + return callback("ERR: " + report.err); + } + if ((report.result.warns.$allMessages || []).length > 0) { + return callback("WARN: " + report.result.warns.$allMessages[0].message); + } + if ((report.result.infos.$allMessages || []).length > 0) { + return callback(undefined, report.result.infos.$allMessages[report.result.infos.$allMessages.length-1].message); + } + return callback(undefined, "OK"); + }); + }, 1000); }); }; exports.commentOnPullRequest = function (options, callback) { - getStatusMessageFromRelease(options.app, options.headRepoOptions, function (err, successMessage) { - var message = err ? ("Was not built:\r\n\r\n" + err + "\r\n\r\nDO NOT MERGE!") : ("Build OK\r\n\r\n" + successMessage); - settings.createGithub(options.baseRepoOptions.owner).issues.createComment({ - user: options.baseRepoOptions.owner, - repo: options.baseRepoOptions.reponame, - number: options.number, - body: message - }, callback); + options.github = settings.createGithub(options.baseRepoOptions.owner); + return checkPullRequest(options, function (err, successMessage) { + getStatusMessageFromRelease(options.app, options.headRepoOptions, function (err, successMessage) { + var message = err ? ("Was not built:\r\n\r\n" + err + "\r\n\r\nDO NOT MERGE!") : ("Build OK\r\n\r\n" + successMessage); + return writeComment(options, message, callback); + }); }); }; diff --git a/BuildServer/routes/postreceive.js b/BuildServer/routes/postreceive.js index ea2e588..aeedfa0 100644 --- a/BuildServer/routes/postreceive.js +++ b/BuildServer/routes/postreceive.js @@ -8,16 +8,19 @@ var builder = require('../lib/builder'), */ var processPush = function (req, res, payload) { - var repository = payload.repository; + var repository = payload.repository, + options = { + app: req.app, + url: repository.url, + owner: repository.owner.name, + reponame: repository.name, + rev: payload.after, + branch: payload.ref + }; - builder.build({ - app: req.app, - url: repository.url, - owner: repository.owner.name, - reponame: repository.name, - rev: payload.after, - branch: payload.ref - }, function (err, result) { + console.log("Got push event for " + options.owner + "/" + options.reponame + ":" + options.branch); + + builder.build(options, function (err, result) { console.log("Done processing request from GitHub"); console.log("Error: " + err); //console.log("Result:"); @@ -37,12 +40,15 @@ var processPullRequest = function (req, res, payload) { owner: headRepo.owner.name || headRepo.owner.login, reponame: headRepo.name, rev: head.sha, + branchname: head.ref, branch: "refs/heads/" + head.ref }, - baseRepo = payload.repository, + base = pullRequest.base, + baseRepo = base.repo, baseRepoOptions = { owner: baseRepo.owner.name || baseRepo.owner.login, - reponame: baseRepo.name + reponame: baseRepo.name, + branchname: base.ref }, options = { app: req.app, @@ -52,8 +58,10 @@ var processPullRequest = function (req, res, payload) { baseRepoOptions: baseRepoOptions }; + console.log("Got pull request " + action + " event, from " + headRepoOptions.owner + "/" + headRepoOptions.reponame + ":" + headRepoOptions.branchname + " to " + baseRepoOptions.owner + "/" + baseRepoOptions.reponame + ":" + baseRepoOptions.branchname); + if (action !== "opened" && action !== "reopened" && action !== "synchronize") { - console.log("Got '" + action + "' event:"); + //console.log("Got '" + action + "' event:"); //console.log(req.body); return res.send("Only opened/reopened/synchronize actions are supported"); }