简体   繁体   English

如何告诉 Jenkins“在文件夹 X 中构建每个项目”?

[英]How to tell Jenkins "Build every project in folder X"?

I have set up some folders (Using Cloudbees Folder Plugin ).我已经设置了一些文件夹(使用Cloudbees 文件夹插件)。

It sounds like the simplest possible command to be able to tell Jenkins: Build every job in Folder X.这听起来像是能够告诉 Jenkins 的最简单的命令:在文件夹 X 中构建每个作业。

I do not want to have to manually create a comma-separated list of every job in the folder.我不想手动创建文件夹中每个作业的逗号分隔列表。 I do not want to add to this list whenever I want to add a job to this folder.每当我想向此文件夹添加作业时,我都不想添加到此列表中。 I simply want it to find all the jobs in the folder at run time, and try to build them.我只是想让它在运行时找到文件夹中的所有作业,并尝试构建它们。

I'm not finding a plugin that lets me do that.我没有找到让我这样做的插件。

I've tried using the Build Pipeline Plugin , the Bulk Builder Plugin , the MultiJob plugin , and a few others.我尝试过使用Build Pipeline PluginBulk Builder PluginMultiJob 插件和其他一些插件 None seem to support the use case I'm after.似乎没有人支持我所追求的用例。 I simply want any Job in the folder to be built.我只是想要构建文件夹中的任何作业。 In other words, adding a job to this build is as simple as creating a job in this folder.换句话说,向此构建添加作业就像在此文件夹中创建作业一样简单。

How can I achieve this?我怎样才能做到这一点?

I've been using Jenkins for some years and I've not found a way of doing what you're after.我已经使用 Jenkins 好几年了,但我还没有找到一种方法来做你所追求的事情。

The best I've managed is: I have a "run every job" job (which contains a comma-separated list of all the jobs you want).我管理的最好的是:我有一个“运行每个作业”的作业(其中包含一个以逗号分隔的所有您想要的作业列表)。 Then I have a separate job that runs periodically and updates the "run every job" job as new projects come and go.然后我有一个单独的工作,它定期运行,并随着新项目的来来往往更新“运行每个工作”的工作。

One way to do this is to create a Pipeline job that runs Groovy script to enumerate all jobs in the current folder and then launch them.一种方法是创建一个管道作业,该作业运行 Groovy 脚本以枚举当前文件夹中的所有作业,然后启动它们。

The version below requires the sandbox to be disabled (so it can access Jenkins.instance ).下面的版本需要禁用沙箱(以便它可以访问Jenkins.instance )。

def names = jobNames()
for (i = 0; i < names.size(); i++) {
    build job: names[i], wait: false
}

@NonCPS
def jobNames() {
  def project = Jenkins.instance.getItemByFullName(currentBuild.fullProjectName)
  def childItems = project.parent.items

  def targets = []
  for (i = 0; i < childItems.size(); i++) {
      def childItem = childItems[i]
      if (!childItem instanceof AbstractProject) continue;
      if (childItem.fullName == project.fullName) continue;

      targets.add(childItem.fullName)
  }

  return targets
}

