Welcome to the documentation for Astrality

Table of Contents

Astrality logo Astrality - A Dynamic Configuration File Manager PyPI package Travis-CI Documentation Status Coveralls gitter

TL;DR: Automatically deploy dotfiles. Grouped into modules with dynamic behaviour.

What does it do?

Astrality is a flexible tool for managing configuration files, inspired by GNU Stow and Ansible.

Let’s begin with a list of some of Astrality’s key features:

  • Manage and deploy configuration files according to a central YAML config file.
  • Group related configuration into modules.
  • Conditionally enable modules based on environment variables, OS, installed programs and shell commands.
  • Copy and/or symlink files.
  • Execute shell commands.
  • Compile Jinja2 templates templates to target destinations.
  • Dynamically manipulate context values used during jinja2 compilation.
  • Automatically re-deploy dotfiles when source content is modified.
  • Subscribe to pre-defined events, such as local daylight, and execute actions accordingly.
  • Fetch modules from GitHub.
  • Restore files created and/or overwritten by modules.

Take a look at the tutorial for managing a dotfile repository, or see the full documentation for all available functionality. Feel free to drop by our Gitter room when getting started.

Here is gif demonstrating how Astrality is used to:

  1. Automatically change the desktop wallpaper based on the sun’s position in the sky.
  2. Dynamically change the font size, and implicitly the bar height, of polybar.
  3. Simultaneously change the color scheme of alacritty, kitty, and polybar at the same time.
https://user-images.githubusercontent.com/10655778/36535609-934488ec-17ca-11e8-860e-4af5e1464997.gif

Getting started

Prerequisites

Astrality requires python 3.6 or greater. Check your version by running python --version.

Installation

astrality-git is published on the AUR for ArchLinux users. Otherwise, you can install Astrality using pip:

Create a new virtualenv for python 3.6 (or use your system python 3.6 if you prefer). Install Astrality from PyPI like so:

$ python3.6 -m pip install astrality

You should now be able to start astrality from your command line, but first, let us create an example configuration:

$ astrality --create-example-config

Take a look at the generated example configuration at ~/.config/astrality. Now start astrality:

$ astrality
Configuration and further documentation

I recommend taking a look at the full documentation of Astrality hosted at Read the Docs.

Configuration

The Astrality configuration directory

The configuration directory for astrality is determined in the following way:

  • If $ASTRALITY_CONFIG_HOME is set, use that path as the configuration directory, else…
  • If $XDG_CONFIG_HOME is set, use $XDG_CONFIG_HOME/astrality, otherwise…
  • Use ~/.config/astrality.

The resulting directory path can be displayed by running:

$ astrality --help
usage: Astrality ...
...
The location of Astralitys configuration directory is:
"/home/jakobgm/.dotfiles/config/astrality".
...

This directory path will be referred to as $ASTRALITY_CONFIG_HOME in the rest of the documentation.

The Astrality configuration files

There are three configuration files of importance in $ASTRALITY_CONFIG_HOME:

astrality.yml
Global configuration options for Astrality.
modules.yml
Here you define modules you want to use.
context.yml
Context values used for placeholder substitution in compiled templates.

If $ASTRALITY_CONFIG_HOME/astrality.yml does not exist, an example configuration directory will be used instead.

You can also copy over this example configuration directory as a starting point for your configuration by running:

$ astrality --create-example-config
Copying over example config directory to "/home/example_username/.config/astrality".

You should now edit astrality.yml, modules.yml, and context.yml to fit your needs.

The configuration file syntax

Astrality’s configuration files uses the YAML format. The syntax should be relatively self-explanatory when looking at the example configuration. If you still want a basic overview, take a look at the Ansible YAML syntax documentation for a quick primer.

Command substitution in configuration files

Astrality’s configuration files are themselves templates that are compiled and interpreted at startup. Using templating features in astrality configuration files is usually unnecessary.

But sometimes it is useful to insert the result of a shell command within a configuration file, such as “context.yml”. You can use command substitutions in order to achieve this:

  • Command substitution:

    {{ 'some_shell_command' | shell }} is replaced with the standard output resulting from running some_shell_command in a bash shell.

    You can set a timeout and/or fallback value for command substitutions. See the documentation for the shell filter.

Note

Shell commands are always executed from the same directory as the file which contains the command substitution.

If you need to refer to paths outside this directory, you can use absolute paths, e.g. {{ 'cat ~/.home_directory_file' | shell }}.

Astrality configuration options

Global Astrality configuration options are specified in astrality.yml within a dictionary named astrality, i.e.:

# Source file: $ASTRALITY_CONFIG_HOME/astrality.yml
astrality:
    hot_reload_config: true
    startup_delay: 10

Avalable configuration options:

hot_reload_config:

Default: false

If enabled, Astrality will watch for modifications to astrality.yml, modules.yml, and context.yml.

When one of these are modified, Astrality will perform all exit actions in the old configuration, and then all startup actions from the new configuration.

Ironically requires restart if enabled.

Useful for quick feedback when editing your configuration.

startup_delay:

Default: 0

Delay Astrality on startup, given in seconds.

Useful when you depend on other startup scripts before Astrality startup, such as reordering displays.

Where to go from here

What you should read of the documentation from here on depends on what you intend to solve by using Astrality. The most central concepts are:

  • Templating explains how to write configuration file templates.
  • Modules specify which templates to compile, when to compile them, and which commands to run after they have been compiled.
  • Event listeners define types of events which modules can listen to and change their behaviour accordingly.

These concepts are relatively interdependent, and each documentation section assumes knowledge of concepts explained in earlier sections. If this is the first time you are reading this documentation, you should probably just continue reading the documentation in chronological order.

Templating

Template files

Templates can be of any file type, named whatever you want, and placed at any desirable path. If you want to write a template for a file named “example.conf” it is recommended that you name it “template.example.conf”.

Context

When you write templates, you use {{ placeholders }} which Astrality replaces with values defined in so-called context sections defined in $ASTRALITY_CONFIG_HOME/context.yml.

Here is an example which defines context values in “context.yml”:

# $ASTRALITY_CONFIG_HOME/context.yml

machine:
    user: jakobgm
    os: linux
    hostname: hyperion

fonts:
    1: FuraCode Nerd Font
    2: FuraMono Nerd Font

Warning

Context keys (anything left of a colon) can only consist of ASCII letters, numbers and underscores. No spaces are allowed.

Inserting context variables into your templates

You should now be able to insert context values into your templates. You can refer to context variables in your templates by using the syntax {{ context_section.variable_name }}.

Using the contexts defined above, you could write the following template:

font-type = '{{ fonts.1 }}'
home-directory = /home/{{ machine.user }}
machine-name = {{ machine.hostname }}

When Astrality compiles your template the result would be:

font-type = 'FuraCode Nerd Font'
home-directory = /home/jakobgm
machine-name = hyperion

Hint

You can create arbitrarily nested structures within context sections. For instance:

cosmetics:
    fonts:
        1:
            family: FuraCode
            font_size: 13
        2:
            family: FuraMono
            font_size: 9

And refer to those nested variables with “dotted” syntax {{ cosmetics.fonts.1.family }}.

The env context

Astrality automatically inserts a context section at runtime named env. It contains all your environment variables. You can therefore insert environment variables into your templates by writing:

{{ env.ENVIRONMENT_VARIABLE_NAME }}
Undefined context values

When you refer to a context value which is not defined, it will be replaced with an empty string, and logged as a warning in Astrality’s standard output.

Default fallback context values

Sometimes you want to refer to context variables in your templates, but you want to insert a fallback value in case the context variable is not defined at compile time. This is often the case when referring to environment variables. Defining a fallback value is easy:

{{ env.ENVIRONMENT_VARIABLE_NAME or 'defualt value' }}
Integer placeholder resolution

There exists another way to define fallback values, which sometimes is much more useful.

Let’s define context values with integer names:

# $ASTRALITY_CONFIG_HOME/context.yml

fonts:
    1: FuraCode Nerd Font
    2: FuraMono Nerd Font

You can now write the following template:

primary-font = '{{ fonts.1 }}'
secondary-font = '{{ fonts.2 }}'
tertiary-font = '{{ fonts.3 }}'

And it will be compiled to:

primary-font = 'FuraCode Nerd Font'
secondary-font = 'FuraMono Nerd Font'
tertiary-font = 'FuraMono Nerd Font'

With other words, references to non-existent numeric context identifiers are replaced with the greatest available numeric context identifier at the same indentation level.

Hint

This construct can be very useful when you are expecting to change the underlying context of templates. Defining font types and color schemes using numeric identifiers allows you to switch between themes which define a different number of fonts and colors to be used!

Advanced templating

Astrality templating uses Jinja2 under the hood. If you want to apply more advanced templating techniques than the ones described here, you can use the extended templating features available in the Jinja2 templating engine. Visit Jinja2’s templating documentation for more information.

Useful constructs include:

Filters:
For manipulating context variables before insertion.
Template inheritance:
For reuse of templates with common sections.
Iterating over context values:
For using both the context name and value in configuration files.
Conditionals:
For only including template content if some conditions(s) are satisfied.
The shell filter

Astrality provides an additional shell template filter in addition to the standard Jinja2 filters. The syntax is:

{{ 'shell command' | shell }}

Note

Shell commands are run from the directory which contains the configuration for the template compilation, most often $ASTRALITY_CONFIG_HOME. If you need to refer to paths outside this directory, you can use absolute paths, e.g. {{ 'cat ~/.bashrc' | shell }}.

You can specify a timeout for the shell command given in seconds:

{{ 'shell command' | shell(5) }}

The default timeout is 2 seconds.

To provide a fallback value for functions that time out or return non-zero exit codes, do:

