Forums/Go Community & Support/Knowledge Base

Go's custom command

Sriram Narayan
posted this on January 06, 2013 20:50

Update: March 2013

Custom command is now enhanced to lookup from a command repository.

Public github repo of commands (contributions accepted)

And you thought Go didn't support Maven, Nuget or Chef?

Use Go's new command repository to lookup your config scripts (advanced)

One task per job

==========================================================

Go’s custom command may be used to run any command-line tool available on the Go Agent. Typically authored through the custom command dialog, it looks like this when saved in Go’s config file.

<exec command="echo">

  <arg>hello</arg>

</exec>

The agent simply execs this at runtime. Note that this is a plain exec, not a shell exec, so the following won’t have the desired effect (it will simply print “$HOME”):

<exec command="echo">

  <arg>$HOME</arg>

</exec>

There are two ways to make this work. One is to put the command into a script file, say myecho.sh:

#!/bin/bash

echo $HOME

or myecho.bat:

echo %HOME%

and then write the Go custom command task as:

<exec command="myecho.sh">

</exec>

or

<exec command="myecho.bat">

</exec>

The other option is to provide an explicit shell context to the custom command like this:

<exec command="/bin/bash">

  <arg>-c</arg>

  <arg>echo $HOME</arg>

</exec>

or

<exec command="cmd">

  <arg>/c</arg>

  <arg>echo %HOME%</arg>

</exec>

See bash -c and cmd /c for more information about how they work.

 

In the above examples, HOME is assumed to defined on the agent (via .bash_profile etc). But the above examples are also valid for environment variables defined via Go (built-in or otherwise). So, for example:

<exec command="/bin/bash">

  <arg>-c</arg>

  <arg>echo $GO_PIPELINE_LABEL</arg>

</exec>

 

Finally, let’s consider  cases where quoting is needed. Suppose you need to send an email from the command-line. You might do something like this:

 

<exec command="/bin/bash">

  <arg>-c</arg>

  <arg>echo hello there | mail -s "subject line" blah@gmail.com</arg>

</exec>

 

Without the double quotes, the subject part of that mail could not have more than one word. Single-quotes could replace double-quotes here. Most times, you’ll need single-quotes when you need to nest quotes.

 

Say you need to send that email from a remote box (maybe only it has SMTP settings set properly). You could do something like this:

 

<exec command="/bin/bash">

  <arg>-c</arg>

  <arg>ssh remotehost 'echo hello there | mail -s "subject line" blah@gmail.com'</arg>

</exec>

 

You need the single quotes here so that the whole “echo … | mail …” part is sent to the remote host.

Windows tips

Go considers the task as successful if the custom command exits with zero, else the task (and it's job) are failed. Some windows commands don't follow this convention. Given robocopy's behaviour, the following task fails even if robocopy doesn't.

<exec command="robocopy">

  <arg>src-folder</arg>

  <arg>\\remote-node\target-folder</arg>

  <arg>*.exe</arg>

</exec>

To make it work with Go, we re-write it as:

<exec command="cmd">

  <arg>/c</arg>

  <arg>robocopy src-folder \\remote-node\target-folder *.exe & rundll32</arg>

</exec>

rundll32 without any arguments is effectively a no-op. This sets the overall errorlevel to zero. Of course, this means the task won't fail even if there is a genuine robocopy failure so use with care.

Powershell 

In case of Powershell script-blocks, the exit code is non-zero if any of the cmdlets in the script block writes to the error-stream. Writing status to error stream is common practice for utils like curl and git. To handle this in powershell V2, we manually clear the $error variable in the following command that executes a git pull remotely using powershell and winrm. V3 claims to have -ErrorAction:Ignore option for this.

<exec command="cmd">
  <arg>/c</arg>
  <arg>powershell $pass = convertto-securestring %password% -asplaintext -force; $mycred = new-object -typename   System.Management.Automation.PSCredential -argumentlist "DOMAIN\%userid%",$pass; invoke-command -computername %remote-computer% -ScriptBlock {cd c:\path\to\git-repo;git pull;$error.clear()} -credential $mycred</arg>
</exec>

Prefer version controlled scripts over fully inline commands

The above powershell hack is a limited workaround to make it work as a fully inline custom command. Fully inline commands are useful to quickly iterate and get the command working. It is better to move long complicated commands into version controlled script files (make them part of the material for the pipeline) and simply run the script from inside a Go custom command.

<exec command="powershell">

  <arg>remote-git-pull.ps1</arg>

</exec>

Thanks to Arvind and Sachin for their inputs to this article.

 

Related articles:

http://support.thoughtworks.com/entries/22029053-representing-an-equals-symbol-as-an-argument

http://support.thoughtworks.com/entries/21525298-passing-environment-variables-to-a-job

http://shirishpadalkar.blogspot.in/2013/01/custom-shell-commands-with-wildcard.html