NAME

holo-build - cross-distribution system package compiler

SYNOPSIS

holo-build [option...] file

holo-build --help|--version

DESCRIPTION

Holo adds a few sprinkles on top of package management to make it suitable for basic configuration management tasks. Its prime directive is to have all configuration statically declared and defined within packages, which can be installed to add new configuration, or uninstalled to remove configuration from a system.

However, the tools for creating system packages are optimized towards compiling applications for tarballs, and introduce needless complexity when you just want to package up a few files and list some dependencies. holo-build provides a simple, distribution-independent package description language and generates a system package from such a description.

For example, the following package description will build a package that installs and configures systemd-timesyncd with a custom NTP server:

[package]
name     = "hologram-systemd-timesyncd"
version  = "1.0"
author   = "Jane Doe <jane.doe@example.org>"
requires = ["systemd"]

[[file]]
path     = "/etc/systemd/timesyncd.conf.d/server.conf"
content  = """
    [Time]
    NTP=ntp.someserver.local
"""

[[symlink]]
# as created by `systemctl enable systemd-timesyncd`
path     = "/etc/systemd/system/sysinit.target.wants/systemd-timesyncd.service"
target   = "/usr/lib/systemd/system/systemd-timesyncd.service"

[[action]]
on     = "setup"
script = "systemctl daemon-reload && systemctl start systemd-timesyncd"

[[action]]
on     = "cleanup"
script = "systemctl stop systemd-timesyncd"

The package description format is described below.

OPTIONS

The only positional argument file is the file name from where the package definition will be read. If no such argument is given, the package definition is read from standard input instead.

--output filename

Write the resulting package to filename. If not given, write the package into the working directory using the naming convention for the corresponding output package format. If filename refers to a directory, write the package into this directory using the naming convention for the corresponding output package format. If the filename is -, write the package to standard output.

--force/--no-force

By default, holo-build will fail if the target file already exists. This behavior protects against the user forgetting to increase the version when editing the package description. With --force, the target file will be overwritten when it exists.

This switch has no effect when --output - or --suggest-filename is in effect.

--format format

Generate a package of the specified format, instead of the default package format for the current distribution. Valid values are debian, pacman and rpm.

WARNING: RPM generation is considered experimental because RPM is underdocumented and a bizarrely baroque format to begin with.

--suggest-filename

Do not generate a package. After reading and validating the package definition, just print on standard output the suggested filename for this package. The printed file name is the same one that will be used when --no-stdout is in effect. The format for package filenames follows the recommendations of the targeted distribution:

$ cat input.toml

[package]
name = "foo"
version = "1.0"

$ holo-build --suggest-filename --format=debian < input.toml
foo_1.0-1_any.deb
$ holo-build --suggest-filename --format=pacman < input.toml
foo-1.0-1-any.pkg.tar.xz

This option can be used when auto-generating Makefiles, where the output filename needs to be known before holo-build runs (for purposes of dependency resolution).

--help

Print out usage information.

--version

Print out holo-build's version string.

Deprecated options

These switches will be removed in the next major version.

--debian

Use --format debian instead.

--pacman

Use --format pacman instead.

--reproducible

No effect. All packages are now built reproducibly.

--rpm

Use --format rpm instead.

--stdout

Use --output - instead.

PACKAGE DESCRIPTION FORMAT

Package descriptions are written in the TOML format.

Only the [package] section is required. All other sections (and all fields not marked as required) are optional.

[package] section

This section is required and contains global properties for the package.

[package]
name    = "example-package"
version = "1.2.5"
epoch   = 2
release = 3
description = "An example package"
author      = "Jane Doe <jane.doe@example.org>"
requires    = [ "other-package >= 2.0" ]
provides    = [ "example-package-api = 1.2" ]
conflicts   = [ "bloatware-package" ]
replaces    = [ "sample-package" ]
name (string, required)

The package name. This string may never contain any slashes or newlines. The output format may impose additional charset or size restrictions on the package name. As a rule of thumb, the pattern [a-z0-9-]+ is a safe set of allowed package names.

For --format=debian, the package name may contain [a-z0-9+-.], but the first character must be alphanumeric.

For --format=pacman, the package name may contain [a-z0-9@._+-], but the first character may not be a hyphen.

version (string, required)

The package version. To ensure sanity, holo-build enforces a relatively strict pattern. Version numbers must be numbers (0|[1-9][0-9]*) chained together by dots:

version = "1.2.3"
version = "0.10"
version = "20151015"
alpha, beta (unsigned integer)

If one of these is non-zero, the package is a prerelease version. For instance, the following corresponds to the full version 1.2.0-beta.3:

version = "1.2.0"
beta = 3

The exact rendition of the version string depends on the output format.

epoch (unsigned integer)

An increase in the epoch (default: 0) can be used to force the package to be seen as newer than any previous version with a lower epoch. This is used when the version numbering scheme for a package changes, breaking normal version comparison logic.

release (unsigned integer)

The release number (default: 1) can be appended if the same package is rebuilt multiple times without its contents changing, in order for the built packages to be distinguishable from one another.

description (string)

A description of the purpose and contents of this package.

author (string, required for --format=debian)

The name and mail address of the package author, in the form Name <address>:

[package]
author = "Jane Doe <jane.doe@example.org>"
architecture (string)

The target architecture of the package. The default (any) is fine unless the package contains compiled binaries. Valid values include:

all any noarch
i386 i686
x86_64 amd64
arm armel armv5tl
armv6h armv6hl
armhf armv7h armv7hl
arm64 aarch64

The values on each line are synonymous to each other. The big variety of architecture strings comes from the big variety of supported target distributions, who all define architecture strings in their own way. holo-build will map the input architecture string to the one that's appropriate for the given target distribution.

requires (array of strings)

A list of other packages that must be installed when this package is installed. Some package managers call these dependencies instead. When a specific package version is required, a version test can be appended, using one of the operators =, >, >=, < or <=. If multiple version tests are required, the same package can be stated multiple times.

[package]
# require any version of foo, and a 2.x version of bar
requires = [ "foo", "bar >= 2.0", "bar < 3.0" ]

When the package contains any files below /usr/share/holo/$PLUGIN_ID, a requirement

requires = [ "holo-$PLUGIN_ID" ]

is implied automatically.

For --format=pacman only, a special syntax is allowed to require complete package groups (by giving the groupname with a group: prefix), and to exclude certain packages or package groups from this group requirement (by prefixing the dependency with except:). For example, to have the package require all packages from the xorg group, except for the xorg-drivers group and the xorg-docs package:

[package]
requires = [
    # require all packages from the xorg group
    "group:xorg",
    # except for xorg-docs and all packages in the xorg-drivers group
    "except:xorg-docs",
    "except:group:xorg-drivers",
]
provides (array of strings)

A list of other packages (or virtual packages) that the software provides the features of. This means that this package can satisfy another package's requirement for the provided package. If a specific version is provided, this can be specified with the same syntax as for requires.

[package]
name     = "rewrite-of-foo"
provides = [ "foo = 2.1" ] # this package acts like foo-2.1

For --format=debian, the provided version syntax is not allowed. provides may only contain plain package names.

conflicts (array of strings)

A list of other packages that may not be installed when this package is installed. Version tests can be added using the same syntax as for requires.

For --format=pacman, the same special syntax is allowed as for requires; see there for details.

replaces (array of strings)

A list of obsolete packages that this package replaces. If this package is not installed, but one of the obsolete packages is installed, a system upgrade will result in the obsolete package being uninstalled, and this package being installed as a replacement.

If this package can replace the obsolete package, but it shall not be replaced automatically, don't use this; reference the obsolete package in both provides and conflicts instead.

For --format=pacman, the same special syntax is allowed as for requires; see there for details.

setupScript (string, deprecated)

A shell script that will be executed (as root) when the package is installed or updated, after the package files have been extracted to the file system.

If the package contains any files below /usr/share/holo, then holo apply will automatically run before this setup script is executed, so you can rely on provisioned files, user accounts and groups to be available already.

DEPRECATED: This key will be removed in the next major release. Use [[action]] sections instead.

# deprecated
[package]
setupScript = "echo Hello World"

# write instead
[[action]]
on     = "setup"
script = "echo Hello World"
cleanupScript (string, deprecated)

A shell script that will be executed (as root) when the package is being uninstalled, after the package files have been removed from the file system.

If the package contains any files below /usr/share/holo, then holo apply will automatically run before this cleanup script is executed.

DEPRECATED: This key will be removed in the next major release. Use [[action]] sections instead.

# deprecated
[package]
cleanupScript = "echo Hello World"

# write instead
[[action]]
on     = "cleanup"
script = "echo Hello World"
definitionFile (string, deprecated)

A path where [[user]] and [[group]] sections will be placed inside the package. See the description of [[user]] and [[group]] sections below. When omitted, the default value is /usr/share/holo/users-groups/${package_name}.toml.

DEPRECATED: This key can be specified for compatibility reasons, but its use is discouraged, and it will be removed in the next major release.

[[file]] section

Each one of these sections define a file to be added to the package.