{{ 'shell command' | shell(1.5, 'fallback value') }}

Caution

The quotes around the shell command are important, since if you ommit the quotes, you end up refering to a context value instead. Though, this can be done intentionally when you have defined a shell command in a context variable.

How to compile templates

Now that you know how to write Astrality templates, you might wonder how to actually compile these templates. You can instruct Astrality to compile templates by defining a module in “$ASTRALITY_CONFIG_HOME/modules.yml”. More on this on the next page of this documentation, but here is a simple example:

Let us assume that you have written the following template:

# Source: $ASTRALITY_CONFIG_HOME/templates/some_template

current_user={{ host.user }}

Where you want to replace {{ host.user }} with your username. Let us define the context value used for insertion in “$ASTRALITY_CONFIG_HOME/context.yml”:

# Source: $ASTRALITY_CONFIG_HOME/context.yml

host:
    user: {{ env.USER }}

In order to compile this template to $XDG_CONFIG_HOME/config.ini we write the following module, which will compile the template on Astrality startup:

# Source: $ASTRALITY_CONFIG_HOME/modules.yml

my_module:
    compile:
        - content: templates/template
          target: $XDG_CONFIG_HOME/config.ini

Now we can compile the template by starting Astrality:

$ astrality

The result should be:

# Source: $XDG_CONFIG_HOME/config.ini

current_user=yourusername

This is probably a bit overwhelming. I recommend to just continue to the next page to get a more gentle introduction to these concepts.

Modules

What are modules?

Tasks to be performed by Astrality are grouped into so-called modules. These modules are used to define:

Action blocks:
A grouping of actions which is supposed to be performed at a specific time, such as “on Astrality startup”, “on Astrality exit”, or “on event”.
Actions
Tasks to be performed by Astrality, for example compiling templates or running shell commands.
Event listeners
Event listeners can listen to predefined events and trigger the “on event” action block of the module.

You can easily enable and disable modules, making your configuration more modular.

How to define modules

There are two types of places where you can define your modules:

Directly in $ASTRALITY_CONFIG_HOME/modules.yml:
Useful if you don’t have too many modules, and you want to keep everything easily accessible in one file.
In a file named modules.yml within a modules directory:

Useful if you have lots of modules, and want to separate them into separate directories with common responsibilities.

See the documentation for external modules for how to define modules this way.

You can use templating features in modules.yml, since they are compiled at startup with all context values defined in all context.yml files.

Hint

A useful configuration structure is to define modules with “global responsibilities” in $ASTRALITY_CONFIG_HOME/modules.yml, and group the remaining modules in seperate module directories by their categorical responsibilites (for example “terminals”).

Here “global responsibility” means having the responsibility to satisfy the dependecies of several other modules, such as defining context values used in several templates, creating directories, or installing common dependencies.

Module definition syntax

Modules are formated as separate dictionaries placed at the root indentation level of “modules.yml”. The key used will become the module name.

The simplest module, with no specific behaviour, is:

# Source: $ASTRALITY_CONFIG_HOME/modules.yml

my_module:
    enabled: true

Astrality skips parsing any modules which contain the option enabled: false. The default value of enabled is true, so you do not have to specify it.

Module dependencies

You can specify conditionals that must be satisfied in order to consider a module enabled. It can be useful if a module requires certain dependencies in order to work correctly

You can specify module requirements by setting the module option requires equal to a list of dictionaries containing one, or more, of the following keywords:

env:
Environment variable specified as a string. The environment variable must be set in order to consider the module enabled.
installed:
Program name specified as a string. The program name must be invokable through the command line, i.e. available through the $PATH environment variable. You can test this by typing command -v program_name in your shell.
shell:

Shell command specified as a string. The shell command must return a 0 exit code (which defines success), in order to consider the module enabled.

If the shell command uses more than 1 second to return, it will be considered failed. You can change the default timeout by setting the requires_timeout configuration option.

You can also override the default timeout on a case-by-case basis by setting the timeout key to a numeric value (in seconds).

module:

Module dependent on other module(s), specified with the same name syntax as with enabled_modules.

If a module is missing one or more module dependencies, it will be disabled, and an error will be logged.

All specified dependencies must be satisfied in order to enable the module.

For example, if your module depends on the docker shell command, another module named docker-machine, the environment variable $ENABLE_DOCKER being set, and “my_docker_container” existing, you can check this by setting the following requirements:

# Souce: $ASTRALITY_CONFIG_HOME/modules.yml

docker:
    requires:
        - installed: docker
        - module: docker-machine
        - env: ENABLE_DOCKER
        - shell: '[ $(docker ps -a | grep my_docker_container) ]'
          timeout: 10 # seconds

Hint

requires can be useful if you want to use Astrality to manage your dotfiles. You can use module dependencies in order to only compile configuration templates to their respective directories if the dependent application is available on the system. This way, Astrality becomes a “conditional symlinker” for your dotfiles.

Action blocks

When you want to assign tasks for Astrality to perform, you have to define when to perform them. This is done by defining those actions in one of five available action blocks.

on_setup:

Tasks to be performed only once and never again. Can be used for setting up dependencies.

Executed actions are written to $XDG_DATA_HOME/astrality/setup.yml, by default $HOME/.local/share. Execute astrality --reset-setup module_name if you want to re-execute a module’s setup actions during the next run.

on_startup:

Tasks to be performed when Astrality first starts up. Useful for compiling templates that don’t need to change after they have been compiled.

Actions defined outside action blocks are considered to be part of this block.

on_exit:
Tasks to be performed when you kill the Astrality process. Useful for cleaning up any unwanted clutter.
on_event:
Tasks to be performed when the specified module event listener detects a new event. Useful for dynamic behaviour, periodic tasks, and templates that should change during runtime. The on_event block will never be triggered when no module event listener is defined. More on event listeners follows in the next section.
on_modified:

Tasks to be performed when specific files are modified on disk. You specify a set of tasks to performed on a per-file-basis. Useful for quick feedback when editing template files.

Caution

Only files within $ASTRALITY_CONFIG_HOME/**/* are observed for modifications.

If this is an issue for you, please open a GitHub issue!

Demonstration of module action blocks:

module_name:
    ...startup actions (option 1)...

    on_setup:
        ...setup actions...

    on_startup:
        ...startup actions (option 2)...

    on_event:
        ...event actions...

    on_exit:
        ...shutdow actions...

    on_modified:
        some/file/path:
            ...some/file/path modified actions...

Note

On Astrality startup, the on_startup event will be triggered, but not on_event. The on_event event will only be triggered when the event listener detects a new event after Astrality startup.

Actions

Actions are tasks for Astrality to perform, and are placed within action blocks in order to specify when to perform them. These are the available action types:

import_context:
Import a context section from a YAML formatted file. context variables are used as replacement values for placeholders in your templates. See context for more information.
compile:
Compile a specific template or template directory to a target path.
copy:
Copy a specific file or directory to a target path.
symlink:
Create symbolic link(s) pointing to a specific file or directory.
stow:
Combination of compile + copy or compile + symlink, bisected based on filename pattern of files within a content directory.
run:
Execute a shell command, possibly referring to any compiled template and/or the last detected event defined by the module event listener.
trigger:
Perform all actions specified within another action block. With other words, this action appends all the actions within another action block to the actions already specified in the action block. Useful for not having to repeat yourself when you want the same actions to be performed during different events.
Context imports

The simplest way to define context values is to just define their values in $ASTRALITY_CONFIG_HOME/context.yml. Those context values are available for insertion into all your templates.

But you can also import context values from arbitrary YAML files. Among other use cases, this allows you to:

  • Split context definitions into separate files in order to clean up your configuration.
  • Combine context imports with on_event blocks in order to dynamically change how templates compile. This allows quite complex behaviour.

Context imports are defined as a dictionary, or a list of dictionaries, if you need several imports. Use the import_context keyword in an action block of a module.

This is best explained with an example. Let us create a color schemes file:

# Source file: $ASTRALITY_CONFIG_HOME/modules/color_schemes/color_schemes.yml

gruvbox_dark:
    background: 282828
    foreground: ebdbb2

gruvbox_light:
    background: fbf1c7
    foreground: 3c3836

Then let us import the gruvbox dark color scheme into the “colors” context section:

# Source file: $ASTRALITY_CONFIG_HOME/modules.yml

color_scheme:
    on_startup:
        import_context:
            from_path: modules/color_schemes/color_schemes.yml
            from_section: gruvbox_dark
            to_section: colors

This is functionally equivalent to writing the following global context file:

# Source file: $ASTRALITY_CONFIG_HOME/context.yml

colors:
    background: 282828
    foreground: ebdbb2

Hint

You may wonder why you would want to use this kind of redirection when definining context variables. The advantages are:

  • You can now use {{ colors.foreground }} in all your templates instead of {{ gruvbox_dark.foreground }}. Since your templates do not know exactly which color scheme you are using, you can easily change it in the future by editing only one line in modules.yml.
  • You can use import_context in a on_event action block in order to change your colorscheme based on the time of day. Perhaps you want to use “gruvbox light” during daylight, but change to “gruvbox dark” after dusk?

The available attributes for import_context are:

from_path:
A YAML formatted file containing context sections.
from_section: [Optional]

Which context section to import from the file specified in from_path.

If none is specified, all sections defined in from_path will be imported.

to_section: [Optional]

What you want to name the imported context section. If this attribute is omitted, Astrality will use the same name as from_section.

This option will only have an effect if from_section is specified.

Compile templates

Template compilations are defined as a dictionary, or a list of dictionaries, under the compile keyword in an action block of a module.

Each template compilation action has the following available attributes:

content:

Path to either a template file or template directory.

If content is a directory, Astrality will compile all templates recursively to the target directory, preserving the directory hierarchy.

