D4.11/sdk-02-blends.md

10 KiB

Blends

Introduction

In the Devuan SDK, a blend is the preferred way we use to make customizations to the vanilla image. Using blends we can very easily create different flavors of our image, by easily including/excluding certain software packages, files, or anything we wish to do. Blends can become a very quick way of creating entire new derivatives of the original vanilla distribution we are building.

This time, we will take the DECODE OS as a blend example. In DECODE OS we provide a blend called decode which is the blend we use to create a production release of DECODE OS. The blend's files are contained within their own directory in the decode-os git repository.

Configuration

Any SDK requires a single file to act as a blend. This file is also a zsh script, and, at the very least, it must contain two functions called:

blend_preinst()
blend_postinst()

These functions are your pathway to expanding your blend into whatever you would like it to do. The preinst function is usually called right after bootstrapping the vanilla root filesystem, and the postinst function is called near the very end, just before packing or compressing the image. These two strategic places should be enough to do changes within the image. If this is not enough, blends also allow you to simply override any variable or function contained within libdevuansdk or the sdk you are using.

Our decode blend is such an example. It is a somewhat expanded blend, not contained within a single file, but rather a directory. This allows easier maintenance and makes the scripts clearer and cleaner.

Adding and removing packages

When we want to add or remove specific packages to our build, we have to override or append to libdevuansdk's arrays. The array for packages we want installed is called extra_packages, and the array for packages we want purged is called purge_packages. In the Decode blend, these can be found in the config file located inside the decode-os blend directory. Keep in mind that these arrays could already contain specific packages, so you are advised to rather append to them, than overriding them.

If the packages you want to install are not available in the repositories, you still have a way of automatically installing them. All you have to do is copy your corresponding .deb files to the following directory of the blend:

$R/extra/custom-packages/

And when that is done, just call the function install-custdebs

Creating a blend

Rather than explaining the following in theory, you are best off viewing the blend files that are provided with decode-os. It is a fairly simple blend and should give you enough insight on how to create your own blend. Here are some important guidelines for creating a blend:

  • The blend should always contain at least two functions

This means you must provide blend_preinst and blend_postinst in your blend. They don't even have to do anything, but they should be there. These two functions open the path for you to call any other functions you created for your blend.

  • When overriding functions, make sure they provide a result that doesn't break the API

Breaking the API may result in unwanted behavior. You should always study well the functions you are planning to override and figure out if it is safe to override them in the way you want. The same goes for any variables as well.

  • Any arguments used after the blend name when loading from the SDK are free for you to use in the blend.

This means you can use anything after the fourth argument ($4 in zsh) inside your blend if you require passing arguments to it.

These are some of the more important guidelines. There is plenty more tricks and quirks, but it's easy to find out how to tweak the configuration files and the blend in general once you read through a blend or two on your own.

Enable the blend

To use your blend in the first place, you need to make the SDK know about it. Thus you should append the path to your new blend inside the blend_map of the sdk file:

blend_map=(
    "devuan-live"    "$R/blends/devuan-live/devuan-live.blend"
    "decode"         "$R/../decode.blend"
    "heads"          "$R/../heads.blend"
    "ournewblend"    "$R/blends/newblend/new-blend.blend"
)

As you can see, the map is a key-value storage. So you can have an alias (name) for your blend, and just use that to point to the path of the blend. The blend file will be sourced by the SDK once it is told to do so.

A configuration file

For having a finer-grained control of what goes into our build, we can create a config file for our blend. From here we can easily control any configurable aspect of it, such as packages that go in or out, the blend name, and much more. Make sure you source this file from your blend.

Adding and removing packages was abstractly mentioned earlier: it goes into two separate arrays holding package names. To add packages, we append to the extra_packages array, which would look like this:

extra_packages+=(
    my_new_package
    foo
    bar
    baz
)

This would install the four packages my_new_package, foo, bar, and baz along with the ones predefined in either libdevuansdk or the SDK you are using. You may also want to see which those are in case you wish to exclude them, but they are sane and useful utilities which should be included in your build if possible. Overriding all those packages, you would need to reset the whole array, so you would simply issue this:

