Templated configuration is one of the flagship new features in pulse™ 2.0. The motivation behind templates is to eliminate unnecessary duplication in your pulse™ configuration. Almost all continuous integration setups involve projects with similar configurations, and indeed multiple very similar configurations for a single project. Maintaining many similar projects can be painful: every time one small detail changes you need to fix it many times over. Templates solve this problem by allowing you to share common configuration among projects in a natural way.
Perhaps the easiest way to understand templating is by considering two example expected usage patterns:
- Within an organisation, it is common for development teams to standardise on a tool set. Often all teams will use the same SCM and build tools. Thus all projects in pulse™ will use the same SCM server, and same build tool to build. Using templates you can configure this standard tool set once for all projects in your organisation. If your SCM server moves to a new host, there is just one field to update in pulse™.
- A single project will usually have multiple build targets. For example, one target may run all tests, and another may build a release package. You might like to set up pulse™ projects for each, to run your tests continuously and to build packages on demand. These two pulse™ projects will be identical apart from the configuration of the build trigger and target. Using templates, you can set up a parent template that contains all of the shared project details. Then each specific project can inherit those details and specify only their trigger and target.
Templates and Concrete Instances
A template is essentially a partial configuration of a project or agent. A concrete instance, on the other hand, is a complete configuration of a project or agent that can be used for building. Templates and concrete instances are arranged in a hierarchy where a child inherits properties from its parent, and can optionally add to or override these properties. Concrete instances can only appear at the leaves of the hierarchy – i.e. they may not be inherited from.
An example project hiearchy appears below:
This hierarchy shows the global project template, the root of the project hierachy which can never be deleted. This can be used to configure properties that are shared for all projects on your pulse™ server. Inheriting from it is a template that specifies a common project tool set of Subversion and Ant. Inheriting from this are two project-specific templates, one for Pulse 1.2.x and another for Pulse 2.0.x. Finally, the leaves of the hiearchy are concrete projects used to build continuous, nightly and release configurations of Pulse.
Creating and Configuring Templates
New templates, and indeed concrete instances, can be added by by choosing the appropriate link on the template view for the parent template. So, for example, to add the first project or template, you should navigate to the global project template and select "add new" or "add new template" as desired:
The add link will take you to the add project wizard which will guide you through the rest of the steps. To further configure a project, select the "configure" link on its template view.
You configure templates just like concrete instances with one exception: there are no required fields. You are free to leave fields blank and fill them in in descendents. Validation will only be applied to fields that are filled in. When viewing configuration, you can easily see which fields are currently inherited from an ancestor and which are overridden locally via small icons beside the field. Inherited fields have a blue "i" icon, and overridden items a yellow "o" icon, as shown below:
Templates and Collections
Templating also works for complex configuration items in collections. All such configuration items have a name which is used to identify the item in the user interface, and to match items for inheritance. Similar to fields, collection items are annotated with icons to identify if they are completely inherited (including all of their own fields and nested items), inherited with some fields overridden, or defined locally.
For example, consider the following table of build stages:
The "default" and "inherited" stages are inherited unchanged from above. The "overridden" stage is inherited, but includes an overridden field. The "local" stage is defined locally, and thus has no annotation.
Note in the screenshot above that only the "local" stage may be deleted at this level, as the other stages are inherited. Those stages may only be deleted where they are defined. You can, however, hide an inherited item. Hiding an item is analogous to deleting it for the local instance and those that inherited from it. Hiding is most useful for configuration of small exceptions to the inheritance hierarchy.
Hidden items are displayed greyed-out in the collections table:
They may be restored using the provided link, which will re-add them to the collection as completely-inherited items.
When you hide an item, all overridden fields in the item and its descendents are deleted. Restoring the item will not restore these overridden values.
Cloning and Smart Cloning
All named configuration items, including templates, concrete instances and collection items, may be cloned. Cloning an item creates an identical sibling with a new name, and is a convenient way to generate similar configurations.
There is a natural connection between cloning and templating: cloning produces identical configuration, and templating allows you to share configuration. For this reason pulse™ also supports smart cloning for projects and agents. A smart clone has the same effect as a clone, in that an identical sibling is created, but it is achieved using templates rather than copying. This produces a more maintainable hierarchy, as the shared values are only defined once.
When you smart clone an instance, pulse™ will:
- Extract a new template parent from the instance, containing all of the actual configuration.
- Add a new empty child to this template (the clone), as a sibling of the original instance.
After the operation both the original instance and the clone will fully inherit their configuration from the new parent template. Typically you will then override certain fields in one or the other.
To smart clone an instance, navigate to its template view and select the smart clone link. You will then see the "smart clone" form:
In all cases, you will need to provide a name for the new extracted parent template and the new clone. In the case where the cloned instance has child instances, you can also optionally clone those descendents by selecting the appropriate checkbox and providing new names for those clones. This allows you to smart clone a whole inheritance sub-tree in one step.
Templates and Permissions
Access to both projects and agents may be controlled in pulse™ using permissions. For the most part, permissions are inherited just like all other configuration. There is one exception, however: the "view" permission is reverse inherited. In other words, if you have access to view some instance, then you automatically have access to view all of its ancestors. The reasoning for this is two-fold:
- As configuration is inherited from above, by observing a project's configuration you already have access to some information about the ancestor configuration.
- Without reverse inheritance, the user interface would become more complicated, as it would need to deal with the possibility of "visibility holes" in the hiearchy.
These complications were felt to outweigh the benefits of being able to restrict view access to ancestors. Note that view permission does not allow the ancestors to be edited or controlled in any way.