target: [Optional]

Default: Temporary file created by Astrality.

Path which specifies where to put the compiled template.

You can skip this option if you do not care where the compiled template is placed, and what it is named. You can still use the compiled result by writing {template_path} in the rest of your module. This placeholder will be replaced with the absolute path of the compiled template. You can for instance refer to the file in a shell command.

include [Optional]

Default: '(.+)'

Regular expression defining which filenames that are considered to be templates. Useful when content is a directory which contains non-template files. By default Astrality will try to compile all files.

If you specify a capture group, astrality will use the captured string as the target filename. For example, templates: 'template\.(.+)' will match the file “template.kitty.conf” and rename the target to “kitty.conf”.

Hint

You can test your regex here. Astrality uses the capture group with the greatest index.

permissions: [Optional]

Default: Same permissions as the template file.

The file mode (i.e. permission bits) assigned to the compiled template. Given either as a string of octal permissions, such as '755', or as a string of symbolic permissions, such as 'u+x'. This option is passed to the linux shell command chmod. Refer to chmod’s manual for the full details on possible arguments.

Note

The permissions specified in the permissions option are applied on top of the default permissions copied from the template file.

For example, if the template’s permissions are rw-r--r-- (644) and the value of 'ug+x' is supplied for the permissions option, the 644 permissions will first be copied to the resulting compiled file and then chmod ug+x will be applied on top of that to give a resulting permission on the file of rwxr-xr-- (754).

If an invalid value is supplied for the permissions option, only the default permissions are copied to the compiled file.

Here is an example:

# Source file: $ASTRALITY_CONFIG_HOME/modules.yml

desktop:
    compile:
        - content: modules/scripts/executable.sh.template
          target: ${XDG_CONFIG_HOME}/bin/executable.sh
          permissions: 0o555
        - content: modules/desktop/conky_module.template

    run:
        - shell: conky -c {modules/desktop/conky_module.template}
        - shell: polybar bar

Notice that the shell command conky -c {modules/desktop/conky_module.template} is replaced with something like conky -c /tmp/astrality/compiled.conky_module.template.

Note

All relative file paths in modules are interpreted relative to the directory which contains “module.yml” which defines the module.

Copy files

You can copy a file or directory to a target destination. Directories will be recursively copied, leaving non-conflicting files at the target destination intact. The copy action have the following available parameters.

content:

Where to copy from, with other words a path to a file or directory with existing content to be copied.

If content is a directory, Astrality will create an identical directory hierarchy at the target directory path and recursively copy all files.

target:
A path specifying where to copy to. Any non-conflicting files at the target destination will be left alone.
include [Optional]

Default: '(.+)'

Regular expression restricting which filenames that should be copied. By default Astrality will try to copy all files.

If you specify a capture group, astrality will use the captured string as the name for the copied file. For example, include: 'copy\.(.+)' will copy the file “copy.binary.blob” and rename the copy to “binary.blob”.

permissions: [Optional]

Default: Same permissions as the original file(s).

See compilation permissions for more information.

Stow a directory

Often you want to:

  1. Move all content from a directory in your dotfile repository to a specific target directory, while…
  2. Compiling any template according to a consistent naming scheme, and…
  3. Symlink or copy the remaining files which are not templates.

The stow action type allows you to do just that! Stow has the following available parameters:

content:
Path to a directory of mixed content, i.e. both templates and non-templates.
target:
Path to directory where processed content should be placed. Templates will be compiled to target, and the remaining files will be treated according to the non_templates parameter.
templates: [Optional]

Default: 'template\.(.+)'

Regular expression restricting which filenames that should be compiled as templates. By default, Astrality will only compile files named “template.*” and rename the compilation target to “*”.

See the compile action include parameter for more information.

non_templates: [Optional]

Default: 'symlink'

Accepts: symlink, copy, ignore

What to do with files that do not match the templates regex.

permissions: [Optional]

Default: Same permissions as the original file(s).

See compilation permissions for more information.

Here is an example module which compiles all files matching the glob $XDG_CONFIG_HOME/**/*.t, and places the compiled template besides the template, but without the file extension “.t”. It leaves all other files alone:

# Source file: $ASTRALITY_CONFIG_HOME/modules.yml

dotfiles:
    stow:
        content: $XDG_CONFIG_HOME
        target: $XDG_CONFIG_HOME
        templates: '(.+)\.t'
        non_templates: ignore
Run shell commands

You can instruct Astrality to run an arbitrary number of shell commands when different action blocks are triggered. Each shell command is specified as a dictionary. The shell command is specified as a string keyed to shell. Place the commands within a list under the run option of an action block. See the example below.

You can place the following placeholders within your shell commands

{event}:
The last event detected by the module event listener.
{template_path}:
Replaced with the absolute path of the compiled version of the template placed at the path template_path.

Example:

weekday_module:
    event_listener:
        type: weekday

    on_startup:
        run:
            - shell: 'notify-send "You just started Astrality, and the day is {event}"'

    on_event:
        run:
            - shell: 'notify-send "It is now midnight, have a great {event}! I'm creating a notes document for this day."'
            - shell: 'touch ~/notes/notes_for_{event}.txt'

    on_exit:
        run:
            - shell: 'echo "Deleting today's notes!"'
            - shell: 'rm ~/notes/notes_for_{event}.txt'

You can actually place these placeholders in any action type’s string values. Placeholders are replaced at runtime every time an action is triggered.

Warning

template/path must be compiled when an action type with a {template/path} placeholder is executed. Otherwise, Astrality does not know what to replace the placeholder with, so it will leave it alone and log an error instead.

Trigger action blocks

From one action block you can trigger another action block by specifying a trigger action.

Each trigger option is a dictionary with a mandatory block key, on of on_startup, on_event, on_exit, or on_modified. In the case of setting block: on_modified, you have to specify an additional path key indicating which file modification block you want to trigger.

An example of a module using trigger actions:

module_using_triggers:
     event_listener:
         type: weekday

     on_startup:
         run:
             - shell: startup_command

         trigger:
             - block: on_event

     on_event:
         import_context:
             - from_path: contexts/A.yml
               from_section: '{event}'
               to_section: a_stuff

         trigger:
             - block: on_modified
               path: templates/templateA

     on_modified:
         templates/A.template:
             compile:
                 content: templates/A.template

             run: shell_command_dependent_on_templateA

This is equivalent to writing the following module:

module_using_triggers:
    event_listener:
        type: weekday

    on_startup:
        import_context:
            - from_path: contexts/A.yml
              from_section: '{event}'
              to_section: a_stuff

        compile:
            content: templates/templateA

        run:
            - shell: startup_command
            - shell: shell_command_dependent_on_templateA

    on_event:
        import_context:
            from_path: contexts/A.yml
            from_section: '{event}'
            to_section: a_stuff

        compile:
            content: templateA

        run:
            - shell: shell_command_dependent_on_templateA

    on_modified:
        templates/templateA:
            compile:
                content: templates/templateA

            run:
                - shell: shell_command_dependent_on_templateA

Hint

You can use trigger: on_event in the on_startup block in order to consider the event detected on Astrality startup as a new event.

The trigger action can also help you reduce the degree of repetition in your configuration.

The execution order of module actions

The order of action execution is as follows:

  1. context_import for each module.
  2. symlink for each module.
  3. copy for each module.
  4. compile for each module.
  5. stow for each module.
  6. run for each module.

Modules are iterated over from top to bottom such that they appear in modules.yml. This ensures the following invariants:

  • When you compile templates, all context imports have been performed, and are available for placeholder substitution.
  • When you run shell commands, all (non-)templates have been compiled/copied/symlinked, and are available for reference.

Global configuration options for modules

Global configuration options for all your modules are specified in $ASTRALITY_CONFIG_HOME/astrality.yml within a dictionary named modules at root indentation, i.e.:

# Source file: $ASTRALITY_CONFIG_HOME/astrality.yml

modules:
    option1: value1
    option2: value2
    ...

Available modules configuration options:

requires_timeout:

Default: 1

Determines how long Astrality waits for module requirements to exit successfully, given in seconds. If the requirement times out, it will be considered failed.

Useful when requirements are costly to determine, but you still do not want them to time out.

run_timeout:

Default: 0

Determines how long Astrality waits for module run actions to exit, given in seconds.

Useful when you are dependent on shell commands running sequantially.

reprocess_modified_files:

Default: false

If enabled, Astrality will watch for file modifications in $ASTRALITY_CONFIG_HOME. All files that have been compiled or copied to a destination will be recompiled or recopied if they are modified.

Hint

You can have more fine-grained control over exactly what happens when a file is modified by using the on_modified module event. This way you can run shell commands, import context values, and compile arbitrary templates when specific files are modified on disk.

Caution

At the moment, Astrality only watches for file changes recursively within $ASTRALITY_CONFIG_HOME.

modules_directory:

default: modules

Where Astrality looks for externally defined configurations directories.

enabled_modules:

default:

enabled_modules:
    - name: '*'
    - name: '*::*'

A list of modules which you want Astrality to use. By default, Astrality enables all defined modules.

Specifying enabled_modules allows you to define a module without necessarily using it, making configuration switching easy.

Module defined in “$ASTRALITY_CONFIG_HOME/modules.yml”:
name: name_of_module
Module defined in “<modules_directory>/dir_name/modules.yml”:
name: dir_name::name_of_module
Module defined at “github.com/<user>/<repo>/blob/master/modules.yml”:
name: github::<user>/<repo>::name_of_module

You can also use wildcards when specifying enabled modules:

  • name: '*' enables all modules defined in: $ASTRALITY_CONFIG_HOME/modules.yml.
  • name: 'text_editors::* enables all modules defined in: $ASTRALITY_CONFIG_HOME/<modules_directory>/text_editors/modules.yml.
  • name: '*::* enables all modules defined in: $ASTRALITY_CONFIG_HOME/<modules_directory>/*/modules.yml.

