D4.11/sdk-02-blends.md

323 lines
10 KiB
Markdown

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.