Products
Support
Company
Contact
- Inedo HQ
56 Front St. Upper
Berea, OH 44017 United States - Contact Form
BuildMaster’s variable-replacement functionality can be extended by writing a custom function using any .NET language.
This tutorial is designed for BuildMaster v5. Feel free to contact us with any questions.
A variable function works just like a variable in BuildMaster, except
it may optionally take arguments, and its value cannot be set. For example,
one of the built-in
functions is MajorVersionNumber
; it can be invoked like $MajorVersionNumber
to return the major version component of the current release number, or it can
be invoked with an explicit parameter like $MajorVersionNumber("5.0.2")
.
A custom function works exactly the same way, only using custom logic supplied
in an extension.
In this tutorial, we will create a new function that returns name of an
application’s issue tracking provider. To start, create a new class named
IssueTrackerNameVariableFunction
that inherits
ScalarVariableFunctionBase:
public class IssueTrackerNameVariableFunction : ScalarVariableFunctionBase { protected override object EvaluateScalar(IGenericBuildMasterContext context) { // TODO: return result of function evaluation } }
For scalar variables, the EvaluateScalar method is invoked by BuildMaster at execution time. The context parameter contains various BuildMaster-related information, such as the current application ID, release number, build number, and so on. See the IGenericBuildMasterContext interface for more information regarding its available properties.
Arguments supplied to the function are automatically bound to properties decorated with the VariableFunctionParameterAttribute
attribute. In this tutorial, we allow one optional argument containing an application ID. The following code
will look up the appropriate issue tracker name and return it:
[DisplayName("applicationId")] [Description("The ID of the application whose name will be returned. If empty, the application ID " + "in the current context will be used.")] [VariableFunctionParameter(0, Optional = true)] public int? ApplicationId { get; set; } protected override object EvaluateScalar(IGenericBuildMasterContext context) { int? applicationId = this.ApplicationId ?? context.ApplicationId; if (applicationId == null || applicationId <= 0) return "INVALID APPLICATION ID"; var application = DB.Applications_GetApplication(applicationId) .Applications_Extended .FirstOrDefault(); if (application == null) return "INVALID APPLICATION ID"; if (application.IssueTracking_Provider_Id == null) return string.Empty; var issueTracker = DB.Providers_GetProvider(application.IssueTracking_Provider_Id); return issueTracker.Provider_Name; }
This fairly simple code uses the application ID from the first argument, if it is supplied. Otherwise, it uses the application ID in context. Then, it looks up the application in BuildMaster, and finally looks up the issue tracking provider as well and returns its name.
The function is now implemented, but we need to add an attribute to tell BuildMaster how this should be invoked. We can do this by adding a VariableFunctionProperties attribute to the class:
[ScriptAlias("IssueTrackerName")] [Description("Returns the name of the issue tracker associated with the application in the current scope.")] [VariableFunctionProperties( Scope = VariableFunctionScope.Application, Category = "Issue Tracking")] public class IssueTrackerNameVariableFunction : ScalarVariableFunctionBase
The ScriptAliasAttribute
specifies the name of the function for use in OtterScript, and the DescriptionAttribute
documents what the function returns. The Scope
value helps BuildMaster understand where the function is meant
to be used. The optional Category
value is used for informational purposes only, particularly the grouping on
the Functions documentation page. Since this action has to do with issue
tracking, we’ll just use that as the category.
This is enough information for BuildMaster. Once compiled as part of an extension and added to the extension library, this function can now be invoked like any of the built in functions.
It is up to you to determine how to perform input validation. In this tutorial, we’ve just returned OBNOXIOUS STRING LITERALS that would usually be easy to notice. You may also choose to throw an exception, which would halt whatever was in progress and log an error message, or you may simply use a default value instead of the bad input – use whatever works best for your use case.