Module subdirectories

You can define “external modules” in files named modules.yml placed within separate subdirectories of your modules directory. You can also place context.yml within these directories, and the context values will become available for compilation in all templates.

Astrality compiles enabled modules.yml files with context from all enabled context.yml files before parsing it. This allows you to modify the behaviour of modules based on context, useful if you want to offer configuration options for modules.

  1. Define your modules in $ASTRALITY_CONFIG_HOME/<modules_directory>/directory/modules.yml.
  2. Enable modules from this config file by appending name: directory::module_name to enabled_modules. Alternatively, you can enable all modules defined in a module directory by appending name: directory::* instead.

By default, all module subdirectories are enabled.

Context values defined in context.yml have preference above context values defined in module subdirectories, allowing you to define default context values, while still allowing others to override these values.

Caution

All relative paths and shell commands in external modules are interpreted relative to the external module directory, not $ASTRALITY_CONFIG_HOME. This way it is more portable between different configurations.

GitHub modules

You can share a module directory with others by publishing the module subdirectory to GitHub. Just define modules.yml at the repository root, i.e. where .git exists, and include any dependent files within the repository.

Others can fetch your module by appending name: github::<your_github_username>/<repository> to enabled_modules.

For example enabling the module named module_name defined in modules.yml in the repository at https://github.com/username/repository:

modules:
    enabled_modules:
        - name: github::username/repository::module_name

Astrality will automatically fetch the module on startup and place it within $ASTRALITY_CONFIG_HOME/<modules_directory>/username/repository. If you want to automatically update the GitHub module, you can specify autoupdate: true:

modules:
    enabled_modules:
        - name: github::username/repository::module_name
          autoupdate: true

If module_name is not specified, all modules will be enabled:

modules:
    enabled_modules:
        - name: github::username/repository
          autoupdate: true

Event listeners

What are event listeners?

Event listeners provide you with the ability to keep track of certain events, and change module behaviour accordingly. A short summation of event listeners:

  1. Event listeners are specified on a per-module-basis.
  2. There are different types of event listeners.
  3. Event listeners determine exactly when the actions you specify within a module’s on_event action block are executed.
  4. Event listeners are optional, you can write valid modules without specifying one. Actions specified within the on_event action block will never be executed when no module event listener is specified.
  5. Event listeners provide the module with the {event} placeholder when specifying actions. It is replaced by the current event at runtime. The replacement value is specific for that specific event listener’s type, and dynamically changes according to rules set by the event listener.

What are event listeners used for?

Event listeners provide you with the tools needed for dynamic module behaviour. Sometimes you want a module to execute different shell commands, and/or compile templates with different context values, depending on exactly when those actions are performed.

How to set a module event listener

Module event listeners are defined within the module block it is supposed to provide functionality for. The syntax is as follows:

some_dynamic_module:
    event_listener:
        type: type_of_event_listener

        option1: whatever
        option2: something
        ...

Most event listeners provide you with additional options in order to tweak their behaviour. These are specified at the same indentation level as the event listener type.

Events

Module event listeners keep track of some type of event and trigger the on_event action block whenever it detects a new event. You can refer to the current event in your module actions with the {event} placeholder.

Caution

When you use placeholders, you must take care that the placeholder is not interpreted as a YAML dictionary instead of a string. The following will not work as intended:

some_option: {event}

This is interpreted as the dictionary {'event': None}. In this case you must mark the option explicitly as a string:

some_option: '{event}'

Using quotes is not necessary when the placeholder is part of a greater string. This works:

some_option: echo {event}
An example using events

The use of events in modules is best explained with an example. Please take a look at this example using the weekday event listener in order to set a separate desktop wallpaper for each day of the week.

Event listener types

Here is a list of all available Astrality module event listeners and their configuration options. If what you need is not available, feel free to open an issue with a event listener request!

Daylight
Description
Keeps track of the daylight at a specific location, i.e. if the sun is above the horizon or not.
Specifier
type: daylight
Events
day, night
Configuration options
Option Default Description
latitude 0 Latitude coordinate point of your location.
longitude 0 Longitude coordinate point of your location.
elevation 0 Height above sea level at your location.

These coordinates can be obtained from this website.

Example configuration

daylight_module:
    event_listener:
        type: daylight

        latitude: 63.446827
        longitude: 10.421906
Solar
Description
Keeps track of the sun’s position in the sky at a given location.
Specifier
type: solar
Events
sunrise, morning, afternoon, sunset, night
Configuration options
Option Default Description
latitude 0 Latitude coordinate point of your location.
longitude 0 Longitude coordinate point of your location.
elevation 0 Height above sea level at your location.

These coordinates can be obtained from this website.

Example configuration

solar_module:
    event_listener:
        type: solar

        latitude: 63.446827
        longitude: 10.421906
Static
Description
An event listener which never changes its event. This is the default event listener for modules.
Specifier
type: static
Events
static

No configuration options are available for the static event listener.

Example configuration

static_module:
    ...
Time of day
Description
Keeps track of a specific time interval for each day of the week. Useful for tracking when you are at work.
Specifier
type: time_of_day
Events
on, off
Configuration options
Option Default Description
monday '09:00-17:00' The time of day that is considered ‘on’.
tuesday '09:00-17:00' The time of day that is considered ‘on’.
wednesday '09:00-17:00' The time of day that is considered ‘on’.
thursday '09:00-17:00' The time of day that is considered ‘on’.
friday '09:00-17:00' The time of day that is considered ‘on’.
saturday '' The time of day that is considered ‘on’.
sunday '' The time of day that is considered ‘on’.

Example configuration

european_tue_to_sat_work_week:
    event_listener:
        type: time_of_day
        monday: ''
        tuesday: '08:00-16:00'
        wednesday: '08:00-16:00'
        thursday: '08:00-16:00'
        friday: '08:00-16:00'
        saturday: '08:00-16:00'
Weekday
Description
Keeps track of the weekdays.
Specifier
type: weekday
Events
monday, tuesday, wednesday, thursday, friday, saturday, sunday

No configuration options are available for the weekday event listener.

Example configuration

weekday_module:
    event_listener:
        type: weekday
Periodic
Description
Keeps track of constant length time intervals.
Specifier
type: periodic
Events
0, 1, 2, 3, and so on…
Configuration options
Option Default Description
seconds 0 Number of seconds between each period.
minutes 0 Number of minutes between each period.
hours 0 Number of hours between each period.
days 0 Number of days between each period.

If the configured time interval is of zero length, Astrality uses hours: 1 instead.

Example configuration

periodic_module:
    event_listener:
        type: periodic
        hours: 8

Tutorial

Managing dotfiles with templates

It is relatively common to organize all configuration files in a “dotfiles” repository. How you structure such a repository comes down to personal preference. We would like to use the templating capabilities of Astrality without making any changes to our existing dotfiles hierarchy. This is relatively easy!

Let us start by managing the files located in $XDG_CONFIG_HOME, where most configuration files reside. The default value of this environment variable is “~/.config”. We will create an Astrality module which automatically detects files named “template.whatever”, and compile it to “whatever”. This way you can easily write new templates without having to add new configuration in order to compile them.

# ~/.config/astrality/modules.yml

dotfiles:
    compile:
        content: $XDG_CONFIG_HOME
        target: $XDG_CONFIG_HOME
        include: 'template\.(.+)'

Let us go through the module configuration step-by-step:

  • We use the compile action type, as we are only interested in compiling templates at the moment.
  • We set both the content and target to be $XDG_CONFIG_HOME, compiling any template to the same directory as the template.
  • We only want to compile template filenames which matches the regular expression template\.(.+).
  • The regex capture group in template\.(.+) specifies that everything appearing after “template.” should be used as the compiled target filename.

We can now compile all such templates within $XDG_CONFIG_HOME by running astrality from the shell. Before doing so, it is recommended to run astrality --dry-run to see which actions that will be performed.

But we would like to automatically recompile templates when we modify them or create new ones. You can achieve this by enabling reprocess_modified_files in astrality.yml:

# ~/.config/astrality/astrality.yml

config/modules:
    reprocess_modified_files: true

Astrality will automatically recompile any modified templates as long as it runs as a background process.

Let us continue by managing a more complicated dotfiles repository. Most people create a separate repository containing all their configuration files, not only $XDG_CONFIG_HOME. The repository is then cloned to something like ~/.dotfiles, the contents of which is symlinked or copied to separate locations, $HOME, $XDG_CONFIG_HOME, $/etc on so on. You can do all of this with Astrality.

For demonstration purposes, let us assume that the templates within “~/.dotfiles/home” should be compiled to “~”, and “~/.dotfiles/etc” to “/etc”, while non-templates should be symlinked instead. This combination of symlink and compile actions can be done with the stow action.

Move modules.yml and astrality.yml to the root of your dotfiles repository. Set export ASTRALITY_CONFIG_HOME=~/.dotfiles. Finally, modify the dotfiles module accordingly:

# ~/.dotfiles/modules.yml

dotfiles:
    stow:
        - content: home
            target: ~
            templates: 'template\.(.+)'
            non_templates: symlink

        - content: etc
            target: /etc
            templates: 'template\.(.+)'
            non_templates: symlink

templates: 'template\.(.+)' and non_templates: symlink are actually the default options for the stow action, so we could have skipped specifying them altogether. Alternatively, you can specify non_templates: copy.

You can now start to write all your configuration files as templates instead, using placeholders for secret API keys or configuration values that change between machines, and much much more.

A module using events

Let us explore the use of events with an example: we want to use a different desktop wallpaper for each day of the week.

