Skip to content

geluk/caerbannog

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

71 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Caerbannog

Caerbannog makes it easy to write declarative Python scripts to automatically configure your local user accounts.

Example

Create a configuration script

First, create a configuration script to describe the various types of devices on which you want to configure your user account:

./configure.py:

from caerbannog.prelude import *

target("laptop").depends_on("windows")
target("desktop").depends_on("archlinux")
target("work-laptop").depends_on("windows").has_roles("vscode")

target("windows").depends_on("common").has_roles("powershell", "windows-terminal")
target("archlinux").depends_on("common").has_roles("zsh", "bspwm", "vim")

target("common").has_roles("git")

commit()

Create role files

Now create roles which describe applications or configurations that you want to deploy. For instance, for git, you may want to install the package and create a .gitconfig and global .gitignore in your home directory:

roles/git/role.py:

from caerbannog.roles.prelude import *

def configure():
    Do(
        Package("git").is_installed(),
        File(home_dir(".gitconfig")).has_template("gitconfig.j2"),
        File(home_dir(".gitignore")).has_lines(".vscode"),
    )

roles/git/gitconfig.j2:

[core]
	hooksPath = "~/.githooks"
[user]
	name = Edsger Dijkstra
	email = dijkstra@novigrad.rd
[fetch]
	prune = true
[pull]
	rebase = true

Run it!

Now run the script to bring your machine to its desired state:

$ python configure.py apply laptop

All actions that are taken and changes that are made are clearly reported: image

How does it work?

Caerbannog allows you to write code that specifies what your system should look like. For instance, you can declare that the file .gitignore in your home directory has .vscode as its content in the following manner:

File(home_dir(".gitignore")).has_lines(".vscode")

If the file exists in your home directory, and it has the content you specified, no action will be taken. If it is not present, it will be created. If it exists, but its content differs, it will be modified.

To keep related assertions together, you can group them by creating roles, and placing all related assertions within those roles. In this case, it makes sense to create a git role which also asserts that your .gitignore has the right content, and that the git package is installed.

Often, you will want to configure multiple devices, and not always all in the same way. To allow for this, you can create targets. By assigning roles to targets, you can selectively apply roles to different devices.

These targets are specified in the configure.py script.

target("laptop").has_roles("git")

Now, if you run the script, you can apply the git role by selecting the laptop target:

python ./configure.py apply laptop

Extending Caerbannog

Caerbannog is intended to be easy to extend, which is done by writing plugins or by creating new subjects.

Plugins can be used to execute custom logic to set contextual information. For instance, you may want to read some system parameters and write these to global variables to make them available from your templates. This is easily done with a plugin.

Adding new subjects will allow you to make new types of changes to your system. By default, Caerbannog provides subjects such as File and Directory (to create, remove, or update files and directories), Package (to install or update packages using the system package manager), and SystemdService (to control services with systemd).

Subjects are classes inheriting from Subject. They expose public methods to let the user describe what should happen to this subject, e.g. File(.gitignore).has_content("__pycache__"). When these methods are executed, they will then register assertions, which describe desired properties for that subject. For instance, calling has_content('text') on a file implies two assertions for that file:

  1. The file exists.
  2. The content of the file is text.

These are represented by the IsFile and HasContent assertions. Assertions can be created by inheriting from Assertion, and must implement an apply() method. When executed, this method should check if the assertion holds, and if it does not, it should make the necessary changes to the system in order to make that assertion true. Using HasContent as an example, the assertion will:

  1. Read the file.
  2. Compare its current content against its desired content.
  3. If both match, no action is taken.
  4. If they do not match, a change is registered to write the desired content to the file.

An assertion itself should not directly make changes to the system. Instead, it should create an instance of a class deriving from Change, which holds all information necessary to perform the change. Changes can be queued by calling register_change().

About

Local account provisioning for mortals

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages