Caution
This page documents the latest, unreleased version of Buildbot. For documentation for released versions, see http://docs.buildbot.net/current/.
2.5.12.19. ShellCommand
Most interesting steps involve executing a process of some sort on the worker.
The ShellCommand
class handles this activity.
Several subclasses of ShellCommand
are provided as starting points for common build steps.
Using ShellCommands
- class buildbot.steps.shell.ShellCommand
This is a useful base class for just about everything you might want to do during a build (except for the initial source checkout).
It runs a single command in a child shell on the worker.
All stdout/stderr is recorded into a LogFile
.
The step usually finishes with a status of FAILURE
if the command’s exit code is non-zero, otherwise it has a status of SUCCESS
.
The preferred way to specify the command is with a list of argv strings, since this allows for spaces in filenames and avoids doing any fragile shell-escaping.
You can also specify the command with a single string, in which case the string is given to /bin/sh -c COMMAND
for parsing.
On Windows, commands are run via cmd.exe /c
which works well.
However, if you’re running a batch file, the error level does not get propagated correctly unless you add ‘call’ before your batch file’s name: cmd=['call', 'myfile.bat', ...]
.
ShellCommand
includes all sub-processes created by the command in JobObject
. This ensures
that all child processes are managed together with the parent process. When the main command is
terminated, all sub-processes are also terminated automatically, preventing any orphaned processes.
This enhancement aligns the behavior of Windows systems with POSIX systems, where similar process
management has been in place.
The ShellCommand
arguments are:
command
A list of strings (preferred) or single string (discouraged) which specifies the command to be run. A list of strings is preferred because it can be used directly as an argv array. Using a single string (with embedded spaces) requires the worker to pass the string to /bin/sh for interpretation, which raises all sorts of difficult questions about how to escape or interpret shell metacharacters.
If
command
contains nested lists (for example, from a properties substitution), then that list will be flattened before it is executed.workdir
All
ShellCommand
s are run by default in theworkdir
, which defaults to thebuild
subdirectory of the worker builder’s base directory. The absolute path of the workdir will thus be the worker’s basedir (set as an option tobuildbot-worker create-worker
, Creating a worker), plus the builder’s basedir (set in the builder’sbuilddir
key inmaster.cfg
), plus the workdir itself (a class-level attribute of the BuildFactory, defaults tobuild
).For example:
from buildbot.plugins import steps f.addStep(steps.ShellCommand(command=["make", "test"], workdir="build/tests"))
env
A dictionary of environment strings which will be added to the child command’s environment. For example, to run tests with a different i18n language setting, you might use:
from buildbot.plugins import steps f.addStep(steps.ShellCommand(command=["make", "test"], env={'LANG': 'fr_FR'}))
These variable settings will override any existing ones in the worker’s environment or the environment specified in the
Builder
. The exception isPYTHONPATH
, which is merged with (actually prepended to) any existingPYTHONPATH
setting. The following example will prepend/home/buildbot/lib/python
to any existingPYTHONPATH
:from buildbot.plugins import steps f.addStep(steps.ShellCommand( command=["make", "test"], env={'PYTHONPATH': "/home/buildbot/lib/python"}))
To avoid the need of concatenating paths together in the master config file, if the value is a list, it will be joined together using the right platform dependent separator.
Those variables support expansion so that if you just want to prepend
/home/buildbot/bin
to thePATH
environment variable, you can do it by putting the value${PATH}
at the end of the value like in the example below. Variables that don’t exist on the worker will be replaced by""
.from buildbot.plugins import steps f.addStep(steps.ShellCommand( command=["make", "test"], env={'PATH': ["/home/buildbot/bin", "${PATH}"]}))
Note that environment values must be strings (or lists that are turned into strings). In particular, numeric properties such as
buildnumber
must be substituted using Interpolate.want_stdout
If
False
, stdout from the child process is discarded rather than being sent to the buildmaster for inclusion in the step’sLogFile
.want_stderr
Like
want_stdout
but forstderr
. Note that commands that run through a PTY do not have separatestdout
/stderr
streams, and both are merged intostdout
.usePTY
If
True
, this command will be run in apty
(defaults toFalse
). This option is not available on Windows.In general, you do not want to use a pseudo-terminal. This is only useful for running commands that require a terminal - for example, testing a command-line application that will only accept passwords read from a terminal. Using a pseudo-terminal brings lots of compatibility problems, and prevents Buildbot from distinguishing the standard error (red) and standard output (black) streams.
In previous versions, the advantage of using a pseudo-terminal was that
grandchild
processes were more likely to be cleaned up if the build was interrupted or it timed out. This occurred because using a pseudo-terminal incidentally puts the command into its own process group.As of Buildbot-0.8.4, all commands are placed in process groups, and thus grandchild processes will be cleaned up properly.
logfiles
Sometimes commands will log interesting data to a local file, rather than emitting everything to stdout or stderr. For example, Twisted’s trial command (which runs unit tests) only presents summary information to stdout, and puts the rest into a file named
_trial_temp/test.log
. It is often useful to watch these files as the command runs, rather than using /bin/cat to dump their contents afterwards.The
logfiles=
argument allows you to collect data from these secondary logfiles in near-real-time, as the step is running. It accepts a dictionary which maps from a local Log name (which is how the log data is presented in the build results) to either a remote filename (interpreted relative to the build’s working directory), or a dictionary of options. Each named file will be polled on a regular basis (every couple of seconds) as the build runs, and any new text will be sent over to the buildmaster.If you provide a dictionary of options instead of a string, you must specify the
filename
key. You can optionally provide afollow
key which is a boolean controlling whether a logfile is followed or concatenated in its entirety. Following is appropriate for logfiles to which the build step will append, where the pre-existing contents are not interesting. The default value forfollow
isFalse
, which gives the same behavior as just providing a string filename.from buildbot.plugins import steps f.addStep(steps.ShellCommand( command=["make", "test"], logfiles={"triallog": "_trial_temp/test.log"}))
The above example will add a log named ‘triallog’ on the master, based on
_trial_temp/test.log
on the worker.from buildbot.plugins import steps f.addStep(steps.ShellCommand(command=["make", "test"], logfiles={ "triallog": { "filename": "_trial_temp/test.log", "follow": True } }))
lazylogfiles
If set to
True
, logfiles will be tracked lazily, meaning that they will only be added when and if something is written to them. This can be used to suppress the display of empty or missing log files. The default isFalse
.timeout
If the command fails to produce any output for this many seconds, it is assumed to be locked up and will be killed. This defaults to 1200 seconds. Pass
None
to disable.maxTime
If the command takes longer than this many seconds, it will be killed. This is disabled by default.
max_lines
If the command outputs more lines than this maximum lines, it will be killed. This is disabled by default.
logEnviron
If
True
(the default), then the step’s logfile will describe the environment variables on the worker. In situations where the environment is not relevant and is long, it may be easier to set it toFalse
.interruptSignal
This is the signal (specified by name) that should be sent to the process when the command needs to be interrupted (either by the buildmaster, a timeout, etc.). By default, this is “KILL” (9). Specify “TERM” (15) to give the process a chance to cleanup. This functionality requires a version 0.8.6 worker or newer.
sigtermTime
If set, when interrupting, try to kill the command with SIGTERM and wait for sigtermTime seconds before firing
interuptSignal
. If None,interruptSignal
will be fired immediately upon interrupt.initialStdin
If the command expects input on stdin, the input can be supplied as a string with this parameter. This value should not be excessively large, as it is handled as a single string throughout Buildbot – for example, do not pass the contents of a tarball with this parameter.
decodeRC
This is a dictionary that decodes exit codes into results value. For example,
{0:SUCCESS,1:FAILURE,2:WARNINGS}
will treat the exit code2
asWARNINGS
. The default ({0:SUCCESS}
) is to treat just 0 as successful. Any exit code not present in the dictionary will be treated asFAILURE
.