If you use Pipeline libraries, then the following is much nicer (and does not require you to allow a Groovy sandbox escape:如果您使用 Pipeline 库,那么以下内容会更好(并且不需要您允许 Groovy 沙箱转义:

Add the following to your library:将以下内容添加到您的库中:

package myorg;

public String runAllSiblings(jobName) {
  def names = siblingProjects(jobName)
  for (def i = 0; i < names.size(); i++) {
    build job: names[i], wait: false
  }
}

@NonCPS
private List siblingProjects(jobName) {
  def project = Jenkins.instance.getItemByFullName(jobName)
  def childItems = project.parent.items

  def targets = []
  for (def i = 0; i < childItems.size(); i++) {
    def childItem = childItems[i]
    if (!childItem instanceof AbstractProject) continue;
    if (childItem.fullName == jobName) continue;

    targets.add(childItem.fullName)
  }
  return targets
}

And then create a pipeline with the following code:然后使用以下代码创建管道:

(new myorg.JobUtil()).runAllSiblings(currentBuild.fullProjectName)

Yes, there are ways to simplify this further, but it should give you some ideas.是的,有一些方法可以进一步简化这一点,但它应该会给你一些想法。

I developed a Groovy script that does this.我开发了一个执行此操作的 Groovy 脚本。 It works very nicely.它工作得很好。 There are two Jobs, initBuildAll, which runs the groovy script and then launches the 'buildAllJobs' jobs.有两个作业,initBuildAll,它运行 groovy 脚本,然后启动“buildAllJobs”作业。 In my setup, I launch the InitBuildAll script daily.在我的设置中,我每天启动 InitBuildAll 脚本。 You could trigger it another way that works for you.你可以用另一种适合你的方式触发它。 We aren't full up CI, so daily is good enough for us.我们没有完成 CI,所以每天对我们来说已经足够了。

One caveat: these jobs are all independent of one another.一个警告:这些工作都是相互独立的。 If that's not your situation, this may need some tweaking.如果这不是您的情况,则可能需要进行一些调整。

These jobs are in a separate Folder called MultiBuild.这些作业位于名为 MultiBuild 的单独文件夹中。 The jobs to be built are in a folder called Projects.要构建的作业位于名为 Projects 的文件夹中。

import com.cloudbees.hudson.plugins.folder.Folder
import javax.xml.transform.stream.StreamSource
import hudson.model.AbstractItem
import hudson.XmlFile
import jenkins.model.Jenkins


    Folder findFolder(String folderName) {
      for (folder in Jenkins.instance.items) {
        if (folder.name == folderName) {
          return folder
        }
      }
      return null
    }

    AbstractItem findItem(Folder folder, String itemName) {
      for (item in folder.items) {
        if (item.name == itemName) {
          return item
        }
      }
      null
    }


    AbstractItem findItem(String folderName, String itemName) {
      Folder folder = findFolder(folderName)
      folder ? findItem(folder, itemName) : null
    }

    String listProjectItems() {
      Folder projectFolder = findFolder('Projects')
      StringBuilder b = new StringBuilder()
      if (projectFolder) {

        for (job in projectFolder.items.sort{it.name.toUpperCase()}) {
          b.append(',').append(job.fullName)
        }
        return b.substring(1) // dump the initial comma
      }
      return b.toString()
    }

    File backupConfig(XmlFile config) {
      File backup = new File("${config.file.absolutePath}.bak")
      FileWriter fw = new FileWriter(backup)
      config.writeRawTo(fw)
      fw.close()
      backup
    }


    boolean updateMultiBuildXmlConfigFile() {
      AbstractItem buildItemsJob = findItem('MultiBuild', 'buildAllProjects')
      XmlFile oldConfig = buildItemsJob.getConfigFile()

      String latestProjectItems = listProjectItems()
      String oldXml = oldConfig.asString()
      String newXml = oldXml;
      println latestProjectItems
      println oldXml

      def mat = newXml =~ '\\<projects\\>(.*)\\<\\/projects\\>'
      if (mat){
        println mat.group(1)
        if (mat.group(1) == latestProjectItems) {
           println 'no Change'
           return false;
        } else {
          // there's a change
             File backup = backupConfig(oldConfig)
           def newProjects = "<projects>${latestProjectItems}</projects>"
           newXml = mat.replaceFirst(newProjects)
           XmlFile newConfig = new XmlFile(oldConfig.file)
           FileWriter nw = new FileWriter(newConfig.file)
           nw.write(newXml)
           nw.close()
           println newXml
           println 'file updated'
           return true
        }
      }
      false
    }

    void reloadMultiBuildConfig() {
      AbstractItem job = findItem('MultiBuild', 'buildAllProjects')

      def configXMLFile = job.getConfigFile();
      def file = configXMLFile.getFile();

      InputStream is = new FileInputStream(file);

      job.updateByXml(new StreamSource(is));
      job.save();

      println "MultiBuild Job updated"

    }

    if (updateMultiBuildXmlConfigFile()) {
      reloadMultiBuildConfig()
    }

A slight variant on Wayne Booth's " run every job " approach.韦恩·布斯 (Wayne Booth) 的“运行每项工作”方法的一个轻微变体。 After a little head scratching I was able to define a "run every job" in Job DSL format .经过一番摸索,我能够以Job DSL 格式定义“运行每个作业”。

The advantage being I can maintain my job configuration in version control.优点是我可以在版本控制中维护我的作业配置。 eg例如

job('myfolder/build-all'){
   publishers {
         downstream('myfolder/job1')
         downstream('myfolder/job2')
         downstream('myfolder/job2')
   }
}

Pipeline Job流水线作业

When running as a Pipeline job you may use something like:当作为流水线作业运行时,您可以使用以下内容:

echo jobNames.join('\n')
jobNames.each {
    build job: it, wait: false
}

@NonCPS
def getJobNames() {
  def project = Jenkins.instance.getItemByFullName(currentBuild.fullProjectName)
  project.parent.items.findAll {
      it.fullName != project.fullName && it instanceof hudson.model.Job
  }.collect { it.fullName }
}


Script Console脚本控制台

Following code snippet can be used from the script console to schedule all jobs in some folder:可以从脚本控制台使用以下代码片段来安排某个文件夹中的所有作业:

import hudson.model.AbstractProject

Jenkins.instance.getAllItems(AbstractProject.class).each {
  if(it.fullName =~ 'path/to/folder') {
    (it as AbstractProject).scheduleBuild2(0)
  }
}

With some modification you'd be able to create a jenkins shared library method (requires to run outside the sandbox and needs @NonCPS ), like:通过一些修改,您将能够创建一个 jenkins 共享库方法(需要在沙箱之外运行并且需要@NonCPS ),例如:

import hudson.model.AbstractProject

@NonCPS
def triggerItemsInFolder(String folderPath) {
  Jenkins.instance.getAllItems(AbstractProject.class).each {
    if(it.fullName =~ folderPath) {
      (it as AbstractProject).scheduleBuild2(0)
    }
  }
}

Reference pipeline script to run a parent job that would trigger other jobs as suggested by @WayneBooth参考管道脚本来运行一个父作业,该作业会按照@WayneBooth 的建议触发其他作业

pipeline {
    agent any
    stages {
 
        stage('Parallel Stage') {
            parallel {
                stage('Parallel 1') {
                    steps {
                        build(job: "jenkins_job_1")
                    }
                }
                stage('Parallel 2') {
                    steps {
                        build(job: "jenkins_job_2")
                    }
                }
            }
        }
}

The best way to run an ad-hoc command like that would be using the Script Console (can be found under Manage Jenkins ).运行这样的临时命令的最佳方法是使用脚本控制台(可以在Manage Jenkins下找到)。

The console allows running Groovy Script - the script controls Jenkins functionality.控制台允许运行 Groovy 脚本 - 该脚本控制 Jenkins 功能。 The documentation can be found under Jenkins JavaDoc .该文档可以在Jenkins JavaDoc下找到。

A simple script triggering immediately all Multi-Branch Pipeline projects under the given folder structure (in this example folder/subfolder/projectName ):一个简单的脚本立即触发给定文件夹结构下的所有多分支管道项目(在本示例中为folder/subfolder/projectName ):

import org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject
import hudson.model.Cause.UserIdCause

Jenkins.instance.getAllItems(WorkflowMultiBranchProject.class).findAll {
    return it.fullName =~ '^folder/subfolder/'
}.each {
    it.scheduleBuild(0, new UserIdCause())
}

The script was tested against Jenkins 2.324.该脚本针对 Jenkins 2.324 进行了测试。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM