Skip to content

Writing Tasks

Aaron Jensen edited this page Feb 25, 2020 · 13 revisions

Table of Contents

Writing a Task

Task Definition

A Whiskey task is a PowerShell function that has a Whiskey.Task attribute:

function FUNCTION_NAME
{
    [Whiskey.Task('TASK_NAME')]
    [CmdletBinding()]
    param(
    )

    Set-StrictMode -Version 'Latest'
}

Task Parameters

To allow users to pass values to your task, create parameters on your function. Whiskey will map the parameter names to the property names from the build YAML.

function FUNCTION_NAME
{
    [Whiskey.Task('TASK_NAME')]
    [CmdletBinding()]
    param(
        [string]$Property
    )

    Set-StrictMode -Version 'Latest'
}

With the above task, your task would be used in the YAML like this:

- TASK_NAME:
    Property: Value

With this YAML, Whiskey will pass Value to your function's Property parameter.

You can get the raw, parsed YAML as a hashtable by having a TaskParameter parameter on your function:

function FUNCTION_NAME
{
    [Whiskey.Task('TASK_NAME')]
    [CmdletBinding()]
    param(
        [hasthable]$TaskParameter
    )

    Set-StrictMode -Version 'Latest'
}

Named parameters are never passed in the TaskParameter hashtable.

All values from YAML files are strings and are passed to your function as strings. If a named parameter has a specific type, PowerShell must be able to implicity convert strings to that type. Additionally, Whiskey will convert YAML booleans (yes, no, true, false, 1, or 0) to PowerShell booleans for any parameters whose types are [Switch] or [bool].

Automatic Parameter Values from Variables

If you want the value of a parameter to come from a Whiskey variable, decorate the parameter with Whiskey's Whiskey.Tasks.ParameterValueFromVariable attribute, passing the variable expression (method/property names allowed):

function FUNCTION_NAME
{
    [Whiskey.Task('TASK_NAME')]
    [CmdletBinding()]
    param(
        [Whiskey.Tasks.ParameterValueFromVariable('WHISKEY_ENVIRONMENT')]
        $Environment,
        
        [Whiskey.Tasks.ParameterValueFromVariable('WHISKEY_ENVIRONMENT.Length')]
        $Length
    )

    Set-StrictMode -Version 'Latest'
}

See Variables for a list of variables available.

Global Reserved Parameters

All Whiskey tasks have these global, reserved parameters, which you should not define on your task:

  • OnlyDuring
  • ExceptDuring
  • OnlyOnBranch
  • ExceptOnBranch
  • OnlyBy
  • ExceptBy
  • WorkingDirectory
  • IfExists
  • UnlessExists

Reserved properties are never passed to your function, either as a parameter or in the TaskParameter hashtable.

Resolving Paths

If you have a parameter that is a path to a file or directory, Whiskey can validate the path exists and convert it to a literal path for you, converting directory separators to the preferred separator for the build's current platform. This allows users to use either forward or backward slashes and your task doesn't have to worry about converting them.

Whiskey resolves all paths relative to the current task's working directory (which by default is the directory of the build's whiskey.yml file, or the directory chosen by the user via the WorkingDirectory common task properth). It passes relative paths to your task. To enable this, add the Whiskey.Tasks.ValidatePath attribute to the function parameter (or parameters) that should be resolved as paths.

function FUNCTION_NAME
{
    [Whiskey.Task('TASK_NAME')]
    [CmdletBinding()]
    param(
        [Whiskey.Tasks.ValidatePath()]
        [string]$Path
    )

    Set-StrictMode -Version 'Latest'
}

