A tool that bootstraps your dotfiles ⚡️

Related tags

Miscellaneous dotbot
Overview

Dotbot Build Status

Dotbot makes installing your dotfiles as easy as git clone $url && cd dotfiles && ./install, even on a freshly installed system!


Rationale

Dotbot is a tool that bootstraps your dotfiles (it's a [Dot]files [bo]o[t]strapper, get it?). It does less than you think, because version control systems do more than you think.

Dotbot is designed to be lightweight and self-contained, with no external dependencies and no installation required. Dotbot can also be a drop-in replacement for any other tool you were using to manage your dotfiles, and Dotbot is VCS-agnostic -- it doesn't make any attempt to manage your dotfiles.

See this blog post or more resources on the tutorials page for more detailed explanations of how to organize your dotfiles.

Getting Started

Starting Fresh?

Great! You can automate the creation of your dotfiles by using the user-contributed init-dotfiles script. If you'd rather use a template repository, check out dotfiles_template. Or, if you're just looking for some inspiration, we've got you covered.

Integrate with Existing Dotfiles

The following will help you get set up using Dotbot in just a few steps.

If you're using Git, you can add Dotbot as a submodule:

cd ~/.dotfiles # replace with the path to your dotfiles
git init # initialize repository if needed
git submodule add https://github.com/anishathalye/dotbot
git config -f .gitmodules submodule.dotbot.ignore dirty # ignore dirty commits in the submodule
cp dotbot/tools/git-submodule/install .
touch install.conf.yaml

If you're using Mercurial, you can add Dotbot as a subrepo:

.hgsub hg add .hgsub git clone https://github.com/anishathalye/dotbot cp dotbot/tools/hg-subrepo/install . touch install.conf.yaml ">
cd ~/.dotfiles # replace with the path to your dotfiles
hg init # initialize repository if needed
echo "dotbot = [git]https://github.com/anishathalye/dotbot" > .hgsub
hg add .hgsub
git clone https://github.com/anishathalye/dotbot
cp dotbot/tools/hg-subrepo/install .
touch install.conf.yaml

If you are using PowerShell instead of a POSIX shell, you can use the provided install.ps1 script instead of install.

To get started, you just need to fill in the install.conf.yaml and Dotbot will take care of the rest. To help you get started we have an example config file as well as configuration documentation for the accepted parameters.

Note: The install script is merely a shim that checks out the appropriate version of Dotbot and calls the full Dotbot installer. By default, the script assumes that the configuration is located in install.conf.yaml the Dotbot submodule is located in dotbot. You can change either of these parameters by editing the variables in the install script appropriately.

Setting up Dotbot as a submodule or subrepo locks it on the current version. You can upgrade Dotbot at any point. If using a submodule, run git submodule update --remote dotbot, substituting dotbot with the path to the Dotbot submodule; be sure to commit your changes before running ./install, otherwise the old version of Dotbot will be checked out by the install script. If using a subrepo, run git fetch && git checkout origin/master in the Dotbot directory.

If you prefer, you can install Dotbot from PyPI and call it as a command-line program:

pip install dotbot
touch install.conf.yaml

In this case, rather than running ./install, you can invoke Dotbot with dotbot -c .

Full Example

Here's an example of a complete configuration.

The conventional name for the configuration file is install.conf.yaml.

- defaults:
    link:
      relink: true

- clean: ['~']

- link:
    ~/.tmux.conf: tmux.conf
    ~/.vim: vim
    ~/.vimrc: vimrc

- create:
    - ~/downloads
    - ~/.vim/undo-history

- shell:
  - [git submodule update --init --recursive, Installing submodules]

The configuration file is typically written in YAML, but it can also be written in JSON (which is a subset of YAML). JSON configuration files are conventionally named install.conf.json.

Configuration

Dotbot uses YAML or JSON-formatted configuration files to let you specify how to set up your dotfiles. Currently, Dotbot knows how to link files and folders, create folders, execute shell commands, and clean directories of broken symbolic links. Dotbot also supports user plugins for custom commands.

Ideally, bootstrap configurations should be idempotent. That is, the installer should be able to be run multiple times without causing any problems. This makes a lot of things easier to do (in particular, syncing updates between machines becomes really easy).

Dotbot configuration files are arrays of tasks, where each task is a dictionary that contains a command name mapping to data for that command. Tasks are run in the order in which they are specified. Commands within a task do not have a defined ordering.

When writing nested constructs, keep in mind that YAML is whitespace-sensitive. Following the formatting used in the examples is a good idea. If a YAML configuration file is not behaving as you expect, try inspecting the equivalent JSON and check that it is correct.

Directives

Most Dotbot commands support both a simplified and extended syntax, and they can also be configured via setting defaults.

Link

Link commands specify how files and directories should be symbolically linked. If desired, items can be specified to be forcibly linked, overwriting existing files if necessary. Environment variables in paths are automatically expanded.

Format

Link commands are specified as a dictionary mapping targets to source locations. Source locations are specified relative to the base directory (that is specified when running the installer). If linking directories, do not include a trailing slash.

Link commands support an optional extended configuration. In this type of configuration, instead of specifying source locations directly, targets are mapped to extended configuration dictionaries.

Parameter Explanation
path The source for the symlink, the same as in the shortcut syntax (default: null, automatic (see below))
create When true, create parent directories to the link as needed. (default: false)
relink Removes the old target if it's a symlink (default: false)
force Force removes the old target, file or folder, and forces a new link (default: false)
relative Use a relative path to the source when creating the symlink (default: false, absolute links)
canonicalize Resolve any symbolic links encountered in the source to symlink to the canonical path (default: true, real paths)
if Execute this in your $SHELL and only link if it is successful.
ignore-missing Do not fail if the source is missing and create the link anyway (default: false)
glob Treat path as a glob pattern, expanding patterns referenced below, linking all files matched. (default: false)
exclude Array of glob patterns to remove from glob matches. Uses same syntax as path. Ignored if glob is false. (default: empty, keep all matches)
prefix Prepend prefix prefix to basename of each file when linked, when glob is true. (default: '')

When glob: True, Dotbot uses glob.glob to resolve glob paths, expanding Unix shell-style wildcards, which are not the same as regular expressions; Only the following are expanded:

Pattern Meaning
* matches anything
** matches any file, recursively (Python >= 3.5 only)
? matches any single character
[seq] matches any character in seq
[!seq] matches any character not in seq

However, due to the design of glob.glob, using a glob pattern such as config/*, will not match items that begin with .. To specifically capture items that being with ., you will need to include the . in the pattern, like this: config/.*.

Example

- link:
    ~/.config/terminator:
      create: true
      path: config/terminator
    ~/.vim: vim
    ~/.vimrc:
      relink: true
      path: vimrc
    ~/.zshrc:
      force: true
      path: zshrc
    ~/.hammerspoon:
      if: '[ `uname` = Darwin ]'
      path: hammerspoon
    ~/.config/:
      path: dotconf/config/**
    ~/:
      glob: true
      path: dotconf/*
      prefix: '.'

If the source location is omitted or set to null, Dotbot will use the basename of the destination, with a leading . stripped if present. This makes the following two config files equivalent.

Explicit sources:

- link:
    ~/bin/ack: ack
    ~/.vim: vim
    ~/.vimrc:
      relink: true
      path: vimrc
    ~/.zshrc:
      force: true
      path: zshrc
    ~/.config/:
      glob: true
      path: config/*
      relink: true
      exclude: [ config/Code ]
    ~/.config/Code/User/:
      create: true
      glob: true
      path: config/Code/User/*
      relink: true

Implicit sources:

- link:
    ~/bin/ack:
    ~/.vim:
    ~/.vimrc:
      relink: true
    ~/.zshrc:
      force: true
    ~/.config/:
      glob: true
      path: config/*
      relink: true
      exclude: [ config/Code ]
    ~/.config/Code/User/:
      create: true
      glob: true
      path: config/Code/User/*
      relink: true

Create

Create commands specify empty directories to be created. This can be useful for scaffolding out folders or parent folder structure required for various apps, plugins, shell commands, etc.

Format

Create commands are specified as an array of directories to be created. If you want to use the optional extended configuration, create commands are specified as dictionaries. For convenience, it's permissible to leave the options blank (null) in the dictionary syntax.

Parameter Explanation
mode The file mode to use for creating the leaf directory (default: 0777)

The mode parameter is treated in the same way as in Python's os.mkdir. Its behavior is platform-dependent. On Unix systems, the current umask value is first masked out.

Example

- create:
    - ~/downloads
    - ~/.vim/undo-history
- create:
    ~/.ssh:
      mode: 0700
    ~/projects:

Shell

Shell commands specify shell commands to be run. Shell commands are run in the base directory (that is specified when running the installer).

Format

Shell commands can be specified in several different ways. The simplest way is just to specify a command as a string containing the command to be run.

Another way is to specify a two element array where the first element is the shell command and the second is an optional human-readable description.

Shell commands support an extended syntax as well, which provides more fine-grained control.

Parameter Explanation
command The command to be run
description A human-readable message describing the command (default: null)
quiet Show only the description but not the command in log output (default: false)
stdin Allow a command to read from standard input (default: false)
stdout Show a command's output from stdout (default: false)
stderr Show a command's error output from stderr (default: false)

Note that quiet controls whether the command (a string) is printed in log output, it does not control whether the output from running the command is printed (that is controlled by stdout / stderr). When a command's stdin / stdout / stderr is not enabled (which is the default), it's connected to /dev/null, disabling input and hiding output.

Example

- shell:
  - chsh -s $(which zsh)
  - [chsh -s $(which zsh), Making zsh the default shell]
  -
    command: read var && echo Your variable is $var
    stdin: true
    stdout: true
    description: Reading and printing variable
    quiet: true
  -
    command: read fail
    stderr: true

Clean

Clean commands specify directories that should be checked for dead symbolic links. These dead links are removed automatically. Only dead links that point to somewhere within the dotfiles directory are removed unless the force option is set to true.

Format

Clean commands are specified as an array of directories to be cleaned.

Clean commands also support an extended configuration syntax.

Parameter Explanation
force Remove dead links even if they don't point to a file inside the dotfiles directory (default: false)
recursive Traverse the directory recursively looking for dead links (default: false)

Note: using the recursive option for ~ is not recommended because it will be slow.

Example

- clean: ['~']

- clean:
    ~/:
      force: true
    ~/.config:
      recursive: true

Defaults

Default options for plugins can be specified so that options don't have to be repeated many times. This can be very useful to use with the link command, for example.

Defaults apply to all commands that come after setting the defaults. Defaults can be set multiple times; each change replaces the defaults with a new set of options.

Format

Defaults are specified as a dictionary mapping action names to settings, which are dictionaries from option names to values.

Example

- defaults:
    link:
      create: true
      relink: true

Plugins

Dotbot also supports custom directives implemented by plugins. Plugins are implemented as subclasses of dotbot.Plugin, so they must implement can_handle() and handle(). The can_handle() method should return True if the plugin can handle an action with the given name. The handle() method should do something and return whether or not it completed successfully.

All built-in Dotbot directives are written as plugins that are loaded by default, so those can be used as a reference when writing custom plugins.

Plugins are loaded using the --plugin and --plugin-dir options, using either absolute paths or paths relative to the base directory. It is recommended that these options are added directly to the install script.

See here for a current list of plugins.

Command-line Arguments

Dotbot takes a number of command-line arguments; you can run Dotbot with --help, e.g. by running ./install --help, to see the full list of options. Here, we highlight a couple that are particularly interesting.

--only

You can call ./install --only [list of directives], such as ./install --only link, and Dotbot will only run those sections of the config file.

--except

You can call ./install --except [list of directives], such as ./install --except shell, and Dotbot will run all the sections of the config file except the ones listed.

Wiki

Check out the Dotbot wiki for more information, tips and tricks, user-contributed plugins, and more.

Contributing

Do you have a feature request, bug report, or patch? Great! See CONTRIBUTING.md for information on what you can do about that.

License

Copyright (c) 2014-2021 Anish Athalye. Released under the MIT License. See LICENSE.md for details.

Comments
  • Proper Variable Expansion

    Proper Variable Expansion

    Currently, dotbot uses os.path.expandvars. However, in some cases, it may be desireable to follow proper shell parameter substitution. (see: the related manual) Here is an example usage: I want my zshrc to work. Some systems may define $ZDOTDIR to be $XDG_DATA_HOME/zsh, some may leave it undefined. The typical solution would be to use ${ZDOTDIR:-$HOME}/.zshrc as the install location. However, without parameter substitution, there is no way to achieve this.

    enhancement 
    opened by CosmicToast 27
  • CLI flag to only run certain sections?

    CLI flag to only run certain sections?

    Would it make sense to add a flag to only run the clean, link, or shell sections of the config? Sometimes I dont want the shell commands to run so I just comment them out. This could be implemented using flags (dotbot -c/--clean -l/--link -s/--shell), or possibly as "sub-commands" (dotbot {clean,link,shell} or dotbot run {clean,link,shell}) and the default would still be to perform all three tasks.

    I also thought it might be useful to add another optional argument to shell configurations to prompt the user if they want the shell command to run or not, something like "confirm: true". Not really sure that would be useful if I could just tell dotbot to only clean and link.

    enhancement 
    opened by PacketPerception 22
  • An error was encountered while executing action link

    An error was encountered while executing action link

    Here is my current install.conf.yaml file:

    #!/usr/bin/env bash
    
    - clean: ['~']
    
    set -e
    
    - link:
      ~/.tmux.conf: tmux.conf
      ~/.vim: vim/
      ~/.vimrc: vimrc
      ~/.zsh: zsh/
      ~/.zshrc: zshrc
    
    - shell:
      - [git submodule update --init --recursive, Installing/updating submodules]
    

    Here is my install script:

    #!/usr/bin/env bash
    
    CONFIG="install.conf.yaml"
    DOTBOT_DIR="dotbot"
    
    DOTBOT_BIN="bin/dotbot"
    BASEDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
    
    cd "${BASEDIR}"
    git submodule update --init --recursive "${DOTBOT_DIR}"
    
    "${BASEDIR}/${DOTBOT_DIR}/${DOTBOT_BIN}" -d "${BASEDIR}" -c "${CONFIG}" "${@}"
    

    Here is my output:

    All targets have been cleaned
    An error was encountered while executing action link
    Action link not handled
    Action ~/.zsh not handled
    Action ~/.vimrc not handled
    Action ~/.vim not handled
    Action ~/.zshrc not handled
    Action ~/.tmux.conf not handled
    Installing/updating submodules [git submodule update --init --recursive]
    All commands have been executed
    
    ==> Some tasks were not executed successfully
    

    This happens no matter what. I have created and destroyed multiple vimrc, zshrc, etc. files, I have completely removed Dotbot and re-downloaded it, I have tried using JSON formatting. Nothing I seem to try will let me create the links. I'm not sure what the problem is at this point; any suggestions will be appreciated.

    question 
    opened by indiesquidge 21
  • Implement globbing support

    Implement globbing support

    Phew! Turns out this does a lot better than I had initially planned!

    I need help making the test cases, here's the behaviour I tried to create and need to test:

    Failure cases

    link:
      ~/bin/:
        path: bin
        use_glob: true
    
    defaults:
      - link:
          use_glob: true
    link:
      ~/bin/:
        path: bin/
    

    Because we aren't sure if we should link bin inside ~/bin or link bin as ~/bin. Handled with error message by the glob_star_loc is -1 and destination[-1] is '/' conditional.

    Success Cases

    link:
      ~/bin:
        path: bin
        use_glob: true
    
    defaults:
      - link:
          use_glob: true
    link:
      ~/bin:
        path: bin
    

    Works as normal link operation ~/bin -> bin, so that globbing can be turned on at default level.

    defaults:
      - link:
          use_glob: true
    link:
      ~/bin/:
        path: bin/*
    
    link:
      ~/bin:
        path: bin/*
    

    Links all the items in bin/* into individual links in ~/bin/. So long as there is a wildcard * in the path: then it will link the glob results as individual items into the destination.

    Trailing Slash

    ~/bin vs ~/bin/ is a special case used to enforce a certain behavior, with a trailing slash and use_glob enabled, it forces dotbot to treat the link task as "link items into this folder" instead of potentially linking as.

    Advanced Quirks

    link:
      ~/.config/tmux/:
        path: tmux/**.conf
        create: true
    

    Links all the .conf files in tmux/ into ~/.config/tmux/ however the double star recursive match is not supported in python's default glob. (but is working in glob2 pip package Example:

    tmux
    ├── 2.1/
    │   ├── bind-pane-zoom.conf -> ../bind-pane-zoom.conf
    │   ├── keybinds-vi.conf -> ../keybinds-vi.conf
    │   ├── mouse-mode-new.conf -> ../mouse-mode-new.conf
    │   └── powerline-py3.conf -> ../powerline-py3.conf
    ├── 2.5/
    │   ├── bind-pane-zoom.conf -> ../bind-pane-zoom.conf
    │   ├── keybinds-vi.conf -> ../keybinds-vi.conf
    │   ├── mouse-mode-new.conf -> ../mouse-mode-new.conf
    │   └── powerline-py3.conf -> ../powerline-py3.conf
    ├── bind-pane-zoom.conf
    ├── keybinds-vi.conf
    ├── mouse-mode-new.conf
    ├── mouse-mode-old.conf
    ├── powerline-py3.conf
    ├── py2u.25.tmux.conf
    └── tmux.conf
    

    After link operation:

    /home/robo/.config/tmux
    ├── bind-pane-zoom.conf -> /home/robo/code/configs/tmux/bind-pane-zoom.conf
    ├── keybinds-vi.conf -> /home/robo/code/configs/tmux/keybinds-vi.conf
    ├── mouse-mode-new.conf -> /home/robo/code/configs/tmux/mouse-mode-new.conf
    ├── mouse-mode-old.conf -> /home/robo/code/configs/tmux/mouse-mode-old.conf
    ├── powerline-py3.conf -> /home/robo/code/configs/tmux/powerline-py3.conf
    ├── py2u.25.tmux.conf -> /home/robo/code/configs/tmux/py2u.25.tmux.conf
    └── tmux.conf -> /home/robo/code/configs/tmux/tmux.conf
    

    Notice the folders were not linked. They could have been included as links by simply changing the glob link target to be tmux/* instead of tmux/*.conf.

    If glob2 was in use, we could do recursive globs and make the glob match detection better.

    I don't think we should bother including glob2 because I think this feature set covers all the use cases we are concerned about, and the cost of inclusion of another library is not worth the benefit here IMO.

    Even without glob2, we can still do some interesting and more useful advanced things, like:

    link:
      ~/.config/:
        path: config/*/*
        create: true
    

    This example creates normal directories for everything at the ~/.config/* level, but then everything at the ~/.config/*/* level is now a symlink.

    Anyways,

    I looked at the test setup, but it's a bit too cryptic for me to dive into tonight. Could someone else try making the test cases that match these?

    opened by robobenklein 19
  • Fails to symlink Sublime Text config

    Fails to symlink Sublime Text config

    This is my dotbot config YAML.

    On line 24 there I try to symlink Sublime Text: ~/Library/Application\ Support/Sublime\ Text\ 3/Packages/User: sublime

    The path is correct. However it fails on me when I run my install script.

    ~/.dotfiles master
    ❯ ./install
    Link /Users/nikivi/.bash_profile -> /Users/nikivi/Dropbox/Mackup/.bash_profile not removed.
    Link /Users/nikivi/.gitmodules -> /Users/nikivi/dotfiles/zprezto/.gitmodules not removed.
    All targets have been cleaned
    Installing submodules [git submodule update --init --recursive]
    All commands have been executed
    Link exists ~/.zshrc -> /Users/nikivi/.dotfiles/zsh/zshrc.zsh
    Link exists ~/.gitconfig -> /Users/nikivi/.dotfiles/git/gitconfig
    Link exists ~/.gitignore_global -> /Users/nikivi/.dotfiles/git/gitignore_global
    Link exists ~/.hammerspoon -> /Users/nikivi/.dotfiles/hammerspoon
    Link exists ~/.ideavimrc -> /Users/nikivi/.dotfiles/idea/ideavimrc
    Link exists ~/Library/Application\ Support/Karabiner -> /Users/nikivi/.dotfiles/karabiner
    Nonexistent target ~/Library/Application\ Support/Sublime\ Text\ 3/Packages/User -> sublime
    Link exists ~/.config/nvim -> /Users/nikivi/.dotfiles/nvim
    Link exists ~/Library/Application\ Support/Code/User -> /Users/nikivi/.dotfiles/vscode
    Link exists ~/.tmux.conf -> /Users/nikivi/.dotfiles/tmux/tmux.conf
    Some links were not successfully set up
    
    ==> Some tasks were not executed successfully
    
    

    I am really not sure why that goes wrong. Shouldn't it work?

    Thank you for any help.

    opened by nikitavoloboev 18
  • Using dotbot for management config system-wide.

    Using dotbot for management config system-wide.

    Hi,

    I want to also use dotbot to management config system-wide instead of only the dotfiles.

    Say, for customized or revised stuff under /etc.

    How to achieve this purpose?

    opened by hongyi-zhao 17
  • Write an automatic dotfiles setup tool

    Write an automatic dotfiles setup tool

    Setting up Dotbot with an existing dotfiles repository is pretty quick.

    It would be cool to have a script to make it easier to set up a dotfiles repository with automatic Dotbot integration. The script could be an interactive program that the user guides in creating a git repository, moving existing dotfiles, and setting up an initial install.conf.yaml automatically based on the files that were moved.

    The script should be mostly automated, basically only asking the user for confirmation when doing things.

    enhancement 
    opened by anishathalye 14
  • Action create not handled

    Action create not handled

    my current file starts like this:

    - defaults:
        link:
            relink: true
    
    - clean: ['~']
    
    - create:
        - ~/.profile.d
        - ~/.local/bin
        - ~/.config/alacritty
        - ~/.config/htop
        - ~/.config/i3status
        - ~/.config/mako
        - ~/.config/redshift/hooks
        - ~/.config/rofi
        - ~/.config/sway
        - ~/.config/swaylock
        - ~/.config/yay
        - ~/.vim/pack/dotfiles
    
    # casual
    - link:
        ~/.dotfiles: ''
        ~/.bashrc: bash/bashrc
        ~/.bash_profile: bash/bashrc
    ...
    

    but it always outputs Action create not handled. So no folders are created. Even the example from the README.md does not work for me.

    maybe you could add a better explanation of the yaml configuration, because i do not see the problem.

    opened by Stunkymonkey 13
  • Platform specific link paths

    Platform specific link paths

    Hello, I've been using dotbot to manage my dotfiles for some time now. It has worked great thus far, since most applications read from a common configuration directory on all platforms. Sublime Text however, uses different directories depending on what platform you're on.

    Would it be feasible to add support for different destination paths depending on your OS? In Python there's sys.platform which contains a string representing the platform Python is running on.

    As for configuration syntax, I'm not sure. YAML has a construct where you can map keys to values, see example 2.6, but since dotbot maps links "the other way", the syntax wouldn't make sense (and is syntactically incorrect):

    - link:
        {
         linux: ~/.config/sublime-text-3,
         darwin: ~/Library/Application Support/Sublime Text 3,
         win32: ~/Sublime Text 3
        }: Sublime Text 3/
    

    If you flip the target and destination, I think it looks decent and you would still be able to configure links independently of the OS/platform. It would obviously break all existing configuration files, however.

    - link:
        Sublime Text 3/:
          linux: ~/.config/sublime-text-3,
          darwin: ~/Library/Application Support/Sublime Text 3,
          win32: ~/Sublime Text 3
        vim/: ~/.vim
    

    What do you think?

    enhancement 
    opened by ToJa92 13
  • [Request] force - does it overwrite any file?

    [Request] force - does it overwrite any file?

    Hi. In the YAML configuration file, if you say force: true, does that currently override the desination no matter what it is? I would like it to overwrite any existing symbolic link, but still be safe in all other ways. This is because I keep different versions of files in my dotbot repository, and several YAML files to install different versions, and would like to switch between them on the fly.

    I have a further feature request: Possibility to set the default behaviour so that one can limit the verbosity of the YAML file; i.e. set globally force: true or the like.

    Thanks for your consideration.

    enhancement 
    opened by Ploppz 13
  • Contribution Request: Another small plugin for core (details inside)

    Contribution Request: Another small plugin for core (details inside)

    Hello again, @anishathalye :wave:

    I recently finished up a small plugin locally for my own use and it's pretty darn useful for a particular use-case that I think might also be useful for a large portion of other users.

    This issue is just to gauge your interest in adding it into core (after a thorough code review, of course).

    The gist

    The plugin is called merge, and it is used like this (subject to changes):

    - merge:
        ~/dest: source
    

    As you can see, it works very much like link, however, the difference is that instead of linking dest to source, it "merges" the contents of source into dest (with symlinks dest -> src).

    The salient use-case for this is with dotfiles belonging in the $XDG_CONFIG_HOME or ~/.config directory.

    Instead of having to do this:

    - link:
        ~/.config/foo: config/foo
        ~/.config/bar: config/bar
        ~/.config/baz: config/baz
    

    You instead only have to do this:

    - merge:
        ~/.config: config
    

    Ah, much better :smile:

    Anyway, I just finished a decent working implementation last night for my own personal use that so far works nicely for my needs.

    Let me know if you might be interested in a PR for this. If not, no hard feelings. :+1:

    opened by dsifford 12
  • Named conditions (and block directive)

    Named conditions (and block directive)

    There's actually two requests here, but given the overlap I thought it suitable to just raise a single issue:

    1. Introduce a new type of plugin, condition plugins, that provide named, configurable conditions written in Python
    2. Introduce a new directive accepting: condition(s) to evaluate and a list of tasks/actions to execute when true

    Motivation

    There are currently 3 plugins linked from the official documentation that provide a condition and a container of tasks to be executed based on the outcome (if, ifarch, ifplatform). Dotbot users have already adopted this type of plugin so the interest is clearly there.

    The most flexible (and similar to this request) is the if plugin which takes a single shell command. Personally, there are at least a couple other conditions that I'd like to share with the community that aren't well suited to a shell one-liner.

    Notably, all three of the plugins also share the same issue. They don't propagate defaults from the parent context (and scope any new defaults set to the child context).

    I think it would be great to provide improved support for this pattern in the core dotbot project itself.

    Examples

    First, you can see below a couple options for a hypothetical new directive. I don't actually have a strong preference here. Whatever we think is least likely to conflict with a custom plugin name.

    - cond:
        if:
          tty: true
        then:
          - shell:
            - '<command_that_may_prompt_for_input>'
        else:
          - shell:
            - 'fallback_command'
    
    - block:
        when:
          tty: true
        tasks:
          - shell:
            - '<command_that_may_prompt_for_input>'
    

    Next you can see a few variants of the condition. I actually think it makes sense to support all of these.

    
    # For a string, it is interpreted as a shell command (providing consistency with the link directive's current if option) 
    - cond:
        if: "command -v ansible"
        then:
          - shell:
            - 'echo "I found ansible in $PATH"'
    
    # For a dict, use each key to find the corresponding Condition plugin and pass the value as arguments
    - cond:
        if:
          tty: true
          platform: 'arch'
        then:
          - shell:
            - 'echo "I am running in a TTY on ArchLinux!"'
    
    # An array is also supported for the same reasons as with dotbot tasks/actions.
    # It allows repeating a condition multiple times.
    - cond:
        if:
          - decrypted: "./config/file/a"
          - decrypted: "./config/file/b"
          - 'command -v "program_requiring_sensitive_config"'
        then:
          - shell:
            - 'echo "I have everything i need to configure <program_requiring_sensitive_config>."'
    

    I'm envisioning the condition plugins following the same pattern as those providing directives. A new test runner class would take the full condition and delegate to the appropriate condition plugins for evaluation.

    import dotbot
    import sys
    
    class TtyCondition(dotbot.Condition):
    
        def can_handle(self, directive):
            return "tty" == self._directive
    
        def handle(self, directive, data=True):
            expected = data if data is not None else True
            return expected == (sys.stdin.isatty() and sys.stdout.isatty() and sys.stderr.isatty())
    
    

    Other thoughts

    1. Unlike #225 the only potential for collision would be the single new directive. Behavior of tasks/actions remains the same as it is today.
    2. The if attribute in the current link plugin could also make use of the above conditions. As described above it would be backwards compatible.
    3. Why not just implement this as a new plugin? Configuring dotbot/dotbot-plugins as submodules seems to complicate import resolution. It's much easier if the new Condition abstract base class is in the dotbot repository itself. I'm fairly new to python, so maybe there's a pattern I'm missing that would actually make this easier.
    opened by gnfzdz 2
  • feat(shell): run shell command conditionally

    feat(shell): run shell command conditionally

    what

    • Run shell command conditionally (optional)
    • Update README docs

    how

    • the shell directive will check if the 'if' property is available.
      • If it is, it will run the command in 'if' property.
      • If the command results in a return 0 code or 'true', then the 'command' property will run.
      • If the condition to run the command is a non 0 code or 'false', the shell command won't run
    • dotbot config example:
    - shell:
        - command: echo "this is running on a MacOS"
          if: uname -s | grep -i "Darwin"
    
    • running shell command regardless
    - shell:
        - command: echo "This should run regardless"
    
    • dotbot config example: skipping command if false
    - shell:
        - command: echo "This command should be skipped"
          if: false
    

    why

    • can run the shell command conditionally
    • can use multiple dotbot configs to run different OS
      • Ex: incorporating it in https://github.com/ecarlson94/dotbot-template

    where

    • file changed in ./dotbot/plugins/shell.py

    usage

    Create dotbot config

    - shell:
        - command: echo "this is running on a MacOS"
          if: uname -s | grep -i "Darwin"
    
    - shell:
        - command: echo "This command should be skipped"
          if: false
    
    - shell:
        - command: apt update && apt upgrade -y
          if: lsb_release -i | grep -io 'debian'
          description: Update APT package repository
    
        - command: dnf update -y
          if: lsb_release -i | grep -io 'fedora'
          description: Update DNF package repository
    

    NOTE

    • The reason to wrap the if command in string, is to prevent Python from interpreting it.
      • str(item["if"]),
      • https://github.com/Clumsy-Coder/dotbot/blob/25ef5d5a5fed1e2f95e57115dfdbc8ba67cfc93f/dotbot/plugins/shell.py#L52
      • Try it without wrapping it in strings
        • https://github.com/Clumsy-Coder/dotbot/blob/974156ccdecee05eb9544b0b21cc0351428ddd43/dotbot/plugins/shell.py#L51-L57
        • run_if_result = dotbot.util.shell_command(
              item["if"],
              cwd=self._context.base_directory(),
              enable_stdin=False,
              enable_stdout=False,
              enable_stderr=stderr,
          )
          
        • Fails to run because error was thrown
          - shell:
              - command: echo "This command should be skipped"
                if: false
          
        • Works if wrapping false in strings
          - shell:
              - command: echo "This command should be skipped"
                if: "false"
          
    opened by Clumsy-Coder 3
  • Globbing with single file in source directory fails

    Globbing with single file in source directory fails

    Running d2f76a2, I found that this from the examples:

    link:
        ~/.config/:
            glob: true
            path: config/*
    

    ... does not work when you only have one file in the config directory.

    Example:

    $ cd ~/.dotfiles
    $ mkdir config
    $ touch config/test.ini
    $ ./install
    ...
    Ambiguous action requested.
    No wildcard in glob, directory use undefined: ~/.config/ -> ['config/test.ini']
    Did you want to link the directory or into it?
    ...
    

    As soon as you add another file, globbing works.

    opened by DivineDominion 1
  • feature-request: run a command IF a condition passes

    feature-request: run a command IF a condition passes

    just like LINK directive, there should one for running individual commands for SHELL directive.

    Ex:

    - shell:
        - command: apt update && apt upgrade -y
          if: lsb_release -i | grep -io 'debian'
          description: Update APT package repository
    
        - command: dnf update -y
          if: lsb_release -i | grep -io 'fedora'
          description: Update DNF package repository
    

    why

    • provides a better flow of running dotbot process
    • can contain similar commands in a single shell directive.
      • Ex: shell directive for updating repositories
      • Ex: installing a package using package manager in one OS, downloading a script from another OS
    opened by Clumsy-Coder 3
  • Add the possibility of specifying more than one source path ...

    Add the possibility of specifying more than one source path ...

    … in which case the destination is assumed to be a folder in which source paths are linked.

    This is to solve #301. The diff on github is borked and shows lots of changes, but the changes are actually quite small:

    • Link._default_sources was changed to return a list of sources rather than a single source
    • path is changed to paths and all of the linking action is wrapped in an additional loop that iterates over source paths
    • If globbing is active, additional tests are added for edge cases
    • If there is no globbing and several paths are given, the destination path is assumed to be a directory, so we adapt the destination path accordingly

    If you're ok with the approach I'll also edit the documentation. I don't really understand how the tests work, but I'll also try to add a few test cases when I have time to look more into it.

    enhancement 
    opened by ldorigo 2
Owner
Anish Athalye
grad student @mit-pdos
Anish Athalye
Dotfiles & list of programs

dotfiles & list of programs So I wanted to just backup my most used files. I have a bad habit, sometimes I get tired of a distro and do a wipe and sta

null 2 Sep 4, 2022
My Dotfiles of Arco Linux

Arco-DotFiles My Dotfiles of Arco Linux Apps Used Htop LightDM lightdm-webkit2-greeter Alacritty Qtile Cava Spotify nitrogen neofetch Spicetify Thunar

$BlueDev5 6 Dec 11, 2022
dotfiles - Cristian Valero Abundio

In this repository you can find various configurations to configure your Linux operating system, preferably ArchLinux and its derivatives.

Cristian Valero Abundio 1 Jan 9, 2022
Pseudometa's dotfiles

pseudometa's dotfiles Table of Contents Why this repository? How this Repository works Special Explanations Got an idea for an improvement? Contact Wh

pseudometa 23 Dec 27, 2022
switching computer? changing your setup? You need to automate the download of your current setup? This is the right tool for you :incoming_envelope:

?? setup_shift(SS.py) switching computer? changing your setup? You need to automate the download of your current setup? This is the right tool for you

Mohamed Elfaleh 15 Aug 26, 2022
A python tool that creates issues in your repos based on TODO comments in your code

Krypto A neat little sidekick python script to create issues on your repo based on comments left in the code on your behalf Convert todo comments in y

Alex Antoniou 4 Oct 26, 2021
A tool to flash .ofp files in bootloader mode without needing MSM Tool, an alternative to official realme tool

Oppo/Realme Flash .OFP File on Bootloader A tool to flash .ofp files in bootloader mode without needing MSM Tool, an alternative to official realme to

Italo Almeida 70 Jan 2, 2023
Purge your likes and wall comments from VKontakte. Set yourself free from your digital footprint.

vk_liberator Regain liberty in the cruel social media world. This program assists you with purging your metadata from Russian social network VKontakte

null 20 Jun 11, 2021
🗽 Like yarn outdated/upgrade, but for pip. Upgrade all your pip packages and automate your Python Dependency Management.

pipupgrade The missing command for pip Table of Contents Features Quick Start Usage Basic Usage Docker Environment Variables FAQ License Features Upda

Achilles Rasquinha 529 Dec 31, 2022
Add your recently blog and douban states in your GitHub Profile

Add your recently blog and douban states in your GitHub Profile

Bingjie Yan 4 Dec 12, 2022
edgetest is a tox-inspired python library that will loop through your project's dependencies, and check if your project is compatible with the latest version of each dependency

Bleeding edge dependency testing Full Documentation edgetest is a tox-inspired python library that will loop through your project's dependencies, and

Capital One 16 Dec 7, 2022
An app that mirrors your phone to your compute and maps controller input to the screen

What is 'Dragalia Control'? An app that mirrors your phone to your compute and maps controller input to the screen. Inputs are mapped specifically for

null 1 May 3, 2022
An assistant to guess your pip dependencies from your code, without using a requirements file.

Pip Sala Bim is an assistant to guess your pip dependencies from your code, without using a requirements file. Pip Sala Bim will tell you which packag

Collage Labs 15 Nov 19, 2022
Helper to organize your windows on your desktop.

The script of positionsing windows on the screen. How does it work? Select your window to move/res

Andrii D. 1 Jul 9, 2021
Its a simple and fun to use application. You can make your own quizes and send the lik of the quiz to your friends.

Quiz Application Its a simple and fun to use application. You can make your own quizes and send the lik of the quiz to your friends. When they would a

Atharva Parkhe 1 Feb 23, 2022
Display your data in an attractive way in your notebook!

Bloxs Bloxs is a simple python package that helps you display information in an attractive way (formed in blocks). Perfect for building dashboards, re

MLJAR 192 Dec 28, 2022
Aero is an open source airplane intelligence tool. Aero supports more than 13,000 airlines and 250 countries. Any flight worldwide at your fingertips.

Aero Aero supports more than 13,000 airlines and 250 countries. Any flight worldwide at your fingertips. Features Main : Flight lookup Aircraft lookup

Vickey 비키 4 Oct 27, 2021
A tool to build reproducible wheels for you Python project or for all of your dependencies

asaman: Amra Saman (আমরা সমান) This is a tool to build reproducible wheels for your Python project or for all of your dependencies. What this means is

Kushal Das 14 Aug 5, 2022
ChainJacking is a tool to find which of your Go lang direct GitHub dependencies is susceptible to ChainJacking attack.

ChainJacking is a tool to find which of your Go lang direct GitHub dependencies is susceptible to ChainJacking attack.

Checkmarx 36 Nov 2, 2022