Examples

This is an example of a more complex configuration that tries to show of most features of uberdot and what you could achieve with inheritance. It’s a variation of a setup I’m using for over a half year now. The tags that I defined have the following semantics:

  • arch: An Arch linux based distribution is installed
  • antergos: An Antergos based distribution is installed
  • debian: An Debian based distribution is installed
  • master: The device is powerful and should be fullfeatured
  • minimal: The device is a low-end-device and most features should be disabled
  • small: This device has a small monitor
  • laptop: This device is a laptop
  • private: This my own device with private data and access to my local network
  • office: A device used for work
  • asus: An asus laptop of mine
  • thinkpad: A thinkpad laptop of mine

First I created some profiles that represent my current setup. Any device that I want to use this setup on inherits from it. These profiles define the programs that should be configured and link some platform specific configs.

# This profile is the base for all devices that use my current setup
class Main(Profile):
    def generate(self):
        tags("arch", "master")
        if info.distribution() == "Antergos":
            tags("antergos")
        subprof("Git", "I3", "Bash", "Zsh", "Vim")

        link("pacman.conf", directory="/etc")


# This profile contains configs that I only want on my private devices. It inherits from the Main profile.
class Private(Main):
    def generate(self):
        tags("private")
        super().generate()

        # File synchronisation
        link("unison_data.prf", name=".unison/data.prf")

        cd("/etc")
        # Custom hosts file and udev rules
        link("hosts", "mkinitcpio.conf")
        links("\d{2}-\w+\.rules", directory="udev/rules.d")

Then there are profiles for each of my devices. Pi and Server don’t inherit from Main so they will define subprofiles as well. The other profiles are used to link configs that configure hardware. These profiles (except for Laptop) are the only profiles that I install as a root profile.

# This profile contains configs that I use on all of my laptops
class Laptop(Profile):
    def generate(self):
        # Automate multimonitor if hdmi is plugged in
        link("hdmi_plugin.sh")
        # Start ssh server if connect to my own private network
        cd("/etc/NetworkManager")
        link("sshOnConnect.sh", directory="dispatcher.d")

        # Wifi profiles
        cd("system-connections")
        opt(permission=600)
        links("wifi-(.+).gpg", replace=r"\1", encrypted=True)


# This is an asus laptop of mine
class Asus(Private):
    def generate(self):
        tags("asus", "small", "laptop")
        super().generate()
        subprof("Laptop")


# This is an thinkpad laptop of mine
class Thinkpad(Private):
    def generate(self):
        tags("thinkpad", "small", "laptop")
        super().generate()
        subprof("Laptop")
        # Fixes sound
        link("alsa-base.conf", directory="/etc/modprobe.d/")


# This is my device at the office
class Office(Main):
    def generate(self):
        tags("office")
        super().generate()


# A raspberry pi I use as mini server
class Pi(Profile):
    def generate(self):
        tags("pi", "debian", "minimal")
        subprof("Git", "Bash", "Vim", "Radicale")


# A more powerful webserver
class Server(Profile):
    def generate(self):
        tags("debian", "master")
        subprof("Git", "Bash", "Vim")

At last there are the profiles for the individual programs that I called as subprofiles above. Most of the links are defined here.

class Bash(Profile):
    def generate(self):
        opt(prefix=".")
        link("bashrc", "inputrc")
        link("bashrc", "inputrc", directory="/root")
        link("bash_profile", name=".bash_profile")
        opt(prefix=".bash_")
        link("sensitiverc", "basicrc", "systemrc")
        link("sensitiverc", "basicrc", "systemrc", directory="/root")
        link("customrc", optional=True)


class Zsh(Profile):
    def generate(self):
        link("zshrc", prefix=".")
        link("zsh_profile", name=".zprofile")
        opt(prefix=".zsh_")
        link("sensitiverc", "basicrc", "systemrc")
        link("customrc", optional=True)


class Git(Profile):
    def generate(self):
        link("gitconfig", prefix=".")
        link("gitconfig_system", name="/etc/gitconfig")


class I3(Profile):
    def generate(self):
        subprof("Rofi", "Polybar")

        cd(".config/i3")
        link(merge("i3config", ["i3-keybindings", "i3-settings", "i3-autostart"]), name="config")
        link("lock.sh", "lock.png")

        link("dunstrc", directory="../dunst")

        cd("$HOME")
        link("wallpaper.sh")
        link("compton.conf", prefix=".")


class Rofi(Profile):
    def generate(self):
        # Fing all configs with the naming schema "rofi-*.rasi" and link all of them
        # but strip away "rofi-" of each filename
        cd(".config/rofi")
        links("rofi-(.+\.rasi)", replace=r"\1")


class Polybar(Profile):
    def generate(self):
        cd(".config/polybar")
        # Strip away "polybar" from each filename
        link("polybarconfig", "polybarlaunch.sh", replace_pattern="polybar(.+)", replace=r"\1")
        link("pkg.sh")


class Vim(Profile):
    def generate(self):
        # Link all vim configs
        links("g?vimrc", prefix=".")
        cd(".vim")
        links("\w+\.vim")

        # Documentation
        link("mydoc.txt", directory="doc")

        # Link all Snippets
        links(".+\.snippets", directory="UltiSnips")

        # Link all Spellfiles
        links(".+\..+\.add(\.spl)?", directory="spell")

        # Configs loaded after plugins
        link("vim-gitgutter.vim", directory="after/plugin")
        link("vim-illuminate.vim", directory="after/plugin")


class Radicale(Profile):
    def generate(self):
        cd(".config")
        link("radicale.conf", name="radicale/config")
        link("radicale_log.conf", name="radicale/log_config")
        link("radicale.service", directory="systemd/user")

You could argue that some programs doesn’t need an extra profile like Polybar and Rofi because those would have fit in I3 as well. It’s fine to do so, however by encapsulating their configs in their own profile you increase the re-usability of a configuration.