By default, the property can be empty and any paths that are given in YAML must exist. If you want to require that the user give at least one path (and fail the build if they don't), use the Mandatory property:

function FUNCTION_NAME
{
    [Whiskey.Task('TASK_NAME')]
    [CmdletBinding()]
    param(
        # The user must supply a path to a single file/directory that exists.
        [Whiskey.Tasks.ValidatePath(Mandatory)]
        [string]$Path
    )

    Set-StrictMode -Version 'Latest'
}

To support multiple paths, set the type of the parameter to [string[]]:

function FUNCTION_NAME
{
    [Whiskey.Task('TASK_NAME')]
    [CmdletBinding()]
    param(
        # The user can use wildcards that point to multiple items.
        [Whiskey.Tasks.ValidatePath(Mandatory)]
        [string[]]$Path
    )

    Set-StrictMode -Version 'Latest'
}

If the path must be to a file or directory, use the PathType property to specify which. The build will fail if any path in the YAML isn't of the given type.

function FUNCTION_NAME
{
    [Whiskey.Task('TASK_NAME')]
    [CmdletBinding()]
    param(
        # Must be paths to files only.
        [Whiskey.Tasks.ValidatePath(PathType='File')]
        [string[]]$FilePath,
        
        # Must be paths to directories only.
        [Whiskey.Tasks.ValidatePath(PathType='Directory')]
        [string[]]$DirectoryPath
    )

    Set-StrictMode -Version 'Latest'
}

By default, the path must point to a file or directory that exists. If you'd like to allow the user to supply a path to a file or directory your task will create, use the AllowNonexistent property:

function FUNCTION_NAME
{
    [Whiskey.Task('TASK_NAME')]
    [CmdletBinding()]
    param(
        # Must be paths to files only.
        [Whiskey.Tasks.ValidatePath(AllowNonexistent)]
        [string]$Path
    )

    Set-StrictMode -Version 'Latest'

    if( -not (Test-Path -Path $Path -PathType Leaf) )
    {
        New-Item -Path $Path -ItemType 'File'
    }
}

By default, paths must be under the build root (i.e. the directory where a build's whiskey.yml file is). If you want your task to access items outside this directory, use the AllowOutsideBuildRoot property:

function FUNCTION_NAME
{
    [Whiskey.Task('TASK_NAME')]
    [CmdletBinding()]
    param(
        # Must be paths to files only.
        [Whiskey.Tasks.ValidatePath(AllowOutsideBuildRoot)]
        [string]$Path
    )

    Set-StrictMode -Version 'Latest'
}

Restricting Supported Operating Systems

If your task should only run on certain platforms/operating systems, set the Platform property on your task's TaskAttribute to the platform(s) it is restricted to. If the task runs on an unsupported platform, the build will fail. By default, tasks can run on all platforms. Supported platforms are Windows, Linux, and MacOS. For example, a task with this task attribute will only run on Windows:

[Whiskey.Task('TASK_NAME',Platform=[Whiskey.Platform]::Windows)]

To run on multiple platforms, use PowerShell's -bor operator:

[Whiskey.Task('TASK_NAME',Platform=[Whiskey.Platform]::Windows -bor [Whiskey.Platform]::Linux)]

Aliases

You can define aliases for your task name. This is useful for making your tasks more discoverable or for re-naming tasks without requiring people to update their build scripts when they upgrade. Use the Aliases property to define a list of aliases.

[Whiskey.Task('TASK_NAME',Aliases=('OldName','AnotherOldName'))]

If you want to warn when someone uses your task using an aliased name, use the WarnWhenUsingAlias property.

[Whiskey.Task('TASK_NAME',Aliases=('DeprecatedName'),WarnWhenUsingAlias)]

Deprecating Tasks

To deprecate a task, set the Obsolete property on its Whiskey.Task attribute:

[Whiskey.Task('TASK_NAME',Obsolete)]

When someone uses your task, Whiskey will write a warning that the task is obsolete and shouldn't be used. You can customize the message shown to users with the ObsoleteMessage property:

[Whiskey.Task('TASK_NAME',Obsolete,ObsoleteMessage='The "TASK_NAME" task is obsolete. Please use the "NonObsoleteTask" instead.')]

Loading and Using Custom Tasks

To use your task in a whiskey.yml file, use the LoadTask task to load it, then its name from the Whiskey.Task attribute in your whiskey.yml file, e.g.

Build:
- LoadTask:
    Path: path\to\task\file.ps1
- TASK_NAME:
    Property: Value
    Property2:
    - One
    - Two

Tools

Automatically Installing Required Tools

Whiskey can automatically install some tools for your task. The path to that tool is passed to your task as a parameter.

To tell Whiskey what tools you depend on, add a Whiskey.RequiresTool attribute. It has two parameters: the name of the tool, and the name of the parameter to which the path to that tool should be passed to your task.

For example, given this task:

function FUNCTION_NAME
{
    [Whiskey.Task('TASK_NAME')]
    [Whiskey.RequiresTool('Node','NodePath')]
    [CmdletBinding()]
    param(
        [string]$NodePath
    )

    & $NodePath --version
}

Whiskey will install the latest version of Node.js, and pass the path Node executable to the NodePath parameter.

Pinning to a Specific Tool Version

We recommend that you always pin your task to a specific version. Use the Whiskey.RequiresTool attribute's Version property:

[Whiskey.RequiresTool('NodeModule::nsp', 'NspPath', Version='2.7.0')]

Wildcards are supported (except for NuGet packages due to a bug in nuget.org), so you can pin to major and minor versions of a tool.

We recommend allowing users to control what version of a tool they want. Use the Whiskey.RequiresTool attribute's VersionParameterName property. Set this to the name of a parameter the user can specify in their whiskey.yml file to control what version to use. The default name is Version. For example, if your Whiskey.RequiresTool attribute looks like this:

[Whiskey.RequiresTool('PowerShell::Zip', 'ZipPath', Version='0.3.*', VersionParameterName='ZipVersion')]

A user could use the ZipVersion property to control the version of the Zip module to use.

Build:
- TASK_NAME:
    ZipVersion: "0.*"

Supported Tools

Whiskey supports installing:

Node.js

To install Node, add this to your task:

[Whiskey.RequiresTool('Node','NodePath')]

The NodePath string is the name of your task's parameter to which Whiskey should pass the path to the Node.js executable when running your task.

If the user doesn't use the Version parameter in their whiskey.yml file to specify what version of Node to use, Whiskey also looks in the user's package.json file at the engines.node property, e.g.

{
    "engines": {
        "node": "^8.9.4"
    }
}

Node is installed to a .node directory in the build root. This gives every project its own version of Node to use.

If you or the user doesn't specify a version of Node.js to use, Whiskey uses the latest LTS version.

Node Modules

To install a Node module, pass the name of the module (case-sensitive) to the Whiskey.RequiresTool attribute, prefixed with NodeModule::. For example,

[Whiskey.RequiresTool('NodeModule::nsp', 'NspPath')]

would cause Whiskey to install the NSP module and passthe path to its directory to the NspPath parameter of your task. The path will always be to the Node module's directory in the node_modules directory.

Node modules are installed in the global packages directory of the instance of Node.js you're using.

.NET Core SDK

To install the .NET Core SDK, add this to your task:

[Whiskey.RequiresTool('DotNet','DotNetPath')]

Replace the DotNetPath string with the name of the parameter to which Whiskey should pass the path to the dotnet executable to your task.

If the SDK version to install is not defined in the whiskey.yml file, then Whiskey will look for a .NET Core global.json file first in the task's working directory and then in the same directory as the whiskey.yml file. The global.json file defines the SDK version with the sdk.version property, e.g.

{
    "sdk": {
        "version": "2.1.4"
    }
}

If no SDK version is found in the whiskey.yml or global.json, Whiskey will use the latest LTS release of the .NET Core SDK.

Before installing the .NET Core SDK to the build root, Whiskey will first search for global .NET Core installs containing the desired version. If a global install is found with the correct version, that path is used and the install to the local build root is skipped.

Whiskey will always update the sdk.version property within the global.json file found in the task working directory or the Whiskey build root with the version of the SDK that task is using. If a global.json file does not exist in either the working directory or build root, one is created in the build root.

PowerShell Modules

To use a PowerShell module, add this to your task:

[Whiskey.RequiresPowerShellModule('Whiskey','WhiskeyPath')]

Whiskey will install and import the module for you.

Replace the WhiskeyPath string with the name of the parameter to which Whiskey should pass the path to the directory where PowerShell's Save-Module cmdlet saved the module (e.g. if it was saved to PSModules\MODULE_NAME\1.2.3, the path will be set to PSModules\MODULE_NAME).

Modules are saved to a PSModules directory in the working directory of the task that requires it. The exact directory is an implementation detail, and is subject to change. Use the path as passed to your task.

NuGet Packages

Support for NuGet packages was added in Whiskey 0.42.0.

To install a NuGet package, pass the name of the NuGet package to the Whiskey.RequiresTool attribute, prefixed with NuGet::. For example, to install NUnit.Console, your attribute would look like this:

[Whiskey.RequiresTool('NuGet::NUnit.Console', 'ConsolePath',)]

The second parameter is the name of the parameter to which the path to the NuGet package should be passed. In the above example, the task should have a ConsolePath parameter. The path to the tool is the root of the package. The layout of NuGet packages can vary, so your task is responsible for finding the right files inside that package.

Packages are installed into a packages directory in the build root.

A note on versioning: there is bug in nuget.org that prevents all versions of a package from being returned. When requesting all versions, only the latest is returned. If you pin to a version of a NuGet package that contains wildcards, and the user's download packages from nuget.org, they'll always get the latest version. So, until this bug if fixed, we recommend pinning the version of the package to a specific version, and exposing a parameter that allows users to use a newer version.

Whiskey API/Helper Functions

Whiskey also exposes many of its internal functions for you to use in your tasks. Documentation for these functions are available in PowerShell. Import Whiskey and run Get-Command -Module Whiskey to see the list. Run help COMMAND_NAME for detailed information.

Clone this wiki locally