extra_packages=(
    my_new_package
    foo
    bar
    baz
)

As you can see, we no longer have the +=, but rather only =, which means we are not appending to the array, but rather redefining it.

All of the above applies as well for removing packages, but in this case the array is called purge_packages.

Custom packages

If you want to install deb packages that aren't in any repositories, put them in the blend directory and simply add them to another array in the configuration file. The contents of the arrays are the paths to the debs, relative to this configuration file:

custom_deb_packages=(
    yad_0.27.0-1_amd64.deb
    palemoon_27.2.0~repack-1_amd64.deb
)

To trigger the installation of these packages, you will need to copy them to $R/extra/custom_packages, and then call the install_custdebs function somewhere from your blend.

Custom files

Any files you want to add to the system to override what's there by default you can add using a rootfs overlay. Create a directory inside your blend directory called rootfs-overlay and simply put files inside it. The directory structure is absolute to the image we are building. For example what's in "rootfs-overlay/etc/" would end up in the "/etc" of our final image. See hier(7) in the Linux manpages for more explanation on this directory hierarchy.

If you end up with any files here, to actually copy them, you will need to either run cp -f it, or rsync the directory if you prefer.

The .blend file

We listed a path to the .blend file in our first step. We need to create this file now.

Start your blend file with the following, so the sdk is aware of the environment:

BLENDPATH="${BLENDPATH:-$(dirname $0)}"
source $BLENDPATH/config

The minimum blend should contain two functions: blend_preinst and blend_postinst. These functions are called at specific points in the build, where they give the most power: just after bootstrapping the vanilla system, and just before packaging the final build, respectively.

blend_preinst

A preinst function can look like this:

blend_preinst() {
    fn blend_preinst
    req=(BLENDPATH R)
    ckreq || return 1

    notice "executing blend preinst"

    add-user "user" "pass"
    cp -fv "$BLENDPATH"/*.deb "$R/extra/custom-packages" || zerr
    install-custdebs || zerr
}

As you can see, the pre-install function will add a new user with the credentials user:pass, it will copy our custom debs where they can be used, and finally it will trigger their installation.

The fn, req, ckreq part on the top of the function is a safety check for the function that is enabled by zuper. It allows us to check if variables are defined when the function is called and fail if it is wrong. You should utilize this as much as possible. The zerr calls are used to exit if the function fails.

blend_postinst

A post-install function can look like the following:

blend_postinst() {
    fn blend_postinst
    req=(BLENDPATH strapdir)
    ckreq || return 1

    notice "executing blend postinst"

    sudo cp -vf "$BLENDPATH"/rootfs-overlay/* $strapdir || zerr

    blend_finalize || zerr
}

This function would copy the rootfs-overlay to the strapdir (which holds our image's filesystem) and it would call the blend_finalize function. By default this function doesn't exist, we quote it as an example for you to see how it is possible to call your own functions as well. You can define them within the blend file.

Using a blend

As previously explained, you can use your blends through the SDK's interactive shell. In decode-os the blend is placed in the root of the git repository, and the sdk wrappers are located within. Therefore an SDK would have to source it with such a path:

$R/../decode.blend

If you take a look at vm-sdk's sdk file, you will see the blend_map array. Using a new blend requires you to add it to this map in the same manner. The map is key-value formatted, and on the left you have an alias of your blend, and on the right you have a script you have to write. It can either be the blend itself or any helper file you might need to initialize your blend.

After you've added it to the blend map, you simply initialize the SDK, and use the same load command we learned earlier, while appending the blend alias and any optional argument.

$ zsh -f
$ source sdk
$ load devuan decode <these> <arguments> <we> <can> <use> <in> <the> <blend>

With this, we've initialized our decode blend. It's always good to add a notice() call to your blend to signal it's been loaded successfully.

Once this is done, we simply build the image the same way we have learned before:

$ build_vagrant_dist

Consult the vm-sdk chapter for this.