The weekday event listener type keeps track of the following events: monday, tuesday, wednesday, thursday, friday, saturday, and sunday.

After having found seven fitting wallpapers, we name them according to the weekday we want to use them, and place them in $ASTRALITY_CONFIG_HOME/modules/weekday_wallpaper/:

$ ls -l $ASTRALITY_CONFIG_HOME/modules/weekday_wallpaper

monday.jpeg
tuesday.jpg
wednesday.png
thursday.tiff
friday.gif
saturday.jpeg
sunday.jpeg

Now we need to create a module with a weekday event listener in modules.yml:

weekday_wallpaper:
    event_listener:
        type: weekday

We also need a way of setting the desktop wallpaper from the shell. Here we are going to use the feh shell utility. Alternatively, on MacOS, we can use this script. After having installed feh, we can use it to set the appropriate wallpaper on Astrality startup:

weekday_wallpaper:
    event_listener:
        type: weekday

    on_startup:
        run:
            - shell: feh --bg-fill modules/weekday_wallpaper/{event}.*

Now Astrality will set the appropriate wallpaper on startup. We still have a small bug in our module. If you do not restart Astrality the next day, yesterday’s wallpaper will still be in use. We can fix this by changing the wallpaper every time the weekday changes by listening for the weekday event.

weekday_wallpaper:
    event_listener:
        type: weekday

    on_startup:
        run:
            - shell: feh --bg-fill modules/weekday_wallpaper/{event}.*

    on_event:
        run:
            - shell: feh --bg-fill modules/weekday_wallpaper/{event}.*

Or, alternatively, we can just trigger the on_startup action block when the event changes:

weekday_wallpaper:
    event_listener:
        type: weekday

    on_startup:
        run:
            - shell: feh --bg-fill modules/weekday_wallpaper/{event}.*

    on_event:
        trigger:
            - block: on_startup

Example configuration

Here is an example configuration of $ASTRALITY_CONFIG_HOME, which you can copy as a starting point by running astrality --create-example-config.

First the global configuration options:

$ASTRALITY_CONFIG_HOME/astrality.yml
astrality:
    # If hot_reload_config is enabled, modifications to this file automatically
    # runs:
    #       1) exit actions from the old configuration
    #       2) startup actions from the new configuration
    # Requires restart if enabled
    hot_reload_config: true

    # You can delay astrality on startup. The delay is given in seconds.
    startup_delay: 0


modules:
    # Modules can require successfull shell commands (non-zero exit codes) in
    # order to be enabled. You can specify the timeout for such checks, given
    # in seconds.
    requires_timeout: 1

    # Astrality can wait for shell commands to complete in their specified
    # order. You can set the number of seconds Astrality waits for the shell
    # commands to exit.
    run_timeout: 0

    # Modified templates can be automatically recompiled. This also includes
    # files that have been copied to a target destination.
    reprocess_modified_files: true

    # There are two possible ways to define modules. Either in this file, as
    # shown further below, or in separate external module directories within the
    # following specified directory, relatively interpreted as:
    # $ASTRALITY_CONFIG_HOME/modules
    modules_directory: modules

    # You enable modules by specifying the <name> of each module.
    # Modules defined in <modules_directory>/<subdirectory>/config.yml are
    # enabled by writing name: <subdirectory>::<name>
    #
    # '*' enables all modules in this file, '*::*' enables all modules defined
    # in subdirectories of <modules_directory>.
    enabled_modules:
        # Module defined in this file
        - name: polybar::*
        - name: terminals

        # All modules defined in <modules_directory>/solar_desktop/config.yml
        - name: solar_desktop::*

        # Module defined at https://github.com/jakobgm/color-schemes.astrality
        - name: github::jakobgm/color-schemes.astrality
          autoupdate: true  # Fetch new color schemes as they are added

Then some example modules:

$ASTRALITY_CONFIG_HOME/modules.yml
dotfiles:
    # This module automatically compiles all filenames within $XDG_CONFIG_HOME
    # prefixed with 'template.'. It removes the prefix for the compile target,
    # placing it in the same directory as the template.
    compile:
        content: $XDG_CONFIG_HOME
        target: $XDG_CONFIG_HOME
        include: 'template\.(.+)'


terminals:
    # By default, this module is not enabled, since it overwrites possibly
    # pre-existing configuration files. Enable it in config/modules.
    #
    # This module uses the color scheme context syntax from:
    # github::jakobgm/color-schemes.astrality
    # And the color scheme can be changed in context/color_schemes_config
    #
    # It makes it easy to change color schemes for all your terminals at the
    # same time.
    #
    # Terminals:
    # Alacritty: https://github.com/jwilm/alacritty
    # Kitty: https://github.com/kovidgoyal/kitty

    requires:
        - installed: 'alacritty'
        - installed: 'kitty'

    compile:
        - content: modules/terminals/alacritty.yml.template
          target: {{ env.XDG_CONFIG_HOME }}/alacritty/alacritty.yml
        - content: modules/terminals/kitty.conf.template
          target: {{ env.XDG_CONFIG_HOME }}/kitty/kitty.conf

Finally some useful context values to be used in templates:

$ASTRALITY_CONFIG_HOME/context.yml
host:
    # Here we define some context values which often change between host
    # computers, and are therefore practical to use in our templates.
    displays:
        # All displays defined here are used in the polybar module. It creates
        # one bar for each of the display handles, where the bar identifier is
        # the same as the display handle. This way you can start a polybar for
        # the primary screen by running:
        #     polybar --config {modules/polybar/config.template} HDMI2
        primary:
            handle: HDMI2
            dpi: 96

        secondary:
            handle: eDP1
            dpi: 96


    interfaces:
        wlan:
            # You can also use command substitution in order to insert the
            # standard output of a shell command into a configuration option.
            #
            # This is also used by the polybar template to point the
            # wireless-internet polybar module to the correct interface.
            handle: {{ 'iwconfig 2>/dev/null | grep -o "^\w*"' | shell }}

        ethernet:
            handle: eno0

    commands:
        # Here we define some commands that might change between hosts
        # with different stacks, i.e. systemd vs init, or wayland vs Xorg
        shutdown: systemctl poweroff -i
        reboot: systemctl reboot -i

        # In order to insert our global IP into a template, we can now do:
        # host.commands.global_ip | shell within placeholder delimiters
        global_ip: 'wget http://checkip.dyndns.org/ -O - -o /dev/null | cut -d: -f 2 | cut -d\< -f 1 | xargs'


fonts:
    # Here we define some context values for fonts that we want to use in
    # several different configurations, another common use case for context
    # values in templates.

    # You can use integer indexed variables in order to have fallback values.
    # If fonts:4/5/6 on so on is used in a template, but it is not
    # defined, it will be replaced with ast:fonts:3 instead. This is
    # useful when you dont want to assume how many fonts you want to use when
    # you write your templates.
    #
    # Here we define the main fonts used across our applications. Where 1
    # is the primary font, 2 the secondary font, and so on.

    # These fonts can be installed here: https://nerdfonts.com/
    1:
        name: FuraCode Nerd Font
        size: 8

    2:
        name: FuraCode Nerd Font Mono
        size: 8

    3:
        name: RobotoMono Nerd Font
        size: 8

    # We also add some configurations which are specific for some application
    # types.
    terminal:
        size: 10

    status_bar:
        size: 8


color_schemes_config:
    # These are context values used by the GitHub module:
    # github.com/jakobgm/color-schemes.astrality
    # See the README of this repository for more information.

    # Enable the following color scheme:
    enabled: gruvbox_dark

    # Import the color scheme into the following context section
    context_section: colors

Tips and Tricks

Configuration of other applications

i3wm

You probably want to automatically start Astrality on startup. Here is an example for those who use the i3 tiling window manager.

Add the following line to $XDG_CONFIG_HOME/i3/config:

exec --no-startup-id "astrality"
Compton

If you are using the compton compositor, and want to use the conky modules included in the example configuration, you should disable any shadows and dims which could be applied to the conky desktop modules. Here is an example compton configuration which you should place at $XDG_CONFIG_HOME/compton/compton.conf:

inactive-dim = 0.1;
shadow = true;
shadow-exclude = [
    "! name~=''",
    "class_g = 'Conky'"
    ]
mark-ovredir-focused = true;

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog and this project adheres to Semantic Versioning.

[1.0.3] - 2018-06-17

Changed
  • Astrality is now marked as “production/stable” on PyPI.
Fixed
  • Fixed bug which caused $ASTRALITY_LOGGING_LEVEL and astrality -l <logging_level> to be ignored.
  • Astrality now catches errors caused by starting the file system watcher. It logs the error and continues on without watching the error in such a case.

[1.0.2] - 2018-05-25

Fixed
  • Fixed lint errors in documentation which caused incorrect rendering on PyPI.

[1.0.1] - 2018-05-24

Fixed
  • Added missing dependency python-dateutil to setup.py.

[1.0.0] - 2018-05-24

Added
  • New symlink action type.
  • New copy action type.
  • New stow action type. This action allows you to either compile+symlink or compile+copy, bisecting a directory based on filename regular expression matching.
  • You can now compile all templates recursively within a directory. Just set content to a directory path. target must be a directory as well, and the relative file hierarchy is preserved.
  • You can now specify which filenames are considered templates when compiling directories recursively.
  • Template target filenames can now be renamed by specifying a regular expression capture group.
  • Non-template files can now be either symlinked, copied, or ignored.
  • The run action now supports timeout option, in order to set run_timeout on command-by-command basis.
  • compile actions now support an optional permissions field for setting the permissions of the compiled template. It allows setting octal values such as '755', and uses the UNIX chmod API.
  • Module requirements can now specify required programs and environment variables by using the dictionary keys installed and env respectively.
  • You can now set requires timeout on a case-by-case basis.
  • Add new --module CLI flag for running specific modules.
  • on_startup blocks can now optionally be implicitly defined at the root indentation level in the module.
  • You can now run astrality with --dry-run in order to check which actions that will be executed.
  • Modules can now depend on other modules with the module requires keyword.
  • Modules can now place action in a setup block, only to be executed once.
  • You can now execute astrality --reset-setup module_name in order to clear executed module setup actions.
  • Files created by compile, copy, stow, and symlink actions are now persisted and cleaned up when executing astrality --cleanup MODULE. Files that are overwritten by Astrality are backed up and restored on clean up.
