如何在多个Pull Requests之间并行运行持续集成?

4
我正在测试使用Jenkins和Github pull request builder plugin。我已经在Github上成功设置了一个玩具项目,并在Jenkins的开发安装中进行了设置,以便在提出PR或将更改推送到PR分支时触发构建。大多数情况下,这符合要求 - 有些事情与首选工作流程不匹配,但是不必编写和维护自己的插件的自由是一件大事。
我有一个潜在的问题。该插件会排队所有它看到的所有PR中的所有推送,并且似乎始终只运行一个作业,即使有可用的空闲执行程序。在现实世界的项目中,我们可能有10个活动的PR,每个PR可能会在一天内获得几个推送的更新,以响应QC注释,而完整的CI运行需要> 30分钟。但是,我们已经提供了足够的构建执行程序以同时运行多个作业。
我无法看到任何配置PR请求构建器以在同一触发器上同时处理多个作业的方法,但我可能在Jenkins的其他地方错过了一些基本内容。是否有一种方法可以做到这一点,而无需自定义插件?
我在一台运行Ubuntu 14.04的新服务器上安装了Jenkins ver.1.649(在VirtualBox客户端上),并按照ghprb插件(当前版本为1.30.5)中的README进行了设置,包括在Github上设置了一个Jenkins“机器人”账户,作为协作者来运行所有与Github的集成API调用。
我想知道如果我克隆该作业(创建新项和“复制现有项”),它的行为会是什么样子,但我预计这将导致同样的作业多次运行,没有好处,而不是与其他轮询相同的PR池的作业智能交互。
2个回答

6
我在探索问题时找到了配置设置。当你知道它是哪个配置项时,它真的很容易,但是Jenkins有很多配置需要处理,特别是当你探索插件时。
关键是,可以并行提供排队的作业选项(如果可用执行程序),这是核心Jenkins配置,而不是Github PR构建器的一部分。
所以,只需检查选项必要时执行并发构建。此选项应该在第一个未命名的部分底部找到。这是一个非常基本的Jenkins选项,像我这样的新手由于其他选项太多而错过了它。

1
谢谢!我也忽略了这个复选框!针对“jenkins并行作业”的90%搜索结果告诉我要使用Build Flow插件或其他插件。你可能是世界上唯一发现这个复选框的人。 - Daniel Darabos
我想知道它是如何工作的,因为每个任务只有一个工作区。如果没有涉及到检出git存储库,那么这些任务肯定可以并行运行。但在这种情况下,git存储库被检出到工作区中。如果有多个PR并且同时运行2个作业,则最后检出的PR是有效代码,但测试将并行运行于最后一个PR。 - lorem
@user2283864:当作业同时运行时,Jenkins会自动创建多个工作区(例如,在名称后面添加@2)。 - Neil Slater
太好了!谢谢!好知道! :) - lorem

3
也许现在回答这个问题有点晚了,但是经过几天的研究,我找到了一种在github中为每个PR创建多个作业的方法。我展示的代码适用于github企业版,但是只需稍微调整url和git命令,也可以在一般的github(bitbucket)上很好地工作。
主线存储库需要有一个文件,我称之为PRJob.groovy,其中包含:
import groovy.json.JsonSlurper


gitUrl = GIT_URL

repoRestUrl = "${GITHUB_WEB_URL}/repos/${project}/${repo}"

def getJSON(url) {
  def conn = (HttpURLConnection) new URL(url).openConnection()
  conn.setRequestProperty("Authorization", "token ${OAUTH_TOKEN}");
  return new JsonSlurper().parse(new InputStreamReader(conn.getInputStream()))
}


def createPipeline(name, description, branch, prId) {

  return pipelineJob(name) {
      delegate.description description
      if (ENABLE_TRIGGERS == 'true') {
        triggers {
          cron 'H H/8 * * *'
          scm 'H/5 * * * *'
        }
      }
      quietPeriod(60)
      environmentVariables {
        env 'BRANCH_NAME', branch
        env 'PULL_REQUEST', prId
        env 'GITHUB_WEB_URL', GITHUB_WEB_URL
        env 'OAUTH_TOKEN', OAUTH_TOKEN
        env 'PROJECT', project
        env 'REPO', repo
      }
      definition {
        cpsScm {
          scriptPath "Jenkinsfile"
          scm {
            git {
              remote {
                credentials "jenkins-ssh-key"
                delegate.url gitUrl
                if (prId != "") {
                  refspec "+refs/pull/${prId}/*:refs/remotes/origin/pr/${prId}/*"
                }
              }
              delegate.branch branch
            }
          }
        }
      }
    }
}




def createPRJobs() {

  def prs = getJSON("${repoRestUrl}/pulls?state=open")

  if (prs.size() == 0) {

    def mergedPrs = getJSON("${repoRestUrl}/pulls?state=closed")
    if (mergedPrs.size() == 0) {
      throw new RuntimeException("No pull-requests found; auth token has likely expired")
    }
  }

  prs.each { pr ->
    def id = pr.get("number")
    def title = pr.get("title")
    def fromRef = pr.get("head")
    def fromBranchName = fromRef.get("ref")
    def prRepo = fromRef.get("repo")
    def repoName = prRepo.get("name")
    def prHref = pr.get("url")

    createPipeline("${repo}-PR-${id}-${fromBranchName}",
        "${prHref} Pull Request ${id}: ${title}", "origin/pr/${id}/head", id)
  }

}

createPRJobs()

这将为每个PR创建1个jenkins作业。 这依赖于项目具有可用于运行管道作业的Jenkinsfile。示例Jenkinsfile如下所示:

//Jenkinsfile for building and creating jobs
commitId = null
repoRestUrl = "${GITHUB_WEB_URL}/repos/${PROJECT}/${REPO}"

try{
   stage('Install and Tests') {
      runTest("Hello")
   }
   notify_github 'success'
}catch (Exception e) {
  notify_github 'failure'
  print e
  throw e
}

def runTest(String someDummyVariable) {
  node {
    checkout scm
    sh 'git clean -qdf'
    if (env.PULL_REQUEST == ""){
       sh 'git rev-parse --verify HEAD > commit.txt'
    } else {
        // We check out PR after it is merged with master, but we need to report the result against the commit before merge
       sh "git rev-parse refs/remotes/origin/pr/${env.PULL_REQUEST}/head^{commit} > commit.txt"

    }
    commitId = readFile 'commit.txt'
    echo commitId
    sh 'rm -f commit.txt'

    //Here goes your code for doing anything
    sh 'echo "Hello World!!!!!"'


  }
}


def http_post(url, rawJson) {
  def conn = (HttpURLConnection) new URL(url).openConnection()
  conn.setRequestProperty("Authorization", "token ${OAUTH_TOKEN}");
  conn.doOutput = true
  conn.requestMethod = "POST"
  conn.setRequestProperty("Content-Type", "application/json")
  def wr = new OutputStreamWriter(conn.getOutputStream());
  wr.write(rawJson);
  wr.close()

  def code = conn.getResponseCode()
  if (code < 200 || code >= 300){
    println 'Failed to post to ' + url
    def es = conn.getErrorStream();
    if (es != null) {
      println es.getText()
    }
  }
}


def notify_github(state) {

  http_post(
    "${repoRestUrl}/statuses/${commitId}",
    """
      { "state": "${state}",
       "target_url": "${env.BUILD_URL}",
       "description": "Build Pipeline",
       "context": "Build Pipeline"
      }
    """
  )
}

希望这对某些人有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接