+ Overview +
+See Luet in action.
+diff --git a/404.html b/404.html new file mode 100644 index 00000000..641e10be --- /dev/null +++ b/404.html @@ -0,0 +1,244 @@ + + +
+ + + + + + + + + + + + + + + + + +Oops! This page doesn't exist. Try going back to our home page.
+ +You can learn how to make a 404 page like this in Custom 404 Pages.
+Luet uses Container technologies ( Docker, img ) to build packages. +It provides an abstraction over the Dockerfile format introducing relation and versioning of images.
+ + + +This release comes with a lot of bugfixes and enhancement to the SAT solver core:
+provides
. They allow to have virtual
packages which can be replaced during solving by other drop-in packages.Monday, December 23, 2019 in News
+Finally the website is up! Docs are a work in progress. Stay tuned for more upcoming updates +
+ +Monday, December 23, 2019 in Releases
+This release comes with a lot of bugfixes and enhancement to the SAT solver core: + Add support for provides. They allow to have virtual packages which can be replaced during solving by other drop-in packages. Tons of fixes Preparation for upcoming …
+ +Monday, December 23, 2019 in News
+Finally the website is up! Docs are a work in progress. Stay tuned for more upcoming updates +
+ +Monday, December 23, 2019 in Releases
+This release comes with a lot of bugfixes and enhancement to the SAT solver core: + Add support for provides. They allow to have virtual packages which can be replaced during solving by other drop-in packages. Tons of fixes Preparation for upcoming …
+ +Luet is an open source project that anyone in the community can use, improve, and enjoy. We'd love you to join us! Here's a few ways to find out what's happening and get involved. +
+Using or want to use Luet? Find out more here: + +
If you want to get more involved by contributing to Luet, join us here: + + +
+ + +You can find out how to contribute to these docs in our Contribution Guidelines +
See Luet in action.
+Package definition syntax
+Extend luet with plugins and extensions
+Was this page helpful?
+ + ++ Glad to hear it! Please tell us how we can improve. +
++ Sorry to hear that. Please tell us how we can improve. +
+Luet currently supports Docker and Img as backends to build packages. Both of them can be used and switched in runtime with the --backend
option, so either one of them must be present in the host system.
Docker is the (less) experimental Luet engine supported. Be sure to have Docker installed and the daemon running. The user running luet
commands needs the corresponding permissions to run the docker
executable, and to connect to a docker
daemon. The only feature needed by the daemon is the ability to build images, so it fully supports remote daemon as well (this can be specified with the DOCKER_HOST
environment variable, that is respected by luet
)
Luet supports Img. To use it, simply install it in your system, and while running luet build
, you can switch the backend by providing it as a parameter: luet build --backend img
. For small packages it is particularly powerful, as it doesn’t require any docker daemon running in the host.
Luet and img can be used together to orchestrate package builds also on kubernetes. There is available an experimental Kubernetes CRD for Luet which allows to build packages seamelessly in Kubernetes and push package artifacts to an S3 Compatible object storage (e.g. Minio).
+Luet provides an abstraction layer on top of the container image layer to make the package a first class construct. A package definition and all its dependencies are translated by Luet to Dockerfiles which can then be built anywhere that docker runs.
+To resolve the dependency tree Luet uses a SAT solver and no database. It is responsible for calculating the dependencies of a package and to prevent conflicts. The Luet core is still young, but it has a comprehensive test suite that we use to validate any future changes.
+Building a package with Luet requires only a definition. This definition can be self-contained and be only composed of one specfile, or a group of them, forming a Luet tree. For more complex use-cases, see collections.
+Run luet build --help
to get more help for each parameter.
Build accepts a list of packages to build, which syntax is in the category/name-version
notation. See also specfile documentation page to see how to express packages from the CLI.
Luet builds passes its environment variable at the engine which is called during build, so for example the environment variable DOCKER_HOST
or DOCKER_BUILDKIT
can be setted.
Every argument from the CLI can be setted via environment variable too with a LUET_
prefix, for instance the flag --clean
, can be setted via environment with LUET_CLEAN
, --privileged
can be enabled with LUET_PRIVILEGED
and so on.
At the moment, luet
can compress packages and tree with zstd
and gzip
. For example:
luet build --compression zstd ...
+
Will output package compressed in the zstd format.
+See the --help
of create-repo
and build
to learn all the available options.
A package definition is composed of a build.yaml
and a sibiling definition.yaml
.
In the following example, we are creating a dummy package (bar/foo
). Which ships one file only, /foo
$> # put yourself in some workdir
+
+$~/workdir> mkdir package
+
+$~/workdir> cat <<EOF > package/build.yaml
+image: busybox
+steps:
+- echo "foo=bar" > /foo
+EOF
+
+$~/workdir> cat <<EOF > package/definition.yaml
+name: "foo"
+version: "0.1"
+category: "bar"
+EOF
+
+
To build it, simply run luet build bar/foo
or luet build --all
to build all the packages in the current directory:
$> luet build --all
+
+📦 Selecting foo 0.1
+📦 Compiling foo version 0.1 .... ☕
+🐋 Downloading image luet/cache-foo-bar-0.1-builder
+🐋 Downloading image luet/cache-foo-bar-0.1
+📦 foo Generating 🐋 definition for builder image from busybox
+🐋 Building image luet/cache-foo-bar-0.1-builder
+🐋 Building image luet/cache-foo-bar-0.1-builder done
+ Sending build context to Docker daemon 4.096kB
+ ...
+
+
Luet “trees” are just a group of specfiles, in the above example, our tree was the current directory. You can also specify a directory with the --tree
option. Luet doesn’t enforce any tree layout, so they can be nested at any level. The only rule of thumb is that a build.yaml
file needs to have either a definition.yaml
or a collection.yaml
file next to it.
In the example above we have created a package from a delta
. Luet by default creates packages by analyzing the differences between the generated containers, and extracts the differences as archive, the resulting files then are compressed and can be consumed later on by luet install
.
Luet can create packages from different building strategies: by delta, by taking a whole container content, or by considering a single directory in the build container.
+Besides that, a package can reference a strict dependency on others.
+Let’s extend the above example with two packages which depends on it during the build phase.
+
+$~/workdir> mkdir package2
+
+$~/workdir> cat <<EOF > package2/build.yaml
+requires:
+- name: "foo"
+ category: "bar"
+ version: ">=0"
+
+steps:
+- source /foo && echo "$foo" > /bar
+EOF
+
+$~/workdir> cat <<EOF > package2/definition.yaml
+name: "ineedfoo"
+version: "0.1"
+category: "bar"
+EOF
+
+
+$~/workdir> mkdir package3
+
+$~/workdir> cat <<EOF > package3/build.yaml
+requires:
+- name: "foo"
+ category: "bar"
+ version: ">=0"
+- name: "ineedfoo"
+ category: "bar"
+ version: ">=0"
+
+steps:
+- source /foo && echo "$foo" > /ineedboth
+- cat /bar > /bar
+
+EOF
+
+$~/workdir> cat <<EOF > package3/definition.yaml
+name: "ineedfooandbar"
+version: "0.1"
+category: "bar"
+EOF
+
+
To build, run again:
+$> luet build --all
+
As we can see, now Luet generated 3 packages, bar/foo
, bar/ineedfoo
and bar/ineedfooandbar
. They aren’t doing anything special than just shipping text files, this is an illustrative example on how build requirements can be combined to form new packages:
bar/ineedfooandbar
depends on both bar/ineedfoo
and bar/foo
during build-time, while bar/foo
uses a docker image as a build base.
See the package definition documentation page for more details on how to instruct the Luet compiler to build packages with different strategies.
+Luet can push and pull the docker images that are being generated during the build process. A tree is represented by a single docker image, and each package can have one or more tags attached to it.
+To push automatically docker images that are built, use the --push
option, to pull, use the --pull
option. An image repository can be specified with --image-repository
flag, and can include also the remote registries where the images are pushed to.
Luet doesn’t handle login to registries, so that has to be handled separately with docker login
or img login
before the build process starts.
When packages are cached, for iterating locally it’s particularly useful to jump straight to the image that you want to build. You can use --only-target-package
to jump directly to the image you are interested in. Luet will take care of checking if the images are present in the remote registry, and would build them if any of those are missing.
build.yaml
are copied in the container which is running your build, so they are always accessible during build time.TMPDIR
env variable to a different folder. By default luet respects the O.S. default (which in the majority of system is /tmp
).Was this page helpful?
+ + ++ Glad to hear it! Please tell us how we can improve. +
++ Sorry to hear that. Please tell us how we can improve. +
+Under the hood, Luet uses boolean satisfiability problem (SAT) reinforcement learning techniques to solve package constraints.
+Luet allows you to specify 3 types of set of contraints on a package definition:
+The package definition in your tree definition, along with its Requires and Conflicts, are turned into Boolean formulas that are consumed by the solver to compute a solution. The solution represent the state of your system after a particular query is asked to the solver (Install, Uninstall, Upgrade).
+A list of requires and conflicts, composed of one or more packages, becomes a SAT formula. The formula is then given to the SAT solver to compute a finite state set of packages which must be installed in the system in order to met the requirements.
+As Luet allows to express constraints with selectors ( e.g. A depends on >=B-1.0
) it generates additional constraints to guarantee that at least one package and at most one is picked as dependency (ALO and AMO).
Provides constraints are not encoded in a SAT formula. Instead, they are expanded
into an in-place substitution of the packages that they have to be replaced with.
+They share the same SAT logic of expansion, allowing to swap entire version ranges (e.g. >=1.0
), allowing to handle package rename, removals, and virtuals.
zypper
): https://en.opensuse.org/openSUSE:Libzypp_satsolver_basicsWas this page helpful?
+ + ++ Glad to hear it! Please tell us how we can improve. +
++ Sorry to hear that. Please tell us how we can improve. +
+Luet provides an abstraction layer on top of the container image layer to make the package a first class construct. A package definition and all its dependencies are translated by Luet to Dockerfiles which can then be built anywhere that docker runs.
+Luet is written entirely in Go and comes as a single static binary. This has a few advantages:
+Luet brings the containers ecosystem to standard software package management and delivery. It is fully built around the container concept, and leverages the huge catalog already present in the wild. It lets you use Docker images from Docker Hub, or from private registries to build packages, and helps you to redistribute them.
+Systems that are using luet as a package manager can consume Luet repositories with only luet itself. No dependency is required by the Package manager, giving you the full control on what you install or not in the system. It can be used to generate Linux from Scratch distributions, also to build Docker images, or to simply build standalone packages that you might want to redistribute.
+The syntax proposed aims to be KISS - you define a set of steps that need to be run to build your image, and a set of constraints denoting the requirements or conflicts of your package.
+There is no known package manager with 0-dependency that fully leverages the container ecosystem. This gap forces current package managers to depend on a specific system layout as base of the building process and the corresponding depencies. This can cause situations leading to a broken system. We want to fix that by empowering the user, by building their own packages, and redistribute them. +Luet allows also to create packages entirely from Docker images content. In this way the user can actually bundle all the files of an image into a package and deliver part of it, or entirely as a layer. All of that, without the package manager depending on a single bit from it.
+Luet uses YAML for the package specification format, Luet parses the requirements to build packages, so Luet can consume them.
+Below you can find links to tutorials on how to build packages, images and repositories.
+ +How to build packages with Luet
+How to create Luet repositories
+How to install packages, manage repositories, …
+How Luet turns Image resolution into CSP
+Was this page helpful?
+ + ++ Glad to hear it! Please tell us how we can improve. +
++ Sorry to hear that. Please tell us how we can improve. +
+After a set of packages has been built, a repository must be created in order to make them accessible by Luet clients. A Repository can be served either local files or via http(s) (at the moment of writing). Luet, by default, supports multiple-repositories with priorities.
+After issuing a luet build
, the built packages are present in the output build directory. The create-repo
step is needed to generate a portable tree, which is read by the clients, and a repository.yaml
which contains the repository metadata.
Note that the output of create-repo
is additive so it integrates with the current build content. The repository is composed by the packages generated by the build
command (or pack
) and the create-repo
generated metadata.
Some of the relevant flags for create-repo
are:
See luet create-repo --help
for a full description.
Build a package and generate the repository metadata:
+$> mkdir package
+
+$> cat <<EOF > package/build.yaml
+image: busybox
+steps:
+- echo "foo" > /foo
+EOF
+
+$> cat <<EOF > package/definition.yaml
+name: "foo"
+version: "0.1"
+category: "bar" # optional!
+EOF
+
+$> luet build --all --destination $PWD/out/ --tree $PWD/package
+
+📦 Selecting foo 0.1
+📦 Compiling foo version 0.1 .... ☕
+🐋 Downloading image luet/cache-foo-bar-0.1-builder
+🐋 Downloading image luet/cache-foo-bar-0.1
+📦 foo Generating 🐋 definition for builder image from busybox
+🐋 Building image luet/cache-foo-bar-0.1-builder
+🐋 Building image luet/cache-foo-bar-0.1-builder done
+ Sending build context to Docker daemon 4.096kB
+ ...
+
+$> luet create-repo --name "test" --output $PWD/out --packages $PWD/out --tree $PWD/package
+ For repository test creating revision 1 and last update 1580641614...
+
+$> ls out
+foo-bar-0.1-builder.image.tar foo-bar-0.1.image.tar foo-bar-0.1.metadata.yaml foo-bar-0.1.package.tar repository.yaml tree.tar
+
+
There are 3 types of repositories supported by luet: disk
, http
, docker
.
disk
It is a repository which is merely a local folder in your system. When creating a repository and specifying --output
, luet
expects a local path to the system where to store the generated metadata.
http
It is a repository type which is hosted behind a webserver. When creating a repository and specifying --output
, luet
expects a local path to the system where to store the generated metadata, similarly to the disk
repository type. Luet is not handling any file upload. The http
repository type gains meaning when being used from the client, where the repository source must be specified
docker
When specifying the docker
repository type, luet
will generate final images from the build results and upload them to the docker reference specified with --output
. The images contains the artifact output from the build result, and they are tagged accordingly to their package name. A single image reference needs to be passed, all the packages will be pushed in a single image but with different tags.
The login to the container registry is not handled, the daemon needs to have already proper permissions to push the image to the destination.
+metadata.yaml
file is required.Was this page helpful?
+ + ++ Glad to hear it! Please tell us how we can improve. +
++ Sorry to hear that. Please tell us how we can improve. +
+To install a package with luet
, simply run:
+$ luet install <package_name>
+
+
To relax dependency constraints and avoid auto-upgrades, add the --relax
flag:
$ luet install --relax <package name>
+
To install only the package without considering the deps, add the --nodeps
flag:
$ luet install --nodeps <package name>
+
To install only package dependencies, add the --onlydeps
flag:
$ luet install --onlydeps <package name>
+
To only download packages, without installing them use the --download-only
flag:
$ luet install --download-only <package name>
+
To uninstall a package with luet
, simply run:
+$ luet uninstall <package_name>
+
+
To upgrade your system, simply run:
+$ luet upgrade
+
Luet automatically syncs repositories definition on the machine when necessary, but it avoids to sync up in a 24h range. In order to refresh the repositories manually, run:
+$ luet repo update
+
To search a package:
+
+$ luet search <regex>
+
+
To search a package and display results in a table:
+
+$ luet search --table <regex>
+
+
To look into the installed packages:
+
+$ luet search --installed <regex>
+
+
Note: the regex argument is optional
+$ luet search --file <file_pattern>
+
Search can return results in the terminal in different ways: as terminal output, as json or as yaml.
+
+$ luet search --json <regex>
+
+
+$ luet search --yaml <regex>
+
+
+$ luet search --table <regex>
+
+
Luet output is verbose by default and colourful, however will try to adapt to the terminal, based on which environment is executed (as a service, in the terminal, etc.)
+You can quiet luet
output with the --quiet
flag or -q
to have a more compact output in all the commands.
Was this page helpful?
+ + ++ Glad to hear it! Please tell us how we can improve. +
++ Sorry to hear that. Please tell us how we can improve. +
+Collections
are a special superset of packages. To define a collection, instead of using a definition.yaml
file, create a collection.yaml
file with a list of packages:
packages:
+- category: "test"
+ name: "foo"
+ version: "1.0"
+- category: "test"
+ name: "bar"
+ version: "1.0"
+
Packages under a collection shares the same build.yaml
and finalize.yaml
, so a typical package layout can be:
collection/
+ collection.yaml
+ build.yaml
+ finalize.yaml
+ ... additional files in the build context
+
Luet during the build phase, will treat packages of a collection individually. A collection is a way to share the same build process across different packages.
+The templating mechanism can be used in collections as well, and each stanza in packages
is used to interpolate each single package.
Was this page helpful?
+ + ++ Glad to hear it! Please tell us how we can improve. +
++ Sorry to hear that. Please tell us how we can improve. +
+A Package in Luet is denoted by a triple (name
, category
and version
), here called package form in a definition.yaml
file in YAML:
name: "awesome"
+version: "0.1"
+category: "foo"
+
While category
and version
can be omitted, the name is required. Note that when refering to a package, the triplet is always present:
requires:
+- name: "awesome"
+ version: "0.1"
+ category: "foo"
+- name: "bar"
+ version: "0.1"
+ category: "foo"
+
When a package is required to be built, Luet resolves the dependency trees and orders the spec files to satisfy the given contraints.
+Each package build context is where the spec files are found (definition.yaml
and build.yaml
). This means that in the container, which is running the build process, the resources inside the package folder are accessible, as normally in Docker.
❯ tree distro/raspbian/buster
+distro/raspbian/buster
+├── build.sh
+├── build.yaml
+├── definition.yaml
+└── finalize.yaml
+
In the example above, build.sh
is accessible in build time and can be invoked easily in build time in build.yaml
:
steps:
+- sh build.sh
+
Packages can specify a list of provides
. This is a list of packages in package form, which indicates that the current definition replaces every occurrence of the packages in the list (both at build and runtime). This mechanism is particularly helpful for handling package moves or for enabling virtual packages (e.g., gentoo virtual packages).
Note: packages in the provides
list don’t need to exist or have a valid build definition either.
By a combination of keywords in build.yaml
, you end up with categories of packages that can be built:
Check the Specfile concept page for a full overview of the available keywords in the Luet specfile format.
+Seed packages denote a parent package (or root) that can be used by other packages as a dependency. Normally, seed packages include just an image (preferably tagged) used as a base for other packages to depend on.
+It is useful to pin to specific image versions, and to write down in a tree where packages are coming from. There can be as many seed packages as you like in a tree.
+A seed package build.yaml
example is the following:
image: "alpine:3.1"
+
Every other package that depends on it will inherit the layers from it.
+If you want to extract the content of the seed package in a separate packages (splitting), you can just create as many package as you wish depending on that one, and extract its content, for example:
+alpine/build.yaml
+image: "alpine:3.1"
+
alpine/definition.yaml
+name: "alpine"
+version: "3.1"
+category: "seed"
+
sh/build.yaml
+# List of build-time dependencies
+requires:
+- name: "alpine"
+ version: "3.1"
+ category: "seed"
+unpack: true # Tells luet to use the image content by unpacking it
+includes:
+- /bin/sh
+
sh/definition.yaml
+name: "sh"
+category: "utils"
+version: "1.0"
+
In this example, there are two packages being specified:
+seed
package, which is the base image employed to later extract packages. It has no installable content, and it is just virtually used during build phase.sh
is the package which contains /bin/sh
, extracted from the seed image and packaged. This can be consumed by Luet clients in order to install sh
in their system.Luet, by default, will try to calculate the delta of the package that is meant to be built. This means that it tracks incrementally the changes in the packages, to ease the build definition. Let’s see an example.
+Given the root package: +alpine/build.yaml
+image: "alpine:3.1"
+
alpine/definition.yaml
+name: "alpine"
+version: "3.1"
+category: "seed"
+
We can generate any file, and include it in our package by defining this simple package:
+foo/build.yaml
+# List of build-time dependencies
+requires:
+- name: "alpine"
+ version: "3.1"
+ category: "seed"
+steps:
+- echo "Awesome" > /foo
+
foo/definition.yaml
+name: "foo"
+category: "utils"
+version: "1.0"
+
By analyzing the difference between the two packages, Luet will automatically track and package /foo
as part of the foo
package.
To allow operations that must not be accounted in to the final package, you can use the prelude
keyword:
foo/build.yaml
+# List of build-time dependencies
+requires:
+- name: "alpine"
+ version: "3.1"
+ category: "seed"
+prelude:
+- echo "Not packaged" > /invisible
+steps:
+- echo "Awesome" > /foo
+
foo/definition.yaml
+name: "foo"
+category: "utils"
+version: "1.0"
+
The list of commands inside prelude
that would produce artifacts, are not accounted to the final package. In this example, only /foo
would be packaged (which output is equivalent to the example above).
This can be used, for instance, to fetch sources that must not be part of the package.
+You can apply restrictions anytime and use the includes
keyword to specifically pin to the files you wish in your package.
Luet can be used to track entire layers and make them installable by Luet clients.
+Given the examples above:
+alpine/build.yaml
+image: "alpine:3.1"
+
alpine/definition.yaml
+name: "alpine"
+version: "3.1"
+category: "seed"
+
An installable package derived by the seed, with the actual full content of the layer can be composed as follows:
+foo/build.yaml
+# List of build-time dependencies
+requires:
+- name: "alpine"
+ version: "3.1"
+ category: "seed"
+unpack: true # It advertize Luet to consume the package as is
+
foo/definition.yaml
+name: "foo"
+category: "utils"
+version: "1.0"
+
This can be combined with other keywords to manipulate the resulting package (layer), for example:
+foo/build.yaml
+# List of build-time dependencies
+requires:
+- name: "alpine"
+ version: "3.1"
+ category: "seed"
+unpack: true # It advertize Luet to consume the package as is
+steps:
+- apk update
+- apk add git
+- apk add ..
+
foo/definition.yaml
+name: "foo"
+category: "utils"
+version: "1.0"
+
In addition, the includes
keyword can be set in order to extract portions from the package image.
git/build.yaml
+# List of build-time dependencies
+requires:
+- name: "alpine"
+ version: "3.1"
+ category: "seed"
+unpack: true # It advertize Luet to consume the package as is
+steps:
+- apk update
+- apk add git
+includes:
+- /usr/bin/git
+
foo/definition.yaml
+name: "git"
+category: "utils"
+version: "1.0"
+
As a reminder, the includes
keywords accepts regular expressions in the Golang format. Any criteria expressed by means of Golang regular expressions, and matching the file name (absolute path), will be part of the final package.
Luet specfile syntax
+Use templates to fine tune build specs
+Group a set of package build spec with templating
+Was this page helpful?
+ + ++ Glad to hear it! Please tell us how we can improve. +
++ Sorry to hear that. Please tell us how we can improve. +
+Luet packages are defined by specfiles. Specfiles define the runtime and builtime requirements of a package. There is an hard distinction between runtime and buildtime. A spec is composed at least by the runtime (definition.yaml
or a collection.yaml
) and the buildtime specification (build.yaml
).
Luet identifies the package definition by looking at directories that contains a build.yaml
and a definition.yaml
(or collection.yaml
) files. A Luet tree is merely a composition of directories that follows this convention. There is no constriction on either folder naming or hierarchy.
Example of a tree folder hierarchy
+tree distro
+distro
+├── funtoo
+│ ├── 1.4
+│ │ ├── build.sh
+│ │ ├── build.yaml
+│ │ ├── definition.yaml
+│ │ └── finalize.yaml
+│ ├── docker
+│ │ ├── build.yaml
+│ │ ├── definition.yaml
+│ │ └── finalize.yaml
+│ └── meta
+│ └── rpi
+│ └── 0.1
+│ ├── build.yaml
+│ └── definition.yaml
+├── packages
+│ ├── container-diff
+│ │ └── 0.15.0
+│ │ ├── build.yaml
+│ │ └── definition.yaml
+│ └── luet
+│ ├── build.yaml
+│ └── definition.yaml
+├── raspbian
+│ ├── buster
+│ │ ├── build.sh
+│ │ ├── build.yaml
+│ │ ├── definition.yaml
+│ │ └── finalize.yaml
+│ ├── buster-boot
+│ │ ├── build.sh
+│ │ ├── build.yaml
+│ │ ├── definition.yaml
+│ │ └── finalize.yaml
+
Build specs are defined in build.yaml
files. They denote the build-time dependencies
and conflicts
, together with a definition of the content of the package.
Example of a build.yaml
file:
steps:
+- echo "Luet is awesome" > /awesome
+prelude:
+- echo "nooops!"
+requires:
+- name: "echo"
+ version: ">=1.0"
+conflicts:
+- name: "foo"
+ version: ">=1.0"
+provides:
+- name: "bar"
+ version: ">=1.0"
+env:
+- FOO=bar
+includes:
+- /awesome
+
+unpack: true
+
Luet can create packages with different strategies:
+prelude
section to exclude certains file during analysis.By default Luet will analyze the container content and extract any file that gets added to it. The difference is calculated by using the container which is depending on, or either by the container which is created by running the steps in the prelude
section of the package build spec:
prelude:
+- # do something...
+steps:
+- # real work that should be calculated delta over
+
By omitting the prelude
keyword, the delta will be calculated from the parent container where the build will start from.
Luet can also generate a package content from a container. This is really useful when creating packages that are entire versioned rootfs
. To enable this behavior, simply add unpack: true
to the build.yaml
. This enables the Luet unpacking features, which will extract all the files contained in the container which is built from the prelude
and steps
fields.
To include/exclude single files from it, use the includes
and excludes
directives.
Similarly, you can tell Luet to create a package from a folder in the build container. To enable this behavior, simply add package_dir: "/path/to/final/dir"
.
+The directory must represent exactly how the files will be ultimately installed from clients, and they will show up in the same layout in the final archive.
So for example, to create a package which ships /usr/bin/mybin
, we could write:
package_dir: "/output"
+steps:
+- mkdir -p /output/usr/bin/
+- echo "fancy stuff" > /output/usr/bin/mybin && chmod +x /output/usr/bin/mybin
+
A package build spec defines how a package is built. In order to do this, Luet needs to know where to start. Hence a package must declare at least either one of the following:
+image
keyword which tells which Docker image to use as base, orrequires
, which are references to other packages available in the tree.They can’t be both present in the same specfile.
+To note, it’s not possible to mix package build definitions from different image
sources. They must form a unique sub-graph in the build dependency tree.
On the other hand it’s possible to have multiple packages depending on a combination of different requires
, given they are coming from the same image
parent.
Luet can also exclude and include single files or folders from a package by using the excludes
and includes
keyword respecitvely.
Both of them are parsed as a list of Golang regex expressions, and they can be combined together to fine-grainly decide which files should be inside the final artifact. You can refer to the files as they were in the resulting package. So if a package produces a /foo
file, and you want to exclude it, you can add it to excludes
as /foo
.
Luet needs an image to kick-off the build process for each package. This image is being used to run the commands in the steps
and prelude
, and then the image is processed by the building strategies explained above.
The image can be resolved either by:
+image
requires
which will be constructed a new image from. The resulting image is an image linked between each other with the FROM
field in the Dockerfile following the SAT solver ordering.requires
and by specifying requires_final_images: true
.requires
and requires
with requires_final_images: true
requires
generates a graph from all the images
of the specfile referenced inside the list. This means it builds a chain of images that are used to build the packages, e.g.: packageA(image: busybox) -> packageB (requires: A) -> packageC (requires: C)
. The container which is running your build then inherits it’s parents from a chain of order resolution, provided by the SAT solver.
When specifying requires_final_images: true
luet builds an artifact for each of the packages listed from their compilation specs and it will later squash them together in a new container image which is then used in the build process to create an artifact.
The key difference is about where your build is going to run from. By specifying requires_final_images
it will be constructed a new image with the content of each package - while if setting it to false, it will order the images appropriately and link them together with the Dockerfile FROM
field. That allows to reuse the same images used to build the packages in the require section - or - create a new one from the result of each package compilation.
Here is a list of the full keyword refereces for the build.yaml
file.
conflicts
(optional) List of packages which it conflicts with in build time. In the same form of requires
it is a list of packages that the current one is conflicting with.
conflicts:
+- name: "foo"
+ category: "bar"
+ version: "1.0"
+...
+- name: "baz"
+ category: "bar"
+ version: "1.0"
+
See Package concepts for more information on how to represent a package in a Luet tree.
+copy
since luet>=0.15.0
+(optional) A list of packages/images where to copy files from. It is the Docker multi-stage build equivalent but enhanced with tree hashing resolution.
+To copy a specific file from a package build container:
+steps:
+- ...
+prelude:
+- ...
+copy:
+- package:
+ category: "foo"
+ name: "bar"
+ version: ">=0"
+ source: "/foo"
+ destination: "/bar"
+
Any package that is listed in the section will be compiled beforeahead the package, and the file is available both in prelude
and steps
.
Internally, it’s rendered as COPY --from=package/image:sha /foo /bar
To copy a specific file from an external image:
+steps:
+- ...
+prelude:
+- ...
+copy:
+- image: "buxybox:latest"
+ source: "/foo"
+ destination: "/bar"
+
env
(optional) A list of environment variables ( in NAME=value
format ) that are expanded in step
and in prelude
. ( e.g. ${NAME}
).
env:
+- PATH=$PATH:/usr/local/go/bin
+- GOPATH=/luetbuild/go
+- GO111MODULE=on
+- CGO_ENABLED=0
+- LDFLAGS="-s -w"
+
excludes
(optional) List of golang regexes. They are in full path form (e.g. ^/usr/bin/foo
) and indicates that the files listed shouldn’t be part of the final artifact
Wildcards and golang regular expressions are supported. If specified, files which are not matching any of the regular expressions in the list will be excluded in the final package.
+excludes:
+- ^/etc/shadow
+- ^/etc/os-release
+- ^/etc/gshadow
+
By combining excludes
with includes
, it’s possible to include certain files while excluding explicitly some others (excludes
takes precedence over includes
).
image
(optional/required) Docker image to be used to build the package.
+image: "busybox"
+
It might be omitted in place of requires
, and indicates the image used to build the package. The image will be pulled and used to build the package.
includes
(optional) List of regular expressions to match files in the resulting package. The path is absolute as it would refer directly to the artifact content.
+Wildcards and golang regular expressions are supported. If specified, files which are not matching any of the regular expressions in the list will be excluded in the final package.
+includes:
+- /etc$
+- /etc/lvm$
+- /etc/lvm/.*
+- /usr$
+- /usr/bin$
+- /usr/bin/cc.*
+- /usr/bin/c\+\+.*
+- /usr/bin/cpp.*
+- /usr/bin/g\+\+.*
+
Note: Directories are treated as standard entries, so to include a single file, you need also to explictly include also it’s directory. Consider this example to include /etc/lvm/lvm.conf
:
includes:
+- /etc$
+- /etc/lvm$
+- /etc/lvm/lvm.conf
+
join
since luet>=0.16.0
+to be deprecated in luet>=0.18.0 in favor of requires_final_images
(optional/required) List of packages which are used to generate a parent image from.
+It might be omitted in place of image
or requires
, and will generate an image which will be used as source of the package from the final packages in the above list. The new image is used to run eventually the package building process and a new artifact can be generated out of it.
join:
+- name: "foo"
+ category: "bar"
+ version: "1.0"
+...
+- name: "baz"
+ category: "bar"
+ version: "1.0"
+
See Package concepts for more information on how to represent a package in a Luet tree.
+package_dir
(optional) A path relative to the build container where to create the package from.
+Similarly to unpack
, changes the building strategy.
steps:
+- mkdir -p /foo/bar/etc/myapp
+- touch /foo/bar/etc/myapp/config
+package_dir: /foo/bar
+
prelude
(optional) A list of commands to perform in the build container before building.
+prelude:
+- |
+ PACKAGE_VERSION=${PACKAGE_VERSION%\+*} && \
+ git clone https://github.com/mudler/yip && cd yip && git checkout "${PACKAGE_VERSION}" -b build
+
requires
(optional/required) List of packages which it depends on.
+A list of packages that the current package depends on in build time. It might be omitted in place of image
, and determines the resolution tree of the package itself. A new image is composed from the packages listed in this section in order to build the package
requires:
+- name: "foo"
+ category: "bar"
+ version: "1.0"
+...
+- name: "baz"
+ category: "bar"
+ version: "1.0"
+
See Package concepts for more information on how to represent a package in a Luet tree.
+requires_final_images
since luet>=0.17.0
+(optional) A boolean flag which instruct luet to use the final images in the requires
field.
By setting requires_final_images: true
in the compilation spec, packages in the requires
section will be first compiled, and afterwards the final packages are squashed together in a new image that will be used during build.
requires:
+- name: "foo"
+ category: "bar"
+ version: "1.0"
+...
+- name: "baz"
+ category: "bar"
+ version: "1.0"
+
+requires_final_images: true
+
requires_final_images
replaces the use of join
, which will be deprecated in luet >=0.18.0
.
step
(optional) List of commands to perform in the build container.
+steps:
+- |
+ cd yip && make build-small && mv yip /usr/bin/yip
+
unpack
(optional) Boolean flag. It indicates to use the unpacking strategy while building a package
+unpack: true
+
It indicates that the package content is the whole container content.
+Runtime specification are denoted in a definition.yaml
or a collection.yaml
sibiling file. It identifies the package and the runtime contraints attached to it.
definition.yaml:
+name: "awesome"
+version: "0.1"
+category: "foo"
+requires:
+- name: "echo"
+ version: ">=1.0"
+ category: "bar"
+conflicts:
+- name: "foo"
+ version: "1.0"
+provides:
+- name: "bar"
+ version: "<1.0"
+
A collection.yaml
can be used in place of a definition.yaml
to identify a set of packages that instead shares a common build.yaml
:
collection.yaml:
+packages:
+- name: "awesome"
+ version: "0.1"
+ category: "foo"
+ requires:
+ - name: "echo"
+ version: ">=1.0"
+ category: "bar"
+ conflicts:
+ - name: "foo"
+ version: "1.0"
+ provides:
+ - name: "bar"
+ version: "<1.0"
+- name: "awesome"
+ version: "0.2"
+ category: "foo"
+ requires:
+ - name: "echo"
+ version: ">=1.0"
+ category: "bar"
+ conflicts:
+ - name: "foo"
+ version: "1.0"
+ provides:
+ - name: "bar"
+ version: "<1.0"
+...
+
All the fields (also the ones which are not part of the spec) in the definition.yaml
file are available as templating values when rendering the build.yaml
file. When running finalizers instead only the fields belonging to the specs are available.
Here is a list of the full keyword refereces
+annotations
(optional) A map of freeform package annotations:
+annotations:
+ foo: "bar"
+ baz: "test"
+
category
(optional) A string containing the category of the package
+category: "system"
+
conflicts
(optional) List of packages which it conflicts with in runtime. In the same form of requires
it is a list of packages that the current one is conflicting with.
conflicts:
+- name: "foo"
+ category: "bar"
+ version: "1.0"
+...
+- name: "baz"
+ category: "bar"
+ version: "1.0"
+
See Package concepts for more information on how to represent a package in a Luet tree.
+description
(optional) A string indicating the package description
+name: "foo"
+description: "foo is capable of..."
+
hidden
(optional) A boolean indicating whether the package has to be shown or not in the search results (luet search...
)
hidden: true
+
labels
(optional) A map of freeform package labels:
+labels:
+ foo: "bar"
+ baz: "test"
+
Labels can be used in luet search
to find packages by labels, e.g.:
$> luet search --by-label foo
+
license
(optional) A string indicating the package license type.
+license: "GPL-3"
+
name
(required) A string containing the name of the package
+name: "foo"
+
provides
(optional) List of packages which the current package is providing.
+conflicts:
+- name: "foo"
+ category: "bar"
+ version: "1.0"
+...
+- name: "baz"
+ category: "bar"
+ version: "1.0"
+
See Package concepts for more information on how to represent a package in a Luet tree.
+requires
(optional) List of packages which it depends on in runtime.
+A list of packages that the current package depends on in runtime. The determines the resolution tree of the package itself.
+requires:
+- name: "foo"
+ category: "bar"
+ version: "1.0"
+...
+- name: "baz"
+ category: "bar"
+ version: "1.0"
+
See Package concepts for more information on how to represent a package in a Luet tree.
+uri
(optional) A list of URI relative to the package ( e.g. the official project pages, wikis, README, etc )
+uri:
+- "http://www.mocaccino.org"
+- ...
+
version
(required) A string containing the version of the package
+version: "1.0"
+
All the luet
commands which takes a package as argument, respect the following syntax notation:
cat/name
: will default to selecting any available package=cat/name
: will default to gentoo parsing with regexp so also =cat/name-1.1
workscat/name@version
: will select the specific version wanted ( e.g. cat/name@1.1
) but can also include ranges as well cat/name@>=1.1
name
: just name, category is omitted and considered emptyFinalizers are denoted in a finalize.yaml
file, which is a sibiling of definition.yaml
and build.yaml
file. It contains a list of commands that finalize the package when it is installed in the machine.
finalize.yaml:
+install:
+- rc-update add docker default
+
install
: List of commands to run in the host machine. Failures are eventually ignored, but will be reported and luet will exit non-zero in such case.Was this page helpful?
+ + ++ Glad to hear it! Please tell us how we can improve. +
++ Sorry to hear that. Please tell us how we can improve. +
+Luet supports the sprig
rendering engine template, like helm. It’s being used to interpolate build.yaml
and finalize.yaml
files before their execution. The following document assumes you are familiar with the helm
templating.
The build.yaml
and finalize.yaml
files are rendered during build time, and it’s possible to use the helm
templating syntax inside such files. The definition.yaml
file will be used to interpolate templating values available in build.yaml
Given the following definition.yaml
:
name: "test"
+category: "foo"
+version: "1.1"
+
+additional_field: "baz"
+
A build.yaml
can look like the following, and interpolates it’s values during build time:
image: ...
+
+steps:
+- echo {{.Values.name}} > /package_name
+- echo {{.Values.additional_field}} > /extra
+
+
Which would be for example automatically rendered by luet like the following:
+
+image: ...
+
+steps:
+- echo test > /package_name
+- echo baz > /extra
+
+
This mechanism can be used in collections as well, and each stanza in packages
is used to interpolate each single package.
It’s possible to interpolate during build phase all the package specs targeted for build with the --values
flag, which takes a yaml file of an arbitrary format, if variables are clashing, the yaml supplied in --values
takes precedence and overwrite the values of each single definition.yaml
file.
Since luet 0.17.5
it is possible to share templates across different packages. All templates blocks found inside the templates
folder inside the root luet tree
of a repository gets templated and shared across all the packages while rendering each compilation spec of the given tree.
Consider the following:
+shared_templates
+├── templates
+│ └── writefile.yaml
+└── test
+ ├── build.yaml
+ └── collection.yaml
+
collection.yaml
We define here two packages with a collection. They will share the same compilation spec to generate two different packages + + + + + +
packages:
+- category: "test"
+ name: "foo"
+ version: "1.0"
+- category: "test"
+ name: "bar"
+ version: "1.0"
+
writefile.yaml
All the files in the templates
folder will get rendered by the template for each package in the tree. We define here a simple block to write out a file from the context which is passed by:
+
+
+
+
+
+
{{ define "writefile" }}
+- echo conflict > /foo/{{.}}
+{{end}}
build.yaml
Finally the build spec consumes the template block we declared above, passing by the name of the package: + + + + + +
image: "alpine"
+prelude:
+ - mkdir /foo
+steps:
+{{ template "writefile" .Values.name }}
+package_dir: /foo
The finalize.yaml
file has access only to the package fields during templating. Extra fields that are present in the definition.yaml
file are not accessible during rendering in the finalize.yaml
file, but only the package fields (name
, version
, labels
, annotations
, …)
Was this page helpful?
+ + ++ Glad to hear it! Please tell us how we can improve. +
++ Sorry to hear that. Please tell us how we can improve. +
+Luet can be extended in 2 ways by extensions and plugins.
+You need to have a working luet
binary installed.
Extensions expand Luet featureset horizontally, so for example, “luet geniso” will allow you to build an iso using luet, without this needing to be part of the luet core.
+An Extension is nothing more than a standalone executable file, whose name begins with luet-
. To install an extension, simply move its executable file to anywhere on your system PATH
.
All the plugins will be accessible to luet as luet pluginname
You can write an extension in any programming language or script that allows you to write command-line commands.
+Executables receive the inherited environment from luet. An extension determines which command path it wishes to implement based on its name. For example, a plugin wanting to provide a new command luet foo, would simply be named luet-foo, and live somewhere in your PATH.
+#!/bin/bash
+
+if [[ "$1" == "help" ]]
+then
+ echo "Extension help"
+ exit 0
+fi
+
+if [[ "$1" == "run" ]]
+then
+ # do something interesting
+fi
+
+echo "I am an Extension named luet-foo"
+
+
To use the above extension, simply make it executable:
+$ sudo chmod +x ./luet-foo
+
and place it anywhere in your PATH:
+$ sudo mv ./luet-foo /usr/local/bin
+
You may now invoke your extension as a luet command:
+$ luet foo
+I am an Extension named luet-foo
+
All args and flags are passed as-is to the executable:
+$ luet foo help
+
+Extension help
+
Plugins instead are expanding Luet vertically by hooking into internal events. Plugins and Extensions can be written in any language, bash included! Luet uses go-pluggable so it can dispatch events to external binaries.
+Similarly to Extensions, a Plugin is nothing more than a standalone executable file, but without any special prefix. To install a plugin, simply move its executable file to anywhere on your system PATH
.
Differently from Extensions, they are not available from the CLI and cannot be invoked directly by the user, instead they are called by Luet during its lifecycle.
+You can write a plugin in any programming language or script.
+The first argument that is passed to a plugin will always be the event that was emitted by Luet in its lifecycle. You can see all the events available here. The second argument, is a JSON
encoded payload of the object that Luet is emitting with the event. The object(s) may vary depending on the emitted event.
The output of the plugin (stdout
) will be parsed as JSON. Every plugin must return a valid JSON at the end of its execution, or it will be marked as failed and stops luet
further execution. See also the go-pluggable README.
The returning payload should be in the following form:
+{ "state": "", "data": "data", "error": ""}
+
By returning a json with the error field not empty, it will make fail the overall execution.
+#!/bin/bash
+echo "$1" >> /tmp/event.txt
+echo "$2" >> /tmp/payload.txt
+
+echo "{}"
+
+
To use the above plugin, simply make it executable:
+$ sudo chmod +x ./test-foo
+
and place it anywhere in your PATH:
+$ sudo mv ./test-foo /usr/local/bin
+
Now, when running luet, add --plugin test-foo
:
+$ luet --plugin test-foo install -y foopackage
+
+
And check /tmp/event.txt
to see the event fired and /tmp/payload.txt
to check the payloads that were emitted by Luet.
A plugin that prints the images that are being built in /tmp/exec.log
:
#!/bin/bash
+exec >> /tmp/exec.log
+exec 2>&1
+event="$1"
+payload="$2"
+if [ "$event" == "image.post.build" ]; then
+ image=$(echo "$payload" | jq -r .data | jq -r .ImageName )
+ echo "{ \"data\": \"$image built\" }"
+else
+ echo "{}"
+fi
+
+
Was this page helpful?
+ + ++ Glad to hear it! Please tell us how we can improve. +
++ Sorry to hear that. Please tell us how we can improve. +
+Contribution guidelines for the Luet project are on the Github repository. Here you can find some heads up for contributing to the documentation website.
+We use github to host code, to track issues and feature requests, as well as accept pull requests.
+We use Hugo to format and generate our website, the +Docsy theme for styling and site structure, +and Github Actions to manage the deployment of the site. +Hugo is an open-source static site generator that provides us with templates, +content organisation in a standard directory structure, and a website generation +engine. You write the pages in Markdown (or HTML if you want), and Hugo wraps them up into a website.
+All submissions, including submissions by project members, require review. We +use GitHub pull requests for this purpose. Consult +GitHub Help for more +information on using pull requests.
+In short, when you submit code changes, your submissions are understood to be under the same License that covers the project. Feel free to contact the maintainers if that’s a concern.
+If you’ve just spotted something you’d like to change while using the docs, Docsy has a shortcut for you:
+Here’s a quick guide to updating the docs with a git local checkout. It assumes you’re familiar with the +GitHub workflow and you’re happy to use the automated preview of your doc +updates:
+make serve
and browse to localhost:1313
By contributing, you agree that your contributions will be licensed under the project Licenses.
+ + + +Was this page helpful?
+ + ++ Glad to hear it! Please tell us how we can improve. +
++ Sorry to hear that. Please tell us how we can improve. +
+No dependencies. For building packages see the Build Packages section
+Just grab a release from the release page on GitHub. The binaries are statically compiled.
+Or you can install Luet also with a single command:
+curl https://get.mocaccino.org/luet/get_luet_root.sh | sudo sh
+
Requirements:
+$> git clone https://github.com/mudler/luet
+$> cd luet
+$> make build # or just go build
+
In the following section we will see how to install luet with luet itself. We will use a transient luet version that we are going to throw away right after we install it in the system.
+# Get a luet release. It will be used to install luet in your system
+wget https://github.com/mudler/luet/releases/download/0.8.3/luet-0.8.3-linux-amd64 -O luet
+chmod +x luet
+
+# Creates the luet configuration file and add the luet-index repository.
+# The luet-index repository contains a collection of repositories which are
+# installable and tracked in your system as standard packages.
+cat > .luet.yaml <<EOF
+repositories:
+- name: "mocaccino-repository-index"
+ description: "MocaccinoOS Repository index"
+ type: "http"
+ enable: true
+ cached: true
+ priority: 1
+ urls:
+ - "https://raw.githubusercontent.com/mocaccinoOS/repository-index/gh-pages"
+EOF
+
+# Install the official luet repository to get always the latest luet version
+./luet install repository/luet
+
+# Install luet (with luet) in your system
+./luet install system/luet
+
+# Remove the temporary luet used for bootstrapping
+rm -rf luet
+
+# Copy over the config file to your system
+mkdir -p /etc/luet
+mv .luet.yaml /etc/luet/luet.yaml
+
Luet stores its configuration files in /etc/luet
. If you wish to override its default settings, create a file /etc/luet/luet.yaml
.
A example of a luet.yaml
file can be found here.
There are a bunch of configuration settings available, but the most relevant are:
+logging:
+ color: true # Enable/Disable colored output
+ enable_emoji: true # Enable/Disable emoji from output
+general:
+ debug: false # Enable/Disable debug
+system:
+ rootfs: "/" # What's our rootfs. Luet can install packages outside of "/"
+ database_path: "/var/db/luet" # Where to store DB files
+ database_engine: "boltdb"
+ tmpdir_base: "/var/tmp/luet" # The temporary directory to be used
+
To add repositories, you can either add a repositories
stanza in your /etc/luet/luet.yaml
or either add one or more yaml files in /etc/luet/repos.conf.d/
.
/etc/luet/luet.yaml
logging:
+ color: true # Enable/Disable colored output
+ enable_emoji: true # Enable/Disable emoji from output
+general:
+ debug: false # Enable/Disable debug
+system:
+ rootfs: "/" # What's our rootfs. Luet can install packages outside of "/"
+ database_path: "/var/db/luet" # Where to store DB files
+ database_engine: "boltdb"
+ tmpdir_base: "/var/tmp/luet" # The temporary directory to be used
+repositories:
+- name: "some-repository-name" # Repository name
+ description: "A beautiful description"
+ type: "http" # Repository type, disk or http are supported (disk for local path)
+ enable: true # Enable/Disable repo
+ cached: true # Enable cache for repository
+ priority: 3 # Cache priority
+ urls: # Repository URLs
+ - "...."
+
/etc/luet/repos.conf.d/
A repository file can be for example:
+name: "..." # Repository name
+description: "..."
+type: "http" # Repository type, disk or http are supported (disk for local path)
+enable: true # Enable/Disable repo
+cached: true # Enable cache for repository
+priority: 3 # Cache priority
+urls: # Repository URLs
+ - "..."
+
There is available a collection of repositories, which is containing a list of repositories that can be installed in the system with luet install
.
If you installed Luet from the curl command, you just need to run luet search repository
to see a list of all the available repository, and you can install them singularly by running luet install repository/<name>
. Otherwise, add the repository stanzas you need to /etc/luet/luet.yaml
.
Was this page helpful?
+ + ++ Glad to hear it! Please tell us how we can improve. +
++ Sorry to hear that. Please tell us how we can improve. +
+Luet is a Package Manager based on containers. It provides an abstraction layer over container specfile format, enhancing the image resolution process of open container inititative (OCI) runtimes with boolean satisfiability problem (SAT) solving techniques.
+Luet can be used to build packages, container images, as well as to manage and distribute installations of derived packages locally.
+Allows to apply semver constraints to Image dependencies, treating it as a classical CSP.
+ + +First steps with Luet
+Documentation references
+Howtos, Cookbooks
+Luet examples, resources, API reference
+Was this page helpful?
+ + ++ Glad to hear it! Please tell us how we can improve. +
++ Sorry to hear that. Please tell us how we can improve. +
+Here we show an example on how to build “burnable” SD images for Raspberry Pi with Luet. This approach lets you describe and version OTA upgrades for your embedded devices, delivering upgrades as layer upgrades on the Pi.
+The other good side of the medal is that you can build a Luet package repository with multiple distributions (e.g. Raspbian
, OpenSUSE
, Gentoo
, … ) and switch among them in runtime. In the above example Raspbian
and Funtoo
(at the time of writing) are available.
You have to run the following steps inside an ARM board to produce arm-compatible binaries. Any distribution with Docker will work. Note that the same steps could be done in a cross-compilation approach, or with qemu-binfmt in a amd64 host.
+You will also need in your host:
+/usr/bin/luet
(arm build)Clone the repository https://github.com/Luet-lab/luet-embedded
+$> git clone https://github.com/Luet-lab/luet-embedded
+$> cd luet-embedded
+$> sudo make build-all
+...
+
+If a rebuild is needed, just do sudo make rebuild-all
after applying the changes.
$> sudo make create-repo
+...
+
+$> make serve-repo
+...
+
+$> sudo LUET_PACKAGES='distro/funtoo-1.4 distro/raspbian-boot-0.20191208 system/luet-develop-0.5' make image
+...
+
+$> sudo LUET_PACKAGES='distro/raspbian-0.20191208 distro/raspbian-boot-0.20191208 system/luet-develop-0.5' make image
+...
+
+At the end of the process, a file luet_os.img
, ready to be flashed to an SD card, should be present in the current directory.
In order to build and add packages to the exiting repository, simply add or edit the specfiles under the distro
folder. When doing make rebuild-all
the packages will be automatically compiled and made available to the local repository.
Was this page helpful?
+ + ++ Glad to hear it! Please tell us how we can improve. +
++ Sorry to hear that. Please tell us how we can improve. +
+Creating and building a simple package:
+$> mkdir package
+
+$> cat <<EOF > package/build.yaml
+image: busybox
+steps:
+- echo "foo" > /foo
+EOF
+
+$> cat <<EOF > package/definition.yaml
+name: "foo"
+version: "0.1"
+EOF
+
+$> luet build --all
+
+📦 Selecting foo 0.1
+📦 Compiling foo version 0.1 .... ☕
+🐋 Downloading image luet/cache-foo-bar-0.1-builder
+🐋 Downloading image luet/cache-foo-bar-0.1
+📦 foo Generating 🐋 definition for builder image from busybox
+🐋 Building image luet/cache-foo-bar-0.1-builder
+🐋 Building image luet/cache-foo-bar-0.1-builder done
+ Sending build context to Docker daemon 4.096kB
+ ...
+
+
In order to build a specific version, a full package definition (triple of category
, name
and version
) has to be specified.
+In this example we will also enable package compression (gzip).
$> mkdir package
+
+$> cat <<EOF > package/build.yaml
+image: busybox
+steps:
+- echo "foo" > /foo
+EOF
+
+$> cat <<EOF > package/definition.yaml
+name: "foo"
+version: "0.1"
+category: "bar"
+EOF
+
+$> luet build bar/foo-0.1 --compression gzip
+
+📦 Selecting foo 0.1
+📦 Compiling foo version 0.1 .... ☕
+🐋 Downloading image luet/cache-foo-bar-0.1-builder
+🐋 Downloading image luet/cache-foo-bar-0.1
+📦 foo Generating 🐋 definition for builder image from busybox
+🐋 Building image luet/cache-foo-bar-0.1-builder
+🐋 Building image luet/cache-foo-bar-0.1-builder done
+ Sending build context to Docker daemon 4.096kB
+ ...
+
+
+
+
+Was this page helpful?
+ + ++ Glad to hear it! Please tell us how we can improve. +
++ Sorry to hear that. Please tell us how we can improve. +
+There might be several reasons why packages fails to build, for example, if your build fails like this:
+$ luet build ...
+
+ INFO Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
+ ERROR Error: Failed compiling development/toolchain-go-0.6: failed building package image: Could not push image: quay.io/mocaccino/micro-toolchain:latest toolchain-go-development-0.6-builder.dockerfile: Could not build image: quay.io/mocaccino/micro-toolchain:latest toolchain-go-development-0.6-builder.dockerfile: Failed running command: : exit status 1
+ ERROR Bailing out
+
means the user you are running the build command can’t either connect to docker or docker
is not started.
Check if the user you are running the build is in the docker
group, or if the docker
daemon is started.
Luet by default if run with multiple packages summarize errors and can be difficult to navigate to logs, but if you think you might have found a bug, run the build with --debug
before opening an issue.
luet
?Well, I have the idea that programs should be small, so they are not difficult to type and easy to remember, and easy to stick in. luet
is really a combination of the first letters of my fiancee name (Lucia) and my name (Ettore) lu+et = luet
! and besides, happen to be also a small bridge in Italy ;)
Was this page helpful?
+ + ++ Glad to hear it! Please tell us how we can improve. +
++ Sorry to hear that. Please tell us how we can improve. +
+Use Luet to build, track, and release OTA update for your embedded devices.
+Examples to build with Luet
+FAQ
+Using Luet to compose images from scratch
+References to various resources related to luet
+Was this page helpful?
+ + ++ Glad to hear it! Please tell us how we can improve. +
++ Sorry to hear that. Please tell us how we can improve. +
+Here is a list of references to projects that are related to Luet (open up a PR to add yours to the list!):
+Description | +URL | +
---|---|
Official Luet repository | +https://github.com/Luet-lab/luet-repo | +
Example repository to host package browser websites on gh-pages. It uses the package-browser extension to generate HTML pages from a list of luet repositories | +https://github.com/Luet-lab/package-browser-sample | +
LineageOS builds with luet | +https://github.com/mudler/android-builds | +
Example repository template to build packages on github actions and push packages on a container registry | +https://github.com/Luet-lab/github-repository | +
Immutable container OS toolkit | +https://github.com/rancher-sandbox/cOS-toolkit | +
mocaccinoOS desktop | +https://github.com/mocaccinoOS/desktop | +
mocaccinoOS extra | +https://github.com/mocaccinoOS/mocaccino-extra | +
Was this page helpful?
+ + ++ Glad to hear it! Please tell us how we can improve. +
++ Sorry to hear that. Please tell us how we can improve. +
+The Docker image quay.io/luet/base
is a scratch
Docker image always kept up-to-date with the latest luet version. That image can be used to bootstrap new images with Luet repositories with the packages you want, from the repositories you prefer.
For example we can mount a config file, and later on install a package:
+cat <<EOF > $PWD/luet.yaml
+repositories:
+ - name: "micro-stable"
+ enable: true
+ cached: true
+ priority: 1
+ type: "http"
+ urls:
+ - "https://get.mocaccino.org/mocaccino-micro-stable"
+EOF
+
+docker rm luet-runtime-test || true
+docker run --name luet-runtime-test \
+ -ti -v /tmp:/tmp \
+ -v $PWD/luet.yaml:/etc/luet/luet.yaml:ro \
+ quay.io/luet/base install shells/bash
+
+docker commit luet-runtime-test luet-runtime-test-image
+
+# Try your new image!
+
+docker run -ti --entrypoint /bin/bash --rm luet-runtime-test-image
+
In this way we will create a new image, with only luet
and bash
, and nothing else from a scratch image.
Was this page helpful?
+ + ++ Glad to hear it! Please tell us how we can improve. +
++ Sorry to hear that. Please tell us how we can improve. +
+In this example, we will build the awesome CatClock on containers we will run it locally in a Luet box.
+We will do this experiment to prove two things:
+To build packages with Luet, you must have installed Docker and container-diff, follow our setup guide.
+To prove our point, we will build our package from an OpenSUSE image, and later on we will consume +entropy repositories for runtime dependencies. To note, this is not the main focus of Luet, and this is a restricted example on its features on build-time resolution. For more syntax examples, see also Build specs and Package types.
+Run this commands in any directory you choose to be your workspace:
+
+# Let's create a directory to store our package spec:
+mkdir -p tree/misc/catclock/
+
Now, let’s generate our build spec:
+# Create a build file. We use here opensuse/leap to build the package, as an example
+cat <<EOF > tree/misc/catclock/build.yaml
+image: opensuse/leap
+
+# Preparation phase
+prelude:
+- zypper in -y git make libXt-devel xmh gcc motif-devel libXext-devel libpulse-devel libaubio-devel
+- git clone https://github.com/BarkyTheDog/catclock
+
+# Here we define the steps that Luet will follow
+steps:
+- cd catclock && make DEFINES="-Wno-incompatible-pointer-types"
+- mv catclock/xclock /usr/bin/xclock
+
+# (optional) File list that will be included in the final package
+# Luet will filter out files that won't match any entry in the list (regex syntax IS supported)
+includes:
+- /usr/bin/xclock
+EOF
+
+
build.yaml
is what an ebuild is for Gentoo and for e.g. what PKGBUILD is for Arch.
zypper
(the openSUSE package manager), and the CatClock with git
. When we declare an image
keyword in a spec, it becomes a seed package ( Package types ) as doesn’t depend on any package in build time, we will cover more use cases in other examples.Now we generate the runtime spec, it’s the part about the binary end which will be installed in the system. It also holds the metadata relative to the package definition (name
, category
, version
).
# Create a runtime definition.
+# We will leverage packages already present on Sabayon Entropy repositories
+# the end-system needs to have the Luet Sabayon Entropy repositories enabled.
+cat <<EOF > tree/misc/catclock/definition.yaml
+category: "misc"
+name: "catclock"
+version: "0.20200318"
+requires:
+- category: meta
+ name: users
+ version: ">=0"
+- category: x11-libs
+ name: motif
+ version: ">=0.1"
+- category: media-libs
+ name: libjpeg-turbo
+ version: ">=0.1"
+EOF
+
ldd
) to which libraries it depends on.
+In this example we consume the dependencies from the Luet Entropy Repo, that we will enable on the following steps.sudo /usr/bin/luet build \
+--tree=$PWD/tree misc/catclock \
+--destination $PWD/build \
+--compression gzip
+
+sudo chown -R $USER $PWD/build # So later on, we can access to the repository with our user
+
We are building the specs in this step.
+tree
.build
.Note, we need sudo to keep the permissions properly mapped in the artifact which is produced +this is not always the case. Depends on the package content.
+We will generate now our repository metadata:
+/usr/bin/luet create-repo --tree "tree" \
+--output $PWD/build \
+--packages $PWD/build \
+--name "test repo" \
+--descr "Test Repo" \
+--tree-compression gzip \
+--meta-compression gzip
+
Creating a repository in Luet is about adding metadata and make our spec tree available to other systems running Luet to intall the package.
+Now we are all set. We have the packages compiled, and we are ready to consume them. We don’t want to break our host system, and we want to test this from our user.
+Let’s create a directory, we will try to setup a full running system, and install everything there.
+# Let's create a directory for our "fake" rootfilesystem
+# it will be populated with a minimal set of packages needed to run
+# our amazing catclock
+mkdir -p $PWD/rootfs
+
# Let's also create a directory to store our config files
+mkdir -p $PWD/conf
+
We will generate now a Luet config. The Luet config is used to read where install things from, and in which directory. +It also lists the repositories that are used by the client to retrieve packages remotely.
+# We create here a config file which references the rootfs.
+# In this way, luet instead installing packages to your host system, will populate the rootfs
+# (note, all the steps are run by a user here, no root required!)
+cat <<EOF > conf/luet-dso-local.yaml
+system:
+ rootfs: $PWD/rootfs # our "fake" rootfs that we created before
+ database_path: "/" # this is where our Luet DB will live
+ database_engine: "boltdb" # this is the Luet DB engine
+repositories:
+ - name: "main"
+ type: "disk"
+ priority: 3
+ enable: true
+ urls:
+ - "$PWD/build" # This is the repository we have created before!
+ - name: "sabayonlinux.org"
+ description: "Sabayon Linux Repository"
+ type: "http"
+ enable: true
+ cached: true
+ priority: 2
+ urls:
+ - "https://dispatcher.sabayon.org/sbi/namespace/luet-entropy-repo"
+ - name: "luet-repo"
+ description: "Luet Official Repository"
+ type: "http"
+ enable: true
+ cached: true
+ priority: 1
+ urls:
+ - "https://raw.githubusercontent.com/Luet-lab/luet-repo/gh-pages"
+EOF
+# we have specified an additional repository, one that is luet-entropy-repo (which contains
+# the runtime dependencies we specified in our package)
+
# Let's populate our rootfs with some minimal things: base-gcc, and bash
+# meta/users is a meta package providing minimal base to run things with a full
+# user-level support.
+export LUET_NOLOCK=true
+luet install \
+--config $PWD/conf/luet-dso-local.yaml \
+meta/users
+
# catclock is a X11 app! we want to be able to play with it locally from our host :)
+# Let's copy the .Xauthority file to allow the X app to communicate with our X server
+# Note: This can be achieved in other ways (set up a tcp X server, and so on)
+cp -rfv $HOME/.Xauthority $PWD/rootfs/
+
luet install \
+--config $PWD/conf/luet-dso-local.yaml \
+misc/catclock
+
# Let's run our beautiful catclock :)
+luet box exec --rootfs $PWD/rootfs \
+--stdin --stdout --stderr --env DISPLAY=$DISPLAY \
+--env XAUTHORITY=/.Xauthority --mount /tmp --entrypoint /usr/bin/xclock
+
Spawn a bash shell inside our box (with permission to access to our running X):
+luet box exec --rootfs $PWD/rootfs \
+--stdin --stdout --stderr --env DISPLAY=$DISPLAY \
+--env XAUTHORITY=/.Xauthority --mount /tmp --entrypoint /bin/bash
+
Was this page helpful?
+ + ++ Glad to hear it! Please tell us how we can improve. +
++ Sorry to hear that. Please tell us how we can improve. +
+This article will guide you to build your first package with Luet! +For this purpose, we have picked a real-world example: gogs which is a “painless self-hosted Git service”, an open-source alternative to Github.
+Gogs is written in Golang, and we need a working Golang version in order to build it.
+Here you can see a live recorded session of this tutorial:
+ +Everything starts from a Luet tree. A Luet tree is just a directory containing one (or more) Luet specfile, here on we assume that you are working in a dedicated folder (e.g. ~/demo
) in your system.
Let’s create then a package that will be our base to build other packages from now on, we have picked busybox
here - it is really small and enough for our purpose.
mkdir busybox
+
Let’s now write the build specification, which is just containing the image tag that we are referencing to
+cat <<EOF > busybox/build.yaml
+image: "busybox:{{.Values.version}}-glibc"
+EOF
+
Now, lets write the definition.yaml
, which contains the metadata information about our package ( e.g. how we refer to it with luet, the version, and so on )
cat <<EOF > busybox/definition.yaml
+category: "distro"
+name: "busybox"
+version: "1.33.0"
+EOF
+
We need now golang in order to build gogs
. Let’s declare then a golang package:
mkdir golang
+
And a build specfile, which is simply fetch golang from https://golang.org and installing it in the busybox container:
+cat <<EOF > golang/build.yaml
+requires:
+- category: "distro"
+ name: "busybox"
+ version: ">=0"
+
+prelude:
+- wget https://golang.org/dl/go{{.Values.version}}.linux-{{.Values.arch}}.tar.gz -O golang.tar.gz
+- mkdir /usr/local
+steps:
+- tar -C /usr/local -xzf golang.tar.gz
+EOF
+
Note how we require
busybox. The Golang container will now be based from busybox, and the prelude
and steps
fields will be executed in that context.
And finally let’s write the golang metadata files, so we can refer to it from other packages
+cat <<EOF > golang/definition.yaml
+name: "go"
+category: "dev-lang"
+version: "1.15.6"
+arch: "amd64"
+EOF
+
Finally we can write the gogs package definition!
+mkdir gogs
+
The build specfile, will just fetch the gogs
sources at a given version (specified in the definition.yaml
) and build the sources with go:
cat <<'EOF' > gogs/build.yaml
+requires:
+- category: "dev-lang"
+ name: "go"
+ version: ">=0"
+env:
+- GOPATH="/go"
+- GOGSPATH="$GOPATH/src/github.com/gogs/gogs"
+- PATH=$PATH:/usr/local/go/bin
+- CGO_ENABLED=0
+prelude:
+- mkdir -p $GOPATH/src/github.com/gogs
+- wget https://github.com/gogs/gogs/archive/v{{.Values.version}}.tar.gz -O - | tar -xzf - -C ./ && mv gogs-{{.Values.version}} $GOGSPATH
+steps:
+- mkdir /usr/bin
+- cd $GOGSPATH && go build && mv gogs /usr/bin/gogs
+excludes:
+# Cache generated by Golang
+- ^/root
+EOF
+
And the metadata, in this way we can refer to gogs in a Luet tree:
+cat <<EOF > gogs/definition.yaml
+category: "dev-vcs"
+name: "gogs"
+version: "0.11.91"
+EOF
+
The simplest and mostly immediate way to build packages, is running luet build <packagename>
in the same folder you have your Luet tree.
In this case, to build gogs and its deps, we can do:
+luet build dev-vcs/gogs
+
And that’s it! you will find the package archives in build/
in the same folder where you started the command.
You will see that Luet generates not only archives with the file resulting to your builds, but it will also generate metadata files (ending with .metadata.yaml
) that contains additional metadata information about your build and the package itself (e.g. checksums).
You can use tools like yq to inspect those:
+yq r build/gogs-dev-vcs-0.11.91.metadata.yaml checksums
+
Now if you want to consume the artifacts just built with luet install
, you can create a repository with luet create-repo
.
Was this page helpful?
+ + ++ Glad to hear it! Please tell us how we can improve. +
++ Sorry to hear that. Please tell us how we can improve. +
+Everything starts from an “Hello!”
+Example on how to build a package and run it locally with luet box
+Was this page helpful?
+ + ++ Glad to hear it! Please tell us how we can improve. +
++ Sorry to hear that. Please tell us how we can improve. +
+Luet uses Container technologies ( Docker, img ) to build packages. +It provides an abstraction over the Dockerfile format introducing relation and versioning of images.
+ + + +Luet uses SAT Solving techniques to compute the dependencies graph. +This allows to refer to docker images by using semver constraints.
+No Relational db is involved.
+ +If you like to play with code, check out our issues that are marked as “good first issue” and open a Pull Request on GitHub. +New users are always welcome, and have fun!
+ +Use container abstraction to define your package repositories
+ +Build, ship and delivery your software. Faster
+ +When Luet is used as installer, it has zero dependencies.
+Your system can’t break anymore
+ +Thanks to its SAT core, Luet can reconstruct images defined by dependencies and version constraints.
+Building a de-facto tree of container images
+ +').text(`No results found for query "${f}"`)):i.forEach(e=>{const f=d.get(e.ref),g=b.data('offline-search-base-href')+e.ref.replace(/^\//,''),c=a('
').text(f.excerpt)),h.append(c)}),e.on('shown.bs.popover',()=>{a('.search-result-close-button').on('click',()=>{e.val(''),e.trigger('change')})});const j=a.fn.tooltip.Constructor.Default.whiteList;j['*'].push('style'),e.data('content',g[0].outerHTML).popover({whiteList:j}).popover('show')}})}(jQuery),function(){var c=function(){a=document.createElement('div'),a.classList.add('drawioframe'),b=document.createElement('iframe'),a.appendChild(b),document.body.appendChild(a)},d=function(){a&&(document.body.removeChild(a),a=void 0,b=void 0)},e=function(a,g){var h="https://embed.diagrams.net/",e,f;h+='?embed=1&ui=atlas&spin=1&modified=unsavedChanges&proto=json&saveAndEdit=1&noSaveBtn=1',e=document.createElement('div'),e.classList.add('drawio'),a.parentNode.insertBefore(e,a),e.appendChild(a),f=document.createElement('button'),f.classList.add('drawiobtn'),f.insertAdjacentHTML('beforeend',''),e.appendChild(f),f.addEventListener('click',function(f){if(b)return;c();var e=function(f){var h=b.contentWindow,c,i;if(f.data.length>0&&f.source==h){if(c=JSON.parse(f.data),c.event=='init')h.postMessage(JSON.stringify({action:'load',xml:g}),'*');else if(c.event=='save')i=g.indexOf('data:image/png')==0?'xmlpng':'xmlsvg',h.postMessage(JSON.stringify({action:'export',format:i}),'*');else if(c.event=='export'){const d=a.src.replace(/^.*?([^/]+)$/,'$1'),b=document.createElement('a');b.setAttribute('href',c.data),b.setAttribute('download',d),document.body.appendChild(b),b.click(),b.parentNode.removeChild(b)}(c.event=='exit'||c.event=='export')&&(window.removeEventListener('message',e),d())}};window.addEventListener('message',e),b.setAttribute('src',h)})},b,a;document.addEventListener('DOMContentLoaded',function(){for(const d of document.getElementsByTagName('img')){const c=d,b=c.getAttribute('src');if(!b.endsWith('.svg')&&!b.endsWith('.png'))continue;const a=new XMLHttpRequest;a.responseType='blob',a.open("GET",b),a.addEventListener("load",function(){const b=new FileReader;b.addEventListener('load',function(){if(b.result.indexOf('mxfile')!=-1){const b=new FileReader;b.addEventListener('load',function(){const a=b.result;e(c,a)}),b.readAsDataURL(a.response)}}),b.readAsBinaryString(a.response)}),a.send()}})}() \ No newline at end of file diff --git a/js/prism.js b/js/prism.js new file mode 100644 index 00000000..ae881ac8 --- /dev/null +++ b/js/prism.js @@ -0,0 +1,21 @@ +/* PrismJS 1.21.0 +https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+bash+c+csharp+cpp+go+java+markdown+python+scss+sql+toml+yaml&plugins=toolbar+copy-to-clipboard */ +var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(u){var c=/\blang(?:uage)?-([\w-]+)\b/i,n=0,M={manual:u.Prism&&u.Prism.manual,disableWorkerMessageHandler:u.Prism&&u.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof W?new W(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/=l.reach);k+=y.value.length,y=y.next){var b=y.value;if(t.length>n.length)return;if(!(b instanceof W)){var x=1;if(h&&y!=t.tail.prev){m.lastIndex=k;var w=m.exec(n);if(!w)break;var A=w.index+(f&&w[1]?w[1].length:0),P=w.index+w[0].length,S=k;for(S+=y.value.length;S<=A;)y=y.next,S+=y.value.length;if(S-=y.value.length,k=S,y.value instanceof W)continue;for(var E=y;E!==t.tail&&(S
l.reach&&(l.reach=j);var C=y.prev;L&&(C=I(t,C,L),k+=L.length),z(t,C,x);var _=new W(o,g?M.tokenize(O,g):O,v,O);y=I(t,C,_),N&&I(t,y,N),1 >>u&g;if(_!==f>>>u&g)break;_&&(l+=(1<o&&(c=c.removeBefore(r,u,a-l)),c&&f i&&(i=c.size),a(u)||(c=c.map(function(e){return pe(e)})),r.push(c)}return i>e.size&&(e=e.setSize(i)),at(e,t,r)}function At(e){return e=lt)return function(e,t,n,r,o){for(var i=0,a=new Array(v),s=0;0!==n;s++,n>>>=1)a[s]=1&n?t[i++]:void 0;return a[r]=o,new We(e,i+1,a)}(e,f,c,s,d);if(l&&!d&&2===f.length&&tt(f[1^p]))return f[1^p];if(l&&d&&1===f.length&&tt(d))return d;var b=e&&e===this.ownerID,_=l?d?c:c^u:c|u,w=l?d?ut(f,p,d,b):function(e,t,n){var r=e.length-1;if(n&&t===r)return e.pop(),e;for(var o=new Array(r),i=0,a=0;as)return{value:void 0,done:!0};var e=o.next();return r||t===M?e:q(t,u-1,t===I?void 0:e.value[1],e)})},c}function Vt(e,t,n,r){var o=en(e);return o.__iterateUncached=function(o,i){var a=this;if(i)return this.cacheResult().__iterate(o,i);var s=!0,u=0;return e.__iterate(function(e,i,c){if(!s||!(s=t.call(n,e,i,c)))return u++,o(e,r?i:u-1,a)}),u},o.__iteratorUncached=function(o,i){var a=this;if(i)return this.cacheResult().__iterator(o,i);var s=e.__iterator(N,i),u=!0,c=0;return new U(function(){var e,i,l;do{if((e=s.next()).done)return r||o===M?e:q(o,c++,o===I?void 0:e.value[1],e);var p=e.value;i=p[0],l=p[1],u&&(u=t.call(n,l,i,a))}while(u);return o===N?e:q(o,i,l,e)})},o}function Ht(e,t){var n=s(e),o=[e].concat(t).map(function(e){return a(e)?n&&(e=r(e)):e=n?ae(e):se(Array.isArray(e)?e:[e]),e}).filter(function(e){return 0!==e.size});if(0===o.length)return e;if(1===o.length){var i=o[0];if(i===e||n&&s(i)||u(e)&&u(i))return i}var c=new ee(o);return n?c=c.toKeyedSeq():u(e)||(c=c.toSetSeq()),(c=c.flatten(!0)).size=o.reduce(function(e,t){if(void 0!==e){var n=t.size;if(void 0!==n)return e+n}},0),c}function Wt(e,t,n){var r=en(e);return r.__iterateUncached=function(r,o){var i=0,s=!1;return function e(u,c){var l=this;u.__iterate(function(o,u){return(!t||cs&&(n=s-u),i=n;i>=0;i--){for(var p=!0,f=0;fo&&(r=o):r=o;var i=t.length;if(i%2!=0)throw new TypeError("Invalid hex string");r>i/2&&(r=i/2);for(var a=0;ao)&&(n=o),e.length>0&&(n<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");r||(r="utf8");for(var i=!1;;)switch(r){case"hex":return b(this,e,t,n);case"utf8":case"utf-8":return _(this,e,t,n);case"ascii":return w(this,e,t,n);case"latin1":case"binary":return x(this,e,t,n);case"base64":return E(this,e,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return S(this,e,t,n);default:if(i)throw new TypeError("Unknown encoding: "+r);r=(""+r).toLowerCase(),i=!0}},u.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var O=4096;function A(e,t,n){var r="";n=Math.min(e.length,n);for(var o=t;oSearch Results
+
+