[[file]]
path    = "/etc/foo.conf"
mode    = "0600"
owner   = "foouser"
group   = "foogroup"
# alternative 1
content = """
    content for foo.conf
    content for foo.conf
"""
# alternative 2
contentFrom = "input.txt"
path (string, required)

The path to this file. The path must be absolute and may not have a trailing slash.

content/contentFrom (string, exactly one required)

If the content field is given, it contains the content of this file. Alternatively, contentFrom may reference a file whose contents will be used. This file must be present at package-build time; relative paths will be interpreted relative to the directory of the input file (or, if the package definition is presented on standard input, to the current working directory of the holo-build process).

If content is given, it may not be empty. To create an empty file, you can use /dev/null as a source:

[[file]]
path        = "/etc/empty-file.conf"
contentFrom = "/dev/null"
raw (boolean)

To aid readability, the content field allows strings to have indentation which will automatically be pruned. The following two sections are equivalent:

[[file]]
path    = "/etc/example.conf"
content = """
foo
    bar
baz
"""

[[file]]
path    = "/etc/example.conf"
content = """
    foo
        bar
    baz
"""

To disable this behavior and use leading whitespace verbatim, set the raw flag:

[[file]]
path    = "/etc/example.conf"
raw     = true
content = """
    foo
        bar
    baz
"""
mode (string)

The mode bits for this file. Since TOML does not support octal number literals, this field must be given as a string containing an octal number:

mode = "0600" # rw-------
mode = "0755" # rwxr-xr-x
owner/group (string or int)

The owner (or group) for this file. If this field contains an integer, it is interpreted as the ID of the user or group. If this field contains a string, it is interpreted as a user/group name.

Since user/group names cannot be mapped to IDs at package build time, specifying a name will result in the file being packaged as belonging to user/group root, and the actual user/group will be applied at install time using chown(1) or chgrp(1).

BUG: For --format=rpm, specification of numeric IDs is broken. When encountering a numeric user/group ID in a holo-build package, RPM will say things like

user 42 does not exist - using root
group 42 does not exist - using root

and fall back to UID/GID 0. As a workaround, call chown(1) or chgrp(1) from the package's setup script to fix the file ownership.

[[directory]] section

Each one of these sections define a directory to be added to the package.

[[directory]]
path = "/var/lib/foo"
mode = "0600"
owner = "foouser"
group = "foogroup"

Note that directories are usually created automatically when files are placed in them. A [[directory]] section is only required to include an empty directory in the package, or to assign non-standard permissions or specific ownership to a directory.

path (string, required)

The path to this directory. The path must be absolute and may not have a trailing slash.

mode/owner/group

These are the same as for [[file]] sections; see above.

[[symlink]] section

Each one of these sections define a symlink to be added to the package.

[[symlink]]
path   = "/etc/foo.conf"
target = "bar.conf"
path (string, required)

The path to this directory. The path must be absolute and may not have a trailing slash.

target

The symlink target. Both relative and absolute targets are acceptable.

[[action]] section

Each one of these sections define an action that can be executed by the package manager. For example:

[[action]]
on     = "setup"
script = "echo Just Installed"
on (string, required)

This field defines when this action will be executed. The following values are valid:

on = "setup"   # run right after package is installed or upgraded
on = "cleanup" # run right after package is removed

If there are multiple actions with the same on value, they will be executed in the order in which they are given in the package description.

script (string, required)

This field contains a shell script that will be run (as root) when the action is executed.

[[user]] and [[group]] sections

These can be used to provision user accounts and groups when the package is installed. For example:

[package]
name    = "foobar"
version = "1.0"

[[group]]
name   = "foobargroup"
system = true

[[user]]
name   = "foobaruser"
uid    = 285
group  = "foobargroup"

The user and group definitions are validated at package compilation time, and written into /usr/share/holo/users-groups/${package_name}.toml. Because of this file, a dependency on holo-users-groups is implied and holo apply is executed in the setup and cleanup scripts. So the previous example is functionally equivalent to:

[package]
name          = "foobar"
version       = "1.0"
depends       = ["holo-users-groups"]

[[file]]
path    = "/usr/share/holo/users-groups/foobar.toml"
content = """
    [[group]]
    name   = "foobargroup"
    system = true

    [[user]]
    name   = "foobaruser"
    uid    = 285
    group  = "foobargroup"
"""

[[action]]
on     = "setup"
script = "holo apply"

[[action]]
on     = "cleanup"
script = "holo apply"

The actual syntax and semantics of [[user]] and [[group]] sections is described in holo-users-groups(8).

SEE ALSO

holo(8)

AUTHOR

Stefan Majewsky

Further documentation is available at the project homepage: https://holocm.org

Please report any issues and feature requests at GitHub: https://github.com/holocm/holo-build/issues