Changed
  • astrality.yml has now been split into three separate files: astrality.yml for global configuration options, modules.yml for global modules, and context.yml for global context.

  • Directory module config file config.yml has been renamed and split into modules.yml and context.yml. See point above.

  • The run module action is now a dictionary instead of a string. This enables us to support additional future options, such as timeout. Now you specify the shell command to be run as a string value keyed to shell.

    Old syntax:

    run:
        - command1
        - command2
    

    New syntax:

    run:
        - shell: command1
        - shell: command2
    
  • The trigger module action is now a dictionary instead of a string. Now you specify the block to be triggered as a string value keyed to block. on_modified blocks need to supply an additional path key indicating which file modification block to trigger.

    Old syntax

    trigger:
        - on_startup
        - on_modified:path/to/file
    

    New syntax:

    trigger:
        - block: on_startup
        - block: on_modified
          path: path/to/file
    
  • Template metadata is now copied to compilation targets, including permission bits. Thanks to @sshashank124 for the implementation!

  • The trigger action now follows recursive trigger actions. Beware of circular trigger chains!

  • recompile_modified_templates has been renamed to reprocess_modified_files, as this option now also includes copied files.

  • Astrality will now only recompile templates that have already been compiled when reprocess_modified_files is set to true.

  • The template compile action keyword has now been replaced with content. This keyword makes more sense when we add support for compiling all templates within a directory. It also stays consistent with the new action types that have been added.

    Old syntax

    compile:
        - template: path/to/template
    

    New syntax:

    compile:
        - content: path/to/template
    
  • The module list items within the module requires option is now a dictionary, where shell commands are specified under the shell keyword. This allows other requirement types (see Added section).

    Old syntax

    requires:
        - './shell/script.sh'
    

    New syntax:

    requires:
        - shell: './shell/script'
    
  • Astrality now automatically quits if there is no reason for it to continue running.

  • When no compilation target is specified for a compile action, Astrality now creates a deterministic file within $XDG_DATA_HOME/astrality/compilations to be used as the compilation target. This behaves better than temporary files when programs expect files to still be present after Astrality restarts.

  • Astrality is now more conservative when killing duplicate Astrality processes by using a pidfile instead of pgrep -f astrality.

Fixed
  • If a import_context action imported specified from_section but not to_section, the section was not imported at all. This is now fixed by setting to_section to the same as from_section.
  • Template path placeholders are now normalized, which makes it possible to refer to the same template path in different ways, using symlinks and .. paths.
  • Module option requires_timeout is now respected.
  • Astrality no longer kills processes containing “astrality” in their command line invocation.

How to contribute

First, thanks for considering contributing to Astrality, that means a lot! Here we describe how you can help out, either by improving the documentation, submitting issues, or creating pull requests.

If you end up contributing, please consider adding yourself to the file CONTRIBUTORS.rst.

Bug reports and feature requests

You can browse any existing bug reports and feature requests on Astrality’s GitHub issues page on GitHub. New issues issues can be submitted the GitHub create issues page.

Improving the documentation

If you find something you would like to improve in the documentation, follow these steps:

  • Navigate to the page that you would like to edit on https://astrality.readthedocs.io.
  • Press the “Edit on GitHub” link in the upper right corner.
  • Press the “pencil” edit icon to the right of the “History” button.
  • Make the changes you intended.
  • Write a title and description for your change on the bottom of the page.
  • Select the radio button marked as: “Create a new branch for this commit and start a pull request”.
  • Press “Propose file change”.

The documentation is written in the “RestructuredText” markup language. If this is unfamiliar to you, take a look at this RST cheatsheet for more information.

Contributing code

Getting up and running
Cloning the repository

First we need to clone the repository. Open your terminal and navigate to the directory you wish to place project directory and run:

git clone https://github.com/jakobgm/astrality
cd astrality
Installing python3.6

Astrality runs on python3.6, so you need to ensure that you have it installed. If you have no specific preferred way of installing software on your computer, you can download and install it from here. Alternatively, if you use brew on MacOS, you can install it by running:

brew install python3

Or on ArchLinux:

sudo pacman -S python
Installing dependencies into a virtual environment

You should create a separate python3.6 “virtual environment” exclusively for Astrality. If this is new to you, take a look at the official virtualenv tutorial.

A quick summation:

python3.6 -m venv astrality-env
source astrality-env/bin/activate

Your terminal prompt should now show the name of the activated virtual environment, for example (astrality-env) $ your_commands_here. You can double check your environment by running echo $VIRTUAL_ENV. Later you can deactivate it by running deactivate or restarting your terminal. The activated virtual environment is necessary in order to run the developer version of Astrality, including the test suite.

Now you can install all the developer dependencies of Astrality into the virtual environment by running:

pip3 install -r requirements.txt

You should now make sure that the environment variable PYTHONPATH is set to the root directory of the repository. Check it by running:

$ echo $PYTHONPATH
/home/jakobgm/dev/astrality

With /home/jakobgm/dev/astrality being whatever makes sense on your system. If the value is incorrect you should run the following from the repository root:

export PYTHONPATH=$(pwd)
Running the developer version of Astrality

You should now be able to run the developer version of Astrality by running the following command:

./bin/astrality
Writing code

The python code in Astrality follows some conventions which we will describe here.

The structure of the code base

A brief outline Astrality’s code base is provided in the API documentation, and is recommended reading for any new contributor.

Tests

Astrality strives for 100% test coverage, and all new lines of code should preferably be covered by tests. That being said, if testing is unfamiliar to you, submitting code without test coverage is better than no code at all.

Tests are written with the pytest test framework, and you can read a “getting started” tutorial here.

You can run the test suite from the root of the repository by running:

pytest

Warning

For now, it is important that you run pytest from the root of the repository, else you will get a whole lot of ModuleNotFoundError exceptions.

Additionally, there are some tests which are hidden behind the --runslow flag, as some tests are slow due to writing files to disk and running certain shell commands. These slow tests can be run by writing:

pytest --runslow

When you submit a pull request, travis-ci will automatically check if all the tests pass with your submitted code. Coveralls will also check if the test coverage decreases.

If this feels intimidating, do not worry. We are happy to help guide you along if you encounter any issues with testing, so please submit pull requests even if the test suite fails for some reason.

Type annotations

Astrality’s code base heavily utilizes the new static type annotations available in python3.6.

The correctness of the type annotations are ensured by using mypy. You can check for type errors by running the following command from the repository root:

mypy .

mypy is a part of the test suite, enabled by the pytest-mypy plugin. Therefore, if the test suite passes, mypy must also be satisfied with your code!

All non-testing code should be completely type annotated, as strictly as possible. If this is new to you, or if you want to learn more, I recommend reading mypy documentation.

The offer to help with testing also holds for type annotations of course!

Continuous testing

Although this is mainly a matter of taste, running tests continuously while writing code is a great feedback mechanism.

pytest-watch should be already be installed on your system as part of Astrality’s developer dependencies. You can use it to rerun the test suite every time you save any *.py file within the repository.

You can run it in a separate terminal by running:

ptw

It is often useful to run pytest-watch in verbose mode, stop on first test failure, and only run one specific test file at a time. You can do all this by running:

ptw -- -vv -x astrality/tests/test_compiler.py
Debugging

If you end up breaking any behaviour during development, it should often be reported by the test suite. Breaking tests will often lead you in the correct direction for fixing the problem.

Some tests might be a bit too brittle, so if you change any underlying data structures it might break some badly written test(s). Sometimes the correct thing to do is to simply delete the failing test. Just ask if you are unsure.

You can also look at the logging output of Astrality in order to pinpoint possible reasons for any weird behaviour. You can set the logging level of astrality by setting the environment variable ASTRALITY_LOGGING_LEVEL to an appropriate value, for example:

# Set the appropiate logging level
export ASTRALITY_LOGGING_LEVEL=DEBUG

# Run the CLI entrypoint
./bin/astrality

If you submit a bug report, we appreciate if you include the standard output of Astrality run with ASTRALITY_LOGGING_LEVEL=DEBUG.

Code style

We use the python source code checker flake8 to help us maintain a consistent style across the code base. It runs automatically as part of our pytest test-suite.

You can lint your code locally by running flake8 . from the root of the repository. Integrating flake8 into your workflow is recommended, there are plugins available for most popular IDEs and text-editors!

You can instruct git to ensure flake8 compliance before every commit by running git config --bool flake8.strict true from your shell.

In addition to this, some additional styling conventions are applied to the project:

  • String literals should use single quotes. With other words: 'this is a string' instead of "this is a string".

  • Try to use keyword arguments when calling functions, unless it is extremely clear from context.

  • Function arguments split over several lines should use trailing commas. With other words, we prefer to write code like this:

    compile_template(
        template=template,
        target=target,
    )
    

    Instead of this:

    compile_template(
        template=template,
        target=target
    )
    

These conventions are mainly enforced in order to stay consistent for choices where PEP 8 do not tell us what to do.

Local documentation

Astrality uses the sphinx ecosystem in conjunction with readthedocs for its documentation.

You can run a local instance of the documentation by running:

cd docs
sphinx-autobuild . _build

The entire documentation should now be available on http://127.0.0.1:8000. When you edit the documentation files placed with docs, your web browser should automatically refresh the website with the new content!

API documentation

This section contains documentation for the source code of Astrality, and is intended for the developers of Astrality.

The structure of the code base

Here we offer a quick overview of the most relevant python modules in the code base, loosely ordered according to their execution order.

bin.astrality:
The CLI entry point of Astrality, using the standard library argparse module.
astrality.astrality:
The main loop of Astrality, binding everything together. Calls out to the different submodules and handles interruption signals gracefully.
astrality.config:
Compilation and pre-processing of the user configuration according to the heuristics explained in the documentation.
astrality.github:
Retrieval of modules defined in GitHub repositories.
astrality.module:

Execution of actions defined in modules.

Each module in the user configuration is represented by a Module object. All Module-objects are managed by a single ModuleManager object which iterates over them and executes their actions.

astrality.requirements:
Module for checking if module requirements are satisfied.
astrality.actions:
Module for executing actions such as “import_context”, “compile”, “run”, and “trigger”.
astrality.event_listener:
Implements all the types of module event listeners as subclasses of EventListener.
astrality.context:
Defines a dictionary-like data structure which contains context values, passed off to Jinja2 template compilation.
astrality.compiler:
Wrappers around the Jinja2 library for compiling templates with specific context values.
astrality.filewatcher:
Implements a file system watcher which dispatches to event handlers when files are modified on disk.
astrality.utils:
Utility functions which are used all over the code base, most importantly a wrapper function for running shell commands.

Modules

Astrality’s modules are placed within the astrality mother-module. For example, the actions module is importable from astrality.actions.

Actions module

Module defining class-representation of module actions.

Each action class type encapsulates the user specified options available for that specific action type. The action itself can be performed by invoking the object method execute().

One of the main goals with Action, is that the arity of execute is 0. This means that we unfortunately need to pass a reference to global mutable state, i.e. the context store.

Another goal is that none of the subclasses require the global configuration of the entire application, just the action configuration itself. Earlier implementations required GlobalApplicationConfig to be passed arround in the entire run-stack, which was quite combersome. Some of the limitations with this approach could be solved if we implement GlobalApplicationConfig as a singleton which could be imported and accessed independently from other modules.

class astrality.actions.Action(options, directory, replacer, context_store, creation_store)[source]

Bases: abc.ABC

Superclass for module action types.

Parameters:
  • options (Union[_Forwardref, _Forwardref, _Forwardref, _Forwardref, _Forwardref, _Forwardref, _Forwardref]) – A dictionary containing the user options for a given module action type.
  • directory (Path) – The directory used as anchor for relative paths. This must be an absolute path.
  • replacer (Callable[[str], str]) – Placeholder substitutor of string user options.
  • context_store (Context) – A reference to the global context store.
  • creation_store (ModuleCreatedFiles) – ModuleCreatedFiles object which stores which files that are created by the different module actions.
Options = typing.Union[_ForwardRef('CompileDict'), _ForwardRef('CopyDict'), _ForwardRef('ImportContextDict'), _ForwardRef('RunDict'), _ForwardRef('StowDict'), _ForwardRef('SymlinkDict'), _ForwardRef('TriggerDict')]
execute(dry_run=False)[source]

Execute defined action.

Parameters:dry_run (bool) – If external side effects should be skipped.
Return type:Any
option(key, default=None, path=False)[source]

Return user specified action option.

All option value access should go through this helper function, as it replaces relevant placeholders users might have specified.

Parameters:
  • key (str) – The key of the user option that should be retrieved.
  • default (Optional[Any]) – Default return value if key not found.
  • path (bool) – If True, convert string path to Path.is_absolute().
Return type:

Any

Returns:

Processed action configuration value.

replace(string)[source]

Return converted string, substitution defined by replacer.

This is used to replace placeholders such as {event}. This redirection is necessary due to python/mypy/issues/2427

Parameters:string (str) – String configuration option.
Return type:str
Returns:String with placeholders substituted.
class astrality.actions.ActionBlock(action_block, directory, replacer, context_store, global_modules_config, module_name)[source]

Bases: object

Class representing a module action block, e.g. ‘on_startup’.

Parameters:
  • action_block (ActionBlockDict) – Dictionary containing all actions to be performed.
  • directory (Path) – The directory used as anchor for relative paths. This must be an absolute path.
  • replacer (Callable[[str], str]) – Placeholder substitutor of string user options.
  • context_store (Context) – A reference to the global context store.
  • module_name (str) – Name of module owning ActionBlock.
  • global_modules_config (GlobalModulesConfig) – Global configuration object.
action_options(identifier)[source]

Return all action configs of type ‘identifier’.

Parameters:identifier (str) – Action type, such as ‘run’ or ‘compile’.
Return type:List[Union[CompileDict, CopyDict, ImportContextDict, RunDict, StowDict, SymlinkDict, TriggerDict]]
Returns:List of action options of that type.
action_types = {'compile': <class 'astrality.actions.CompileAction'>, 'copy': <class 'astrality.actions.CopyAction'>, 'import_context': <class 'astrality.actions.ImportContextAction'>, 'run': <class 'astrality.actions.RunAction'>, 'stow': <class 'astrality.actions.StowAction'>, 'symlink': <class 'astrality.actions.SymlinkAction'>, 'trigger': <class 'astrality.actions.TriggerAction'>}
compile(dry_run=False)[source]

Compile templates.

Return type:None
copy(dry_run=False)[source]

Copy files.

Return type:None
execute(default_timeout, dry_run=False)[source]

Execute all actions in action block.

The order of execution is:
  1. Perform all context imports into the context store.
  2. Compile all templates.
  3. Run all shell commands.
Return type:None
import_context(dry_run=False)[source]

Import context into global context store.

Return type:None
performed_compilations()[source]

Return all earlier performed compilations.

Return type:Defaultdict[Path, Set[Path]]
Returns:Dictionary with template keys and target path set.
run(default_timeout=None, dry_run=False)[source]

Run shell commands.

Parameters:default_timeout (Union[int, float, None]) – How long to wait for run commands to exit
Return type:Tuple[Tuple[str, str], …]
Returns:Tuple of 2-tuples containing (shell_command, stdout,)
stow(dry_run=False)[source]

Stow directory contents.

Return type:None

Symlink files.

Return type:None
triggers(dry_run=False)[source]

Return all trigger instructions specified in action block.

Return type:Tuple[Trigger, …]
Returns:Tuple of Trigger objects specified in action block.
class astrality.actions.CompileAction(*args, **kwargs)[source]

Bases: astrality.actions.Action

Compile template action.

Options = typing.Union[_ForwardRef('CompileDict'), _ForwardRef('CopyDict'), _ForwardRef('ImportContextDict'), _ForwardRef('RunDict'), _ForwardRef('StowDict'), _ForwardRef('SymlinkDict'), _ForwardRef('TriggerDict')]
create_compilation_target(template)[source]

Create compilation target for template with unspecified target.

Compilation targets are stored in $XDG_DATA_HOME/astrality/compilations. For details regarding the implementation see:

Parameters:name – Path to template to be compiled.
Return type:Path
Returns:Path to deterministicly determined compilation target.
execute(dry_run=False)[source]

Compile template source to target destination.

Parameters:dry_run (bool) – If True, skip and log compilation(s).
Return type:Dict[Path, Path]
Returns:Dictionary with template content keys and target path values.
option(key, default=None, path=False)

Return user specified action option.

All option value access should go through this helper function, as it replaces relevant placeholders users might have specified.

Parameters:
  • key (str) – The key of the user option that should be retrieved.
  • default (Optional[Any]) – Default return value if key not found.
  • path (bool) – If True, convert string path to Path.is_absolute().
Return type:

Any

Returns:

Processed action configuration value.

performed_compilations()[source]

Return dictionary containing all performed compilations.

Return type:Defaultdict[Path, Set[Path]]
Returns:Dictinary with keys containing compiled templates, and values as a set of target paths.
priority = 400
replace(string)

Return converted string, substitution defined by replacer.

This is used to replace placeholders such as {event}. This redirection is necessary due to python/mypy/issues/2427

Parameters:string (str) – String configuration option.
Return type:str
Returns:String with placeholders substituted.
class astrality.actions.CopyAction(*args, **kwargs)[source]

Bases: astrality.actions.Action

Copy files Action sub-class.

Options = typing.Union[_ForwardRef('CompileDict'), _ForwardRef('CopyDict'), _ForwardRef('ImportContextDict'), _ForwardRef('RunDict'), _ForwardRef('StowDict'), _ForwardRef('SymlinkDict'), _ForwardRef('TriggerDict')]
execute(dry_run=False)[source]

Copy from content path to target path.

Parameters:dry_run (bool) – If True, skip and log copy creation(s).
Return type:Dict[Path, Path]
Returns:Dictionary with content keys and copy values.
option(key, default=None, path=False)

Return user specified action option.

All option value access should go through this helper function, as it replaces relevant placeholders users might have specified.

Parameters:
  • key (str) – The key of the user option that should be retrieved.
  • default (Optional[Any]) – Default return value if key not found.
  • path (bool) – If True, convert string path to Path.is_absolute().
Return type:

Any

Returns:

Processed action configuration value.

priority = 300
replace(string)

Return converted string, substitution defined by replacer.

This is used to replace placeholders such as {event}. This redirection is necessary due to python/mypy/issues/2427

Parameters:string (str) – String configuration option.
Return type:str
Returns:String with placeholders substituted.
class astrality.actions.ImportContextAction(options, directory, replacer, context_store, creation_store)[source]

Bases: astrality.actions.Action

Import context into global context store.

Parameters:context_store (Context) – A mutable reference to the global context store.

See Action for documentation for the other parameters.

Options = typing.Union[_ForwardRef('CompileDict'), _ForwardRef('CopyDict'), _ForwardRef('ImportContextDict'), _ForwardRef('RunDict'), _ForwardRef('StowDict'), _ForwardRef('SymlinkDict'), _ForwardRef('TriggerDict')]
execute(dry_run=False)[source]

Import context section(s) according to user configuration block.

Parameters:dry_run (bool) – This parameter is ignored, as import_context only has internal side effects.
Return type:None
option(key, default=None, path=False)

Return user specified action option.

All option value access should go through this helper function, as it replaces relevant placeholders users might have specified.

Parameters:
  • key (str) – The key of the user option that should be retrieved.
  • default (Optional[Any]) – Default return value if key not found.
  • path (bool) – If True, convert string path to Path.is_absolute().
Return type:

Any

Returns:

Processed action configuration value.

priority = 100
replace(string)

Return converted string, substitution defined by replacer.

This is used to replace placeholders such as {event}. This redirection is necessary due to python/mypy/issues/2427

Parameters:string (str) – String configuration option.
Return type:str
Returns:String with placeholders substituted.
class astrality.actions.RunAction(options, directory, replacer, context_store, creation_store)[source]

Bases: astrality.actions.Action

Run shell command Action sub-class.

Options = typing.Union[_ForwardRef('CompileDict'), _ForwardRef('CopyDict'), _ForwardRef('ImportContextDict'), _ForwardRef('RunDict'), _ForwardRef('StowDict'), _ForwardRef('SymlinkDict'), _ForwardRef('TriggerDict')]
execute(default_timeout=0, dry_run=False)[source]

Execute shell command action.

Parameters:
  • default_timeout (Union[int, float]) – Run timeout in seconds if no specific value is specified in options.
  • dry_run (bool) – If True, skip and log commands to be executed.
Return type:

Optional[Tuple[str, str]]

Returns:

2-tuple containing the executed command and its resulting stdout.

option(key, default=None, path=False)

Return user specified action option.

All option value access should go through this helper function, as it replaces relevant placeholders users might have specified.

Parameters:
  • key (str) – The key of the user option that should be retrieved.
  • default (Optional[Any]) – Default return value if key not found.
  • path (bool) – If True, convert string path to Path.is_absolute().
Return type:

Any

Returns:

Processed action configuration value.

priority = 600
replace(string)

Return converted string, substitution defined by replacer.

This is used to replace placeholders such as {event}. This redirection is necessary due to python/mypy/issues/2427

Parameters:string (str) – String configuration option.
Return type:str
Returns:String with placeholders substituted.
class astrality.actions.SetupActionBlock(action_block, directory, replacer, context_store, global_modules_config, module_name)[source]

Bases: astrality.actions.ActionBlock

Setup action block which only executes actions once.

action_options(identifier)[source]

Return action configs of ‘identifier’ type that have not been executed.

Parameters:identifier (str) – Action type, such as ‘run’ or ‘compile’.
Return type:List[Union[CompileDict, CopyDict, ImportContextDict, RunDict, StowDict, SymlinkDict, TriggerDict]]
Returns:List of action options of that type.
action_types = {'compile': <class 'astrality.actions.CompileAction'>, 'copy': <class 'astrality.actions.CopyAction'>, 'import_context': <class 'astrality.actions.ImportContextAction'>, 'run': <class 'astrality.actions.RunAction'>, 'stow': <class 'astrality.actions.StowAction'>, 'symlink': <class 'astrality.actions.SymlinkAction'>, 'trigger': <class 'astrality.actions.TriggerAction'>}
compile(dry_run=False)

Compile templates.

Return type:None
copy(dry_run=False)

Copy files.

Return type:None
execute(default_timeout, dry_run=False)

Execute all actions in action block.

The order of execution is:
  1. Perform all context imports into the context store.
  2. Compile all templates.
  3. Run all shell commands.
Return type:None
import_context(dry_run=False)

Import context into global context store.

Return type:None
performed_compilations()

Return all earlier performed compilations.

Return type:Defaultdict[Path, Set[Path]]
Returns:Dictionary with template keys and target path set.
run(default_timeout=None, dry_run=False)

Run shell commands.

Parameters:default_timeout (Union[int, float, None]) – How long to wait for run commands to exit
Return type:Tuple[Tuple[str, str], …]
Returns:Tuple of 2-tuples containing (shell_command, stdout,)
stow(dry_run=False)

Stow directory contents.

Return type:None

Symlink files.

Return type:None
triggers(dry_run=False)

Return all trigger instructions specified in action block.

Return type:Tuple[Trigger, …]
Returns:Tuple of Trigger objects specified in action block.
class astrality.actions.StowAction(*args, **kwargs)[source]

Bases: astrality.actions.Action

Stow directory action.

Options = typing.Union[_ForwardRef('CompileDict'), _ForwardRef('CopyDict'), _ForwardRef('ImportContextDict'), _ForwardRef('RunDict'), _ForwardRef('StowDict'), _ForwardRef('SymlinkDict'), _ForwardRef('TriggerDict')]
execute(dry_run=False)[source]

Stow directory source to target destination.

Parameters:dry_run (bool) – If True, skip and log copies, symlinks, and compilations.
Return type:Dict[Path, Path]
Returns:Dictionary with source keys and target values. Contains compiled, symlinked, and copied files.
managed_files()[source]

Return dictionary containing content keys and target values.

Return type:Dict[Path, Set[Path]]
Returns:Dictinary with keys containing compiled templates, and values as a set of target paths. If non_templates is ‘copy’, then these will be included as well.
option(key, default=None, path=False)

Return user specified action option.

All option value access should go through this helper function, as it replaces relevant placeholders users might have specified.

Parameters:
  • key (str) – The key of the user option that should be retrieved.
  • default (Optional[Any]) – Default return value if key not found.
  • path (bool) – If True, convert string path to Path.is_absolute().
Return type:

Any

Returns:

Processed action configuration value.

priority = 500
replace(string)

Return converted string, substitution defined by replacer.

This is used to replace placeholders such as {event}. This redirection is necessary due to python/mypy/issues/2427

Parameters:string (str) – String configuration option.
Return type:str
Returns:String with placeholders substituted.
class astrality.actions.SymlinkAction(*args, **kwargs)[source]

Bases: astrality.actions.Action

Symlink files Action sub-class.

Options = typing.Union[_ForwardRef('CompileDict'), _ForwardRef('CopyDict'), _ForwardRef('ImportContextDict'), _ForwardRef('RunDict'), _ForwardRef('StowDict'), _ForwardRef('SymlinkDict'), _ForwardRef('TriggerDict')]
execute(dry_run=False)[source]

Symlink to content path from target path.

Parameters:dry_run (bool) – If True, skip and log symlink creation(s).
Return type:Dict[Path, Path]
Returns:Dictionary with content keys and symlink values.
option(key, default=None, path=False)

Return user specified action option.

All option value access should go through this helper function, as it replaces relevant placeholders users might have specified.

Parameters:
  • key (str) – The key of the user option that should be retrieved.
  • default (Optional[Any]) – Default return value if key not found.
  • path (bool) – If True, convert string path to Path.is_absolute().
Return type:

Any

Returns:

Processed action configuration value.

priority = 200
replace(string)

Return converted string, substitution defined by replacer.

This is used to replace placeholders such as {event}. This redirection is necessary due to python/mypy/issues/2427

Parameters:string (str) – String configuration option.
Return type:str
Returns:String with placeholders substituted.
class astrality.actions.Trigger(block, specified_path=None, relative_path=None, absolute_path=None)[source]

Bases: object

A class representing an instruction to trigger a specific action block.

Variables:
  • block – The block to be trigger, for example ‘on_startup’, ‘on_event’, ‘on_exit’, or ‘on_modified’.
  • specified_path – The string path specified for a ‘on_modified’ block.
  • relative_path – The relative pathlib.Path specified by specified_path.
  • absolute_path – The absolute path specified by specified_path.
class astrality.actions.TriggerAction(options, directory, replacer, context_store, creation_store)[source]

Bases: astrality.actions.Action

Action sub-class representing a trigger action.

Options = typing.Union[_ForwardRef('CompileDict'), _ForwardRef('CopyDict'), _ForwardRef('ImportContextDict'), _ForwardRef('RunDict'), _ForwardRef('StowDict'), _ForwardRef('SymlinkDict'), _ForwardRef('TriggerDict')]
execute(dry_run=False)[source]

Return trigger instruction.

If no trigger is specified, return None.

Parameters:dry_run (bool) – This parameter is ignored.
Return type:Optional[Trigger]
Returns:Optional Trigger instance.
option(key, default=None, path=False)

Return user specified action option.

All option value access should go through this helper function, as it replaces relevant placeholders users might have specified.

Parameters:
  • key (str) – The key of the user option that should be retrieved.
  • default (Optional[Any]) – Default return value if key not found.
  • path (bool) – If True, convert string path to Path.is_absolute().
Return type:

Any

Returns:

Processed action configuration value.

priority = 0
replace(string)

Return converted string, substitution defined by replacer.

This is used to replace placeholders such as {event}. This redirection is necessary due to python/mypy/issues/2427

Parameters:string (str) – String configuration option.
Return type:str
Returns:String with placeholders substituted.

Attributions

Inspirations for themes

Themes have been made by the help of several posts on the /r/unixporn subreddit. Here are some of them:

In addition, the astrality logo is a modified version of a logo designed by miscellaneous from Flaticon.

License

Distributed under the terms of the MIT license, Astrality is free and open source software.

The MIT License (MIT)

Copyright (c) 2018 Jakob Gerhard Martinussen

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.