Builder Template

Often, developers create build steps as shell scripts to address enterprise requirements, for custom deployments, packaging, notification, QA integration, etc. The same script is used across a large set of jobs and only differ by few attributes. Maintaining such scripts and avoiding copy/paste mistakes can be a nightmare on large Jenkins installations. The builder template creates a new type of builder, thereby eliminating the need of copying a script, and highlighting the things that need to be filled in by users.

A similar goal can be achieved by a custom plugin development. A builder template, compared to a custom plugin, has the following trade-offs:

  • Writing a plugin requires a significant one-time learning cost, whereas a simple template can be developed rapidly. So for simple things, templates tend to get the job done more quickly.
  • A template can be created, modified, or removed without restarting Jenkins.
  • You get a lot more tool assistance for plugin development, thanks to the great IDEs available for Java. So for complex things, plugins tend to get the job done more quickly and elegantly.

Compared to job templates, builder templates tend to be more flexible, as the person configuring jobs still has substantial freedom to control how it works. A builder template is also the easiest kind of template to create, especially in combination with the specialized transformers that generate shell scripts. This makes builder templates the best entry point to the template plugin.

Creating a new Builder Template

The new item wizard offers a few options, including that of creating a new Builder Template. The template configuration allows the administrator to define the execution details of any custom tool, which will be exposed to users as a new build step.

Defining a Transformer

The transformer controls how your builder template maps to the actual builder that carries out the build.

One way to define a transformer is to use one of the standard transformers that takes an instance and produces XML. (In this case, the XML should match that saved for a build step in a job configuration.) As we discussed in the tutorial, a good way to do this is programming by example, where you manually create a freestyle project and configure it, then observe what XML it is producing.

The other way to do this, which is often preferable, is to use one of the specialized transformers that only work with builder templates, which we’ll discuss below.

Generate shell script via Jelly/Groovy

For shell scripts, a simpler way to define a builder template is to generate the script using Jelly or Groovy instrumentation to process the attributes and generate the adequate script. These specialized transformers eliminate the need of “programming by example” by focusing on the shell scripts.

See the transformer reference section for more details about the general syntax of Jelly/Groovy template, as well as available variable bindings.

The following screenshot demonstrates a Groovy-generated shell script to run the du command on an arbitrary user home directory. The \${… } in the text should not be mistaken for a shell variable reference. These Groovy expressions are expanded during the transformation before it gets passed to the shell.

Such templates are easily created by a Unix administrator to automate maintenance processes, and can be used to give users the ability to run tools without the risk of allowing arbitrary commands to be executed.

Figure 10.9. Generate Shell Script via Jelly/Groovy

template script

Predefine variables for shell/batch script

An alternate transformer option, Sets some variables using Groovy then runs a shell/batch script, is also available. Unlike the previous transformer, the shell (or Windows batch) script section is not processed using any template language. Instead, your template attributes are directly available as shell variables, and you can optionally run a Groovy program to define other shell variables (typically based on the Jenkins build object, as in the section called “Template evaluation timing”).

For example, suppose you have defined an auxiliary template for compiler options (the section called “Auxiliary Template” has more).

Figure 10.10. Defining an auxiliary template for compiler options

compiler option

Now if you want to collect a number of options in a builder template, and produce an options variable suitable for interpolation in a shell script, you can use a brief Groovy snippet to process the options into a flat string.

Figure 10.11. Defining attributes for a compiler builder template

compiler template attributes

Figure 10.12. Defining a transformer for a compiler builder template

compiler template transformer

When this builder template is used in a job definition, compiler options may be defined structurally.

Figure 10.13. Using a compiler builder template

compiler template usage

The resulting build will run: cc -O3 -g --sysroot=/usr/local *.c

Template evaluation timing

Another key difference of these specialized transformers is the difference in the timing of the template evaluation. The general purpose transformers perform transformation asynchronously from the execution of builds (roughly speaking, think of it as transforming right after the job is configured), whereas these shell script transformers run during the build.

This allows you to perform contextual transformations that depend on the parameters and conditions of the in-progress build (such as the changes in the source code, or what started it).

To do so, use the build variable from within your template to access the AbstractBuild object that represents the current build in progress.

Tip

One special use for a builder template is to use Groovy to compute some variables, and then make them available to subsequent build steps—without actually running anything itself. The Sets some variables using Groovy then runs a shell/batch script transformer can be used for that purpose. Just leave Shell/Batch Script empty, and use something like the following in Groovy Script:

e = new hudson.EnvVars()
e.put('VAR1', 'val1')
e.put('VAR2', 'val2')
build.environments.add(hudson.model.Environment.create(e))

These variables can now be used like any others in subsequent build steps, e.g. $VAR1 from Execute shell.