linci configuration

How to write build files

Writing build files is a case of creating a text file in /var/linci/config with a specific name file extension. If you are in the linci shell create build myproj will create a file /var/linci/config/ based on a template, and open it in vim, or whatever editor you have defined in $EDITOR.

This is what a linci build file looks like

linci> edit myproj

description: Example build
init: ssl:vbox:1424 //depot/myproj/... 
clean: rm -rf *


test: test/
deb: sudo deploy/
install: sudo deploy/
publish: sudo -E /var/linci/config/lib/ myproj-*.x86_64.deb 

Lines before targets:: must be executed individually e.g.

linci> build myproj clean

All lines after targets:: are executed in order when you call

linci> build myproj
N.B. The above build provides incremental builds. build myproj init is called once, build myproj is triggered by commits. To guarantee repeatable builds, move clean: and init: below targets::

There is no difference between build files created manually, and when created with create build ....


Linci contains vim syntax highlighting rules.

File names

The file extension should be .build for build files and .job for job files, other extensions are ignored.

Build file names are restricted to letters from a to z, numbers 0 to 9 and the dash character as per sanename. You are discouraged from using uppercase letters; the regexp of file names is as follows. ^[a-zA-Z][a-zA-Z0-9-]*$.

The file name defines the build name, if changed, build history is disassociated.


Lines starting with a hash # and blank lines are ignored.


Each target line should be a target name and a colon followed by a command. Target names should be all lowercase letters from a-z (^[a-z]+$).
Warning: Target name syntax restrictions are not validated at time of writing, but may be in a future version.
Whitespace is significant, it is not permitted before the target name or between the target name and the colon, after the colon any whitespace is sent unadjusted to bash, bash ignores leading whitespace.

test:   test/

Commands and escaping

You are strongly encouraged to ensure commands simply define a program and its arguments.
For example...

clean: rm -rf *

Multiple line commands are not supported. Technically pipes and redirects and all single line bash features work in the current version. Linci copies each command to a temporary shell script to run it. There is no modification of the command, when the temporary script is created. Future versions might not use bash, limiting commands to a program and its arguments will ensure compatability.
e.g. the following is not recommended...

publish: do-pub --ip $(hostname -i) | tee extra.log

Passing arguments to targets

To running the same build with different parameters use environment variables. e.g.

linci> FOO=bar build myproj


Build files should be text files. Line endings should be \n, use system encoding, i.e. utf-8.

Current directory

Every target is executed with the workspace as the current directory, ala Make. This ensures targets executed individually behave the same way, when run with other targets.

What goes where

When writing build files its important to make the distinction between what you can do, and what you should do. Linci build files allow you to run any commands; this gives you enough rope to hang yourself.

Linci's responsibilities are as follows.

Check out the code

Checking out the code has to be linci's responsibility. Scripts to perform checkout update should be stored in /usr/share/linci/bin/vcs. bzr,git, p4 and svn are provided, vcs scripts can be as simple as

svn co $1 .

It would have been possible to store the rest of the build configuration alongside the source code, other CI tools go this route (e.g. Jenkinsfile). Linci takes the stance that scripts and configuration related to linci's responsibilities are best stored outside the source. This facilitates different builds for the same source, and enables linci to function without modifications to source code.
Linci specific build scripts should be added to /var/linci/config/lib, which is on the $PATH.

Compilation processes

Scripts that define how to compile and package the code should almost always be in the source tree. A Makefile, maven poms, ant scripts etc. is standard practice. Some CI tools have lanugage specific features and plugins, build instructions end up in the CI tool. Linci considers this an anti-pattern. Many Open Source licenses require build scripts to be delivered with the code.

Development processes

If a target defines a task that is also executed manually by developers, scripts defining how to perform these tasks should be in the source repository. e.g. scripts that define how to run unit tests. Linci targets should always be a one-liner. Its possible to write in a config file...

pre-test: scripts/
do-test: scripts/
This is encapsulates how to run tests in linci. The two steps should be combined in the source code.

Publishing / Reporting processes

Presuming that you never publish code straight from your laptop and you follow a CI flow, it may make sense that scripts to handle publishing should be in linci itself if the process is more than a one-liner. A standard process across projects makes sense to keep in /var/linci/config/lib. /var/linci/config/lib should be versioned and backed up along with build files. A reporting script that is only relevant in the CI server, e.g. a script that monitors builds and calculates developer of the month, should be in /var/linci/config/lib. Avoid linci specific code in the source tree.

Deployment processes

Its not so obvious where to store deployment scripts. As a CI only concern: scripts could live in /var/linci/config/lib. Often deployment process version must match the code being deployed: deployment scripts alongside code can be convenient, Open Source code must be delivered with installation scripts. To support matching versions and to iterate on the deployment scripts a separate source repository is sometimes necessary.

Reporting success ro failure

Since CI builds often run without human interaction it is important to inform commiters when there are failures or integrate other systems e.g. raising issues for failures.

Hopefully, after reading this, you understand why we did not implement linci with Make.