holo-build(8)
NAME β¨
holo-build β cross-distribution system package compiler π
(yep, this is the holo-build(8) page from the Configuration Management section, bestie)
SYNOPSIS π¦
holo-build [option...] file
holo-build --help|--version
DESCRIPTION πΈ
Okay so picture this: Holo adds a few little sprinkles on top of package management so it can handle basic configuration management tasks. Its whole prime directive is that all configuration should be statically declared and defined inside packages β install a package to add new configuration, uninstall it to take that configuration away. Clean. Iconic. Very main character energy.
But the usual tools for making system packages are optimized for compiling applications out of tarballs, and they bring in a bunch of needless complexity when all you want is to package up a few files and list some dependencies. That's where holo-build comes in: it gives you a simple, distribution-independent package description language, and it generates a system package from that description. Periodt.
For example, this 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, so keep reading bestie π
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. Easy.
--output filenameβ Write the resulting package tofilename. If you don't give it, write the package into the working directory using the naming convention for the corresponding output package format. Iffilenamerefers to a directory, write the package into this directory using the naming convention for the corresponding output package format. If thefilenameis-, write the package to standard output. Slay.--force/--no-forceβ By default,holo-buildwill 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-filenameis in effect. No cap.--format formatβ Generate a package of the specified format, instead of the default package format for the current distribution. Valid values aredebian,pacmanandrpm.
WARNING: RPM generation is considered experimental because RPM is underdocumented and a bizarrely baroque format to begin with. You have been warned, bestie π
--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-stdoutis 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). Very handy.
--helpβ Print out usage information.--versionβ Print outholo-build's version string.
Deprecated options π«
These switches will be removed in the next major version. Use the shiny replacements, okay?
--debianβ Use--format debianinstead.--pacmanβ Use--format pacmaninstead.--reproducibleβ No effect. All packages are now built reproducibly.--rpmβ Use--format rpminstead.--stdoutβ Use--output -instead.
PACKAGE DESCRIPTION FORMAT π¦β¨
Package descriptions are written in TOML format. Yes bestie, TOML.
Only the [package] section is required. All other sections (and all fields not marked as required) are optional. Chill.
"[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 β like, do not go there β then don't use this. Instead, reference the obsolete package in both provides and conflicts. Two birds, one iconic move. β¨
For --format=pacman, the same special syntax is allowed as for requires; go peek at that part for the deets. π
π«Ά setupScript (string, deprecated)
A shell script that gets executed (as root) when the package is installed or updated, right 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 100% rely on provisioned files, user accounts and groups being available already. Bestie, they'll be there. π
DEPRECATED: This key will be removed in the next major release. Use [[action]] sections instead. No cap, it's on its way out.
# 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. Tidy. πΈ
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. Like, it still works, but it's giving legacy energy. π
π "[[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. Periodt. π«
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). So keep your paths straight, bestie. π§
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 β literally the same output, iykyk:
[[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 β quoting is not optional, bestie:
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). That's the trade, that's the deal.
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 likeuser 42 does not exist - using root group 42 does not exist - using rootand fall back to UID/GID 0. As a workaround, callchown(1)orchgrp(1)from the package's setup script to fix the file ownership. Ugly but it eats. π
π "[[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. So don't be out here making sections for no reason. π
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. Same vibes, same rules. β¨
π¦ "[[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. (Yep, the page says directory here β we're just reporting the lore verbatim, bestie. π)
target
The symlink target. Both relative and absolute targets are acceptable. Go wherever, it's valid. π
π "[[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. Order matters, bestie β first come, first served. β
script (string, required)
This field contains a shell script that will be run (as root) when the action is executed. Root, so, like, be normal about it.
π "[[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). Go read that one too, it's a whole thing. π
β¨ SEE ALSO β¨
holo(8)
π«Ά AUTHOR π«Ά
Stefan Majewsky β icon, legend, the moment. π
Further documentation is available at the project homepage: http://holocm.org
Please report any issues and feature requests at Github: http://github.com/holocm/holo/issues
and that's the whole page, bestie!! π This little masterpiece is holo-build 1.6.1, dated 1980-01-01 (yes, the date is a whole vibe, don't question it π). Go build something iconic. πβ¨π¦
π¦ where this page comes from
- nixos 26.05: holo-build 1.6.1
- nixpkgs unstable: holo-build 1.6.1
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms