12 KiB
AWS Build Accumulator
Litani Command-line reference
AWS Build Accumulator is a suite of tools that collect build jobs from multiple sources before executing them
concurrently.
Users access AWS Build Accumulator through litani
, the command-line
interface.
This document serves as a reference for using litani
and integrating
it into your project.
litani
allows you to use multiple build systems in the same project,
providing a backend that each build system emits jobs to.
Once all jobs have been enqueued, litani
executes them all as a single
unified build graph.
litani
also provides platform-independent job control (timeouts and
control of return codes), as well as grouping of job artifacts into
stages.
Overview
Consider the following Makefile:
foo.out: foo.in
timeout 90 my_command $^ > $@; if [ $? -eq 10 ]; then exit 0; fi
foo.out
is built from foo.in
using my_command
. We want to kill
my_command
if it runs for more than 90 seconds, and we're also
expecting that my_command
may exit with a return code of 10; we don't
consider that to be an error, so we exit the subshell with 0 in that
case.
The timeout and error escaping in this command are unportable. We can
replace it with an invocation to litani
as follows:
foo.out: foo.in
litani add-job \
--command "my_command $^" \
--inputs $^ \
--outputs $@ \
--timeout 90 \
--ok-returns 10 \
--pipeline-name my_command \
--ci-stage build \
--stdout-file $@
To actually run this, write the following shell script:
#!/bin/sh
litani init --project my_project
make foo.out
litani run-build
Running make
doesn't actually run the job; rather, it runs litani
,
which saves the job for later. You can run litani add-job
as many
times as you like after running litani init
; all these jobs are cached
and turned into a dependency graph using the arguments to --inputs
and
--outputs
. Running litani run-build
runs all cached build jobs together,
in parallel if possible.
litani
continuously updates a run.json
file while the run-build
command
is running, showing progress of each job as well as recording the return
codes, timeout information, and stdout/stderr of each job. This file is
documented below and is designed to be easy to render into a dashboard,
for example in HTML.
Motivation
While the platform-independent job-control features are a nice bonus,
litani
's real value is in serving as a backend for executing a graph of
build tasks that are added from heterogeneous sources. In a complex
software project, different parts of the project can use incompatible
build systems. To build the entire project, one must either run all
build systems in parallel—potentially overcommitting on
concurrency and introducing nondeterminism when some of the targets
overlap—or run each build system serially, wasting time.
litani
makes it possible for jobs that are specified in different build
systems to depend on each other. It also obviates the need to force
different parts of the project to use a unified build syntax, if that is
unnatural for some reason. If some parts of the build tree are specified
in Make, with others specified in CMake, then litani
allows developers
working on each part of the codebase to use the build system that makes
sense to them, while litani
builds the entire tree in the background.
Subtool Reference
litani
consists of three user-facing commands:
litani init
—create a new runlitani add-job
—add a job to the runlitani run-build
—run all jobs in the run in dependency order
You use litani
by invoking all three of these commands. You firstly
initialize a run; add at least one job to the run; and then build the
run, which invokes all the jobs in dependency order.
litani
writes all data associated with the run to a single directory;
the directory for each run is unique. litani
's data format is versioned
with semantic versioning. litani --version
prints the data format version and exits. The current version is 1.0.0.
litani init
litani [-v] init [-h] --project-name NAME
Create a new run for jobs to be added to. litani
output from subsequent
commands, as well as job artifacts and output files, will be written to
a directory associated with this run.
`-v, --verbose`
Verbose output. In particular, print the name of the directory to which all output files and artifacts for this run will be written to.
`--project-name NAME`
Associate this run to a "project". A project is a collection of runs. The name of the project will be included in the JSON output for the run.
litani add-job
litani [-v] add-job --command CMD
[--inputs F [F ...]]
[--outputs F [F ...]]
--pipeline-name P --ci-stage S
[-h] [--cwd DIR] [--timeout N] [--timeout-ok]
[--timeout-ignore] [--ignore-returns RC [RC ...]]
[--ok-returns RC [RC ...]]
[--interleave-stdout-stderr]
[--stdout-file FILE] [--stderr-file FILE]
[--description DESC] [--tags TAG [TAG ...]]
Describe one of the jobs to be run when litani run-build
is invoked. A
job is a command that depends on inputs emits outputs. The command is
invoked when the jobs that emit each of this job's inputs have
successfully executed. Inputs and outputs can be files, but they don't
have to be; litani
doesn't actually check whether the files were
written, just whether the job completed successfully.
describing the build graph:
`--command CMD`
The command to run once all dependencies are satisfied. `CMD` is parsed as a single string and invoked using a subshell.
`--inputs F [F ...]`
List of inputs that this job depends on.
`--outputs F [F ...]`
List of outputs that this job generates.
job control:
`--pipeline-name P`
Which pipeline this job is a member of.
`--ci-stage `
Which CI stage this job is a member of.
`--cwd DIR`
Directory that the command should execute in.
`--timeout N`
How long the job should be allowed to run for. If the timeout is reached, the command will be terminated and the job will exit unsuccessfully, blocking any dependant jobs from running, unless `--timeout-ok` or `--timeout-ignore` are passed.
`--timeout-ok`
If the command times out, it will be terminated, but the job will be considered successful. The pipeline that this job is part of will also be considered successful.
`--timeout-ignore`
If the command times out, it will be terminated, but this will not block dependent jobs from running. Nevertheless, the pipeline that this job is part of will be considered to have failed. This option is useful when you wish to run dependant jobs even after this job has timed out, but still want to indicate an overall failure.
`--ignore-returns RC [RC ...]`
If the exits with one of the specified return codes, it will not block dependent jobs from running. Nevertheless, the pipeline that this job is part of will be considered to have failed. This option is useful when you wish to run dependant jobs even after the command exited with an abnormal status, but still want to indicate an overall failure. For example, you may wish to generate a report as a dependant job; the report must still be generated if this job fails.
`--ok-returns RC [RC ...]`
If the command exits with one of the specified return codes, the job will still be considered successful.
`--interleave-stdout-stderr`
Simulate `2>&1 >...`
`--stdout-file FILE`
Write the command's stdout to `FILE`. Note that even if this option is not passed, `litani` will not print the command's stdout to the terminal, but will save any output to the JSON file for the run.
`--stderr-file FILE`
Write the command's stderr to `FILE`. Note that even if this option is not passed, `litani` will not print the command's stderr to the terminal, but will save any output to the JSON file for the run.
misc:
`--description DESC`
A human-readable string to describe the job. This will be printed to terminal when the job is run, and can also be used in reports. `litani` does not interpret the description, but will include it in the JSON report for the run.
`--tags TAG [TAG ...]`
A list of tags for this job. `litani` does not interpret tags, but will include them all in the JSON report for the run.
litani run-build
litani run-build [-h] [-n] [-j N] [-o F]
[--pipelines P [P ...] |
--ci-stage <build|test|report>]
Create a new run for jobs to be added to. litani
output from subsequent
commands, as well as job artifacts and output files, will be written to
a directory associated with this run.
`-j N, --parallel N`
Run at most `N` jobs in parallel. 0 means infinity, the default is based on the number of cores on your system.
`-n, --dry-run`
Don't actually run the commands in each job, just pretend that they succeeded.
`-o F, --out-file F`
Periodically write a JSON file describing the run so far to `F`. `litani` already writes such a file to the run directory created by `litani init`; this flag specifies an additional, more easily-accessible file to write to.
`-p P [P ...], --pipelines P [P ...]`
Only run the jobs that are associated with the pipeline `P`. This allows you to run a subset of the jobs from start to finish.
`-s , --ci-stage `
Only run the jobs that are part of the CI stage `S`. This allows you to run all jobs up to and including a particular stage.