Managing ports in FreeBSD with CFEngine3


CFEngine is a fantastic tool for managing configuration files and packages across heterogeneous environments. I figured I could use it to maintain a basic set of installed packages across my Debian, OpenBSD and FreeBSD systems.

Except that the FreeBSD package_method promises in the cfengine standard library suck.

They use FreeBSD packages, and unless you have a box building your own sets of FreeBSD packages...packages suck. They are built with all of the bells and whistles, ergo they pull in a ton of shit you probably don't need on your system.

So I wrote a package_method promise that instead uses portmaster to install and remove packages. Here's the code:

body package_method freebsd_portmaster
    package_changes => "individual";

    package_list_command => "/usr/sbin/pkg_info";

    package_list_name_regex    => "([^\s]+)-.*";
    package_list_version_regex => "[^\s]+-([^\s]+).*";

    package_installed_regex => ".*";

    package_name_convention => "$(name)";
    package_delete_convention => "$(name)-$(version)";

    package_file_repositories => {

    package_add_command => "/usr/local/sbin/portmaster -D -G --no-confirm";
    package_update_command => "/usr/local/sbin/portmaster -D -G --no-confirm";
    package_delete_command => "/usr/local/sbin/portmaster --no-confirm -e";

    expireafter => 240;

The only interesting thing about the body of the promise is that I'm using package_file_repositories in a way that it was never intended to be used. Since the port names needed to be provided to portmaster in a / format, I'm leveraging a cfengine trick to kind of fake that. If you provide a package_file_repositories clause, cfengine will prepend a variable called $(firstrepo) to the name of every package that it adds to the package_add_command line.

Armed with this knowledge, we just add a package_file_repositories entry for each port category directory, and the problem's been solved.