Code Monkey home page Code Monkey logo

dfm's Introduction

dfm

A dotfile manager for lazy people and pair programmers.

Table of Contents

Features

dfm supports these features that I was unable to find in other Dotfile Management solutions.

Multiple dotfile profiles

dfm's core feature is the idea of profiles. Profiles are simply a collection of dotfiles that dfm manages and links in the $HOME directory or configuration directories. This means that you can have multiple profiles and overlap them.

This feature is hard to describe, so I will illustrate it's usefulness with two use cases:

The work profile

I use one laptop for work and personal projects in my dfm profiles I have my personal profile chasinglogic which contains all my dotfiles for Emacs, git, etc. and a "work" profile which only has a .gitconfig that has my work email in it. So my profile directory looks like this:

profiles/
├── chasinglogic
│   ├── agignore
│   ├── bash
│   ├── bashrc
│   ├── gitconfig
│   ├── gnupg
│   ├── password-store
│   ├── pypirc
│   ├── spacemacs.d
│   └── tmux.conf
└── work
    └── gitconfig

Since dfm when linking only overwrites the files which are in the new profile, I can run dfm link work and still have access to my emacs configuration but my gitconfig has been updated to use my work email. Similarly when I leave work I just dfm link chasinglogic to switch back.

See profile modules for an even better solution to this particular use case.

Pair programming

The original inspiration for this tool was pair programming with my friend lionize. lionize has a dotfiles repository so I can clone it using the git backend for dfm with dfm clone --name lionize https://github.com/lionize/dotfiles.

Now our profile directory looks like:

profiles/
├── chasinglogic
│   ├── .dfm.yml
│   ├── .git
│   ├── .gitignore
│   ├── agignore
│   ├── bash
│   ├── bashrc
│   ├── gitconfig
│   ├── gnupg
│   ├── password-store
│   ├── pypirc
│   ├── spacemacs.d
│   └── tmux.conf
├── lionize
│   ├── .agignore
│   ├── .git
│   ├── .gitconfig
│   ├── .gitignore_global
│   ├── .gitmessage
│   ├── .scripts
│   ├── .tmux.conf
│   ├── .vim
│   ├── .vimrc -> ./.vim/init.vim
│   └── .zshrc
└── work
    ├── .git
    └── gitconfig

Now when I'm driving I simply dfm link chasinglogic and when passing back to lionize he runs dfm link lionize and we don't have to mess with multiple machines vice versa.

Profile modules

dfm supports profile modules which can be either additional dotfiles profiles as accepted by the dfm clone command or can be any git repository such as Spacemacs. You can get more info about how to use them and configure them in Configuration

Pre and Post command hooks

dfm supports pre and post command hooks. that allows you to specify before and after command scripts to run. For example, I use a profile module to keep certain ssh keys in an encrypted git repository. Whenever I run the dfm sync command I have hooks which fix the permissions of the keys and ssh-add them to my ssh agent. You can read about how to write your own hooks in Configuration

Respects $XDG_CONFIG_HOME

dfm respects dotfiles which exist in the $XDG_CONFIG_HOME directory, meaning if in your repository you have a folder named config or .config it'll translate those into the $XDG_CONFIG_HOME directory automatically. Similarly when using dfm add if inside your $XDG_CONFIG_HOME or $HOME/.configuration directories it'll add those to the repository appropriately.

Skips relevant files

dfm by default will skip multiple relevant files.

  • .git

dfm will skip the .git directory so your $HOME directory isn't turned into a git repository.

  • .gitignore

If you would like to store a global .gitignore file you can either omit the leading dot (so just gitignore) or name the global one .ggitignore and dfm will translate the name for you. Otherwise it assumes that .gitignore is the gitignore for the profile's repository and so skips it.

  • README

Want to make a README for your dotfiles? Go ahead! As long as the file name starts with README dfm will ignore it. So README.txt README.md and README.rst or whatever other permutations you can dream up all work.

  • LICENSE

You should put a LICENSE on all code you put on the internet and some dotfiles / configurations are actual code (See: Emacs). If you put a LICENSE in your profile dfm will respect you being a good open source citizen and not clutter your $HOME directory.

  • .dfm.yml

This is a special dfm file used for hooks today and in the future for other ways to extend dfm. As such dfm doesn't put it in your $HOME directory.

Custom mappings

The above ignores are implemented as a dfm feature called Mappings. You can write your own mappings to either skip, skip based on platform or translate files to different locations than dfm would normally place them. You can read how to configure your own mappings in Configuration

Encrypted Dotfiles

Using hooks and mappings you can integrate GPG with DFM to have an encrypted dotfiles repository.

If you add the following .dfm.yml to your repository per the Configuration documentation:

---
mappings:
  - match: '.*.gpg'
    skip: true

hooks:
  before_sync:
    - interpreter: /bin/bash -c
      script: |
        echo "encrypting files..."
        for file in $(find . -not -name '*.gpg' -not -name '.dfm.yml' -not -name '.gitignore' -not -path './.git/*'); do
          echo "Encrypting $file to ${file/.gpg/}"
          gpg --batch --yes --encrypt ${file/.gpg/}
        done
  after_sync:
    - interpreter: /bin/bash -c
      script: |
        for file in $(git ls-files | grep -v .dfm.yml | grep -v .gitignore); do
          gpg --batch --yes --decrypt -o ${file/.gpg/} $file
        done

And the following .gitignore file:

*
!*/
!.gitignore
!.dfm.yml
!*.gpg

Then when running dfm sync DFM will run the gpg command to encrypt all your files, then git will ignore all non-GPG encrypted files (due to the .gitignore), and after syncing DFM will decrypt all the GPG encrypted files.

This all happens before linking, when you run dfm link DFM will ignore all gpg encrypted files due to the mapping configuration. It will then only link the unencrypted versions into your home directory.

Installation

Install from Release

dfm is available on Github Releases and should be installed from there.

The latest release is available here.

Download the archive that is appropriate for your platform and extract the binary into your $PATH. A common valid path location is /usr/local/bin.

You can run these commands to automate this install (on most platforms, it does not always work):

platform=$(uname -s)
arch=$(uname -m)
# If you're running on an M1 Macbook run this:
# arch="x86_64"
download_url=$(curl -s https://api.github.com/repos/chasinglogic/dfm/releases/latest | grep "browser_download_url.*$arch.*${platform,,}" | cut -d : -f 2,3 | sed 's/"//g' | xargs)
curl -L -o /tmp/dfm.tar.gz "$download_url"
tar -C /tmp -xzvf /tmp/dfm.tar.gz
mv $(find /$(readlink /tmp) -perm +111 -type f -name dfm 2>/dev/null) /usr/local/bin/

Install from Source

You will need a rust compiler to build dfm from source.

Clone the repository and run ./scripts/local_install.sh:

git clone https://github.com/chasinglogic/dfm
cd dfm
./scripts/local_install.sh

It's possible that for your system you will need to run the install script with sudo.

Usage

Usage:
    dfm [options] <command> [<args>...]
    dfm help
    dfm sync
    dfm link <profile>

Dotfile management written for pair programmers. Examples on getting
started with dfm are avialable at https://github.com/chasinglogic/dfm

Options:
    -v, --verbose  If provided print more logging info
    --debug        If provided print debug level logging info
    -h, --help     Print this help information

Commands:
    help           Print usage information about dfm commands
    sync (s)       Sync your dotfiles
    add (a)        Add the file to the current dotfile profile
    clean (x)      Clean dead symlinks
    clone (c)      Use git clone to download an existing profile
    git (g)        Run the given git command on the current profile
    init (i)       Create a new profile
    link (l)       Create links for a profile
    list (ls)      List available profiles
    remove (rm)    Remove a profile
    run-hook (rh)  Run dfm hooks without using normal commands
    where (w)      Prints the location of the current dotfile profile

See 'dfm help <command>' for more information on a specific command.

Quick start

Quick start (Existing dotfiles repository)

If you already have a dotfiles repository you can start by cloning it using the clone command.

SSH URLs will work as well.

dfm clone https://github.com/chasinglogic/dotfiles

If you're using GitHub you can shortcut the domain:

dfm clone chasinglogic/dotfiles

If you want to clone and link the dotfiles in one command:

dfm clone --link chasinglogic/dotfiles

You may have to use --overwrite as well if you have existing non-symlinked versions of your dotfiles

Once you have multiple profiles you can switch between them using dfm link

dfm link some-other-profile

See the Usage Notes below for some quick info on what to expect from other dfm commands.

Quick Start (No existing dotfiles repository)

If you don't have a dotfiles repository the best place to start is with dfm init

dfm init my-new-profile

Then run dfm link to set it as the active profile, this is also how you switch profiles

dfm link my-new-profile

Once that's done you can start adding your dotfiles

dfm add ~/.bashrc

Alternatively you can add multiple files at once

dfm add ~/.bashrc ~/.vimrc ~/.vim ~/.emacs.d

Then create your dotfiles repository on GitHub. Instructions for how to do that can be found here. Once that's done get the "clone" URL for your new repository and set it as origin for the profile:

Note: When creating the remote repository don't choose any options such as "initialize this repository with a README" otherwise git'll get cranky when you add the remote because of a recent git update and how it handles unrelated histories if you do don't worry the linked post explains how to get past it.

dfm git remote add origin <your clone URL>

Then simply run dfm sync to sync your dotfiles to the remote

dfm sync

Now you're done!

Configuration

dfm supports a .dfm.yml file in the root of your repository that changes dfm's behavior when syncing and linking your profile. This file will be ignored when doing a dfm link so won't end up in your home directory. The .dfm.yml can be used to configure these features:

Modules

Modules in dfm are sub profiles. They're git repositories that are cloned into a a special directory: $XDG_CONFIG_HOME/dfm/modules. They're shared across profiles so if two dotfile profiles have the same module they'll share that module.

The syntax for defining a minimum module is as follows:

modules:
    - repository: [email protected]:chasinglogic/dotfiles

This would clone my dotfiles repository as a module into $XDG_CONFIG_HOME/dfm/modules/chasinglogic. If I wanted to use a unique name or some other folder name so it wouldn't be shared you can specify an additional option name:

modules:
    - repository: [email protected]:chasinglogic/dotfiles
      name: chasinglogic-dotfiles

Which would instead clone into $XDG_CONFIG_HOME/dfm/modules/chasinglogic-dotfiles. You can define multiple modules:

modules:
    - repository: [email protected]:chasinglogic/dotfiles
      name: chasinglogic-dotfiles
    - repository: [email protected]:lionize/dotfiles

Make sure that you specify a name if the resulting clone location as defined by git would conflict as we see here. Both of these would have been cloned into dotfiles which would cause the clone to fail for the second module if we didn't specify name for chasinglogic's dotfiles.

An additional use for modules is that of a git repository you want to clone but not link. An example use would be for downloading Spacemacs or any such community configuration like oh-my-zsh, etc.

modules:
    - repo: [email protected]:syl20bnr/spacemacs
      link: none
      pull_only: true
      location: ~/.emacs.d

Here we specify a few extra keys. There purpose should be self explanatory but if you're curious below is a detailed explanation of all keys that each module configuration supports.

Modules work just like any other dfm profile so if a module you're pulling in has a .dfm.yml in it that will be loaded and executed accordingly. Including pulling down any modules it defines.

Available keys

repo

Required, this is the git repository to clone for the module.

name

This changes the cloned name. This only has an effect if location isn't provided. Normally a git repository would be cloned into $XDG_CONFIG_HOME/dfm/modules and the resulting folder would be named whatever git decides it should be based on the git URL. If this is provided it'll be cloned into the modules directory with the specified name. This is useful if multiple profiles use the same module.

location

If provided module will be cloned into the specified location. You can use the ~ bash expansion here to represent $HOME. No other expansions are available. This option is useful for cloning community configurations like oh-my-zsh or spacemacs.

link

Determines when to link the module. Link in this context means that it'll be treated like a normal dotfile profile, so all files will go through the same translation rules as a regular profile and be linked accordingly. Available values are post, pre, and none. post is the default and means that the module will be linked after the parent profile. "pre" means this will be linked before the parent profile, use this if for instance you want to use most files from this profile and override a few files with those from the parent file since dfm will overwrite the links with the last one found. "none" means the module is not a dotfiles profile and shouldn't be linked at all, an example being community configuration repositories like oh-my-zsh or spacemacs.

pull_only

If set to true won't attempt to push any changes. It's important to know that dfm always tries to push to origin master, so if you don't have write access to the repository or don't want it to automatically push to master then you should set this to true. This is useful for community configuration repositories.

mappings

A list of file mappings as described below in Mappings. Modules do not inherit parent mappings, they do however inherit the default mappings as described in Skips Relevant Files

Mappings

Mappings are a way of defining custom file locations. To understand mappings one must understand dfm's default link behavior:

Default behavior

For an example let's say you have a file named my_config.txt in your dotfile repository. dfm will try and translate that to a new location of $HOME/.my_config.txt. It'll then create a symlink at that location pointing to my_config.txt in your dotfile repository.

Using mappings

With mappings you can replace this behavior and make it so dfm will link my_config wherever you wish. This is useful if you need to store config files that are actually global. Such as configuration files that would go into /etc/ or if you want to sync some files in your repo but not link them.

Here is a simple example:

mappings:
  - match: .config/some-dir
    link_as_dir: true
  - match: my_global_etc_files
    target_dir: /etc/
  - match: something_want_to_skip_but_sync
    skip: true
  - match: something_only_for_macos
    target_os: "Darwin"
  - match: some_file_for_mac_and_linux_only
    target_os:
        - "Linux"
        - "Darwin"
  - match: some_specific_translation_for_mac
    dest: ~/.mac_os_dotfile
    target_os:
        - "Darwin"

Here dfm uses the match as a regular expression to match the file paths in your dotfile repository. When it finds a path which matches the regular expression it adds an alternative linking behavior. For anything where skip is true it simply skips linking. For anything with target_dir that value will override $HOME when linking. For anything with a target_os value the file will only be linked if dfm is being run on the given os.

Link as Dir Mappings

Above you can see a mapping using the link_as_dir option. When this is set to true for a mapping the match: value will be used as a directory relative to the root of the dotfile repo and will be linked as a directory. Normally DFM only links files, this can cause issues with some types of configuration where you regularly generate files like snippet tools. Consider the following dotfiles in a dotfile repository:

$REPO/.config/nvim
├── UltiSnips
│   ├── gitcommit.snippets
│   └── python.snippets

That would produce the following links in $HOME/.config/nvim:

$HOME/.config/nvim
├── UltiSnips
│   ├── gitcommit.snippets -> $HOME/.config/dfm/profiles/chasinglogic/.config/nvim/UltiSnips/gitcommit.snippets
│   └── python.snippets -> $HOME/.config/dfm/profiles/chasinglogic/.config/nvim/UltiSnips/python.snippets

Every time you used :UltiSnipsEdit to create a new snippet file type you'd have to then remember to manually move that into your dotfile repository and re-run dfm link. To solve this problem you can use the following mapping in your .dfm.yml you can instead link UltiSnips the directory instead of it's files:

mappings:
  - match: .config/nvim/UltiSnips
    link_as_dir: true

Now DFM links the $HOME/.config/nvim/UltiSnips directory to the $REPO/.config/nvim/UltiSnips:

$HOME/.config/nvim
├── UltiSnips -> $HOME/.config/dfm/profiles/chasinglogic/.config/nvim/UltiSnips

Available configuration

Mappings support the following configuration options:

match

Match is a regular expression used to match the file path of any files in your dotfile repository. This is used to determine if the custom linking behavior for a file should be used.

These are python style regular expressions and are matched using the re.findall method so are by default fuzzy matching.

skip

If provided the file/s will not be linked.

dest

The new full path to the file. This can be used to completely change a file's name or put it in a wholly new location. This is more explicity than target_dir and covers cases that target_dir is not suited for (for example if a file is a dotfile on one OS but not on another.)

target_dir

Where to link the file to. The ~ expansion for $HOME is supported here but no other expansions are available. It is worth noting that if you're using ~ in your target_dir then you should probably just create the directory structure in your git repo.

target_os

A string or list of strings matching the OS's to link this file on. A non-exhaustive list of common values are: Linux, Darwin, or Windows. This matches the string returned by Python's platform.system() function.

Hooks

Hooks in dfm are used for those few extra tasks that you need to do whenever your dotfiles are synced or linked.

An example from my personal dotfiles is running an Ansible playbook whenever I sync my dotfiles. To accomplish this I wrote an after_sync hook as follows:

hooks:
  after_sync:
    - ansible-playbook ansible/dev-mac.yml

Now whenever I sync my dotfiles Ansible will run my dev-mac playbook to make sure that my packages etc are also in sync!

The hooks option is just a YAML map which supports the following keys: after_link, before_link, after_sync, and before_sync. The values of any of those keys is a YAML list of strings which will be executed in a shell via /bin/sh -c '$YOUR COMMAND'. An example would be:

hooks:
  after_link:
    - ls -l
    - whoami
    - echo "All done!"

All commands are ran with a working directory of your dotfile repository and the current process environment is passed down to the process so you can use $HOME etc environment variables in your commands.

By default the comamnds will run with the interpreter /bin/sh -c. So the expanded comamnd line for the first hook above would be:

/bin/sh -c 'ls -l'

If you want to use a different interpreter you can use instead use this hook format:

hooks:
  after_link:
    - interpreter: python -c
      script: |
        print("hello world from Python")

You may want to do this in cases where you need complex logic (like that which should live in a Python script) or for example on Debian based systems which use dash instead of bash as the /bin/sh interpreter and so have a very limited expansion feature set.

Contributing

  1. Fork it!
  2. Create your feature branch: git checkout -b my-new-feature
  3. Commit your changes: git commit -am 'Add some feature'
  4. Push to the branch: git push origin my-new-feature
  5. 🔥 Submit a pull request :D 🔥

All pull requests should go to the develop branch not master. Thanks!

License

This code is distributed under the GNU General Public License

    Copyright (C) 2018 Mathew Robinson

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

dfm's People

Contributors

chasinglogic avatar dependabot[bot] avatar henrik avatar with-heart avatar xakv avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

dfm's Issues

add remove command

need a command for removing a profile and all aliases attached to it.

dfm add ~/.emacs.d does not add to tracked files.

Traceback (most recent call last):
File "/usr/local/bin/dfm", line 11, in
sys.exit(dfm())
File "/usr/local/lib/python3.6/site-packages/click/core.py", line 722, in call
return self.main(*args, **kwargs)
File "/usr/local/lib/python3.6/site-packages/click/core.py", line 697, in main
rv = self.invoke(ctx)
File "/usr/local/lib/python3.6/site-packages/click/core.py", line 1066, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/usr/local/lib/python3.6/site-packages/click/core.py", line 895, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/usr/local/lib/python3.6/site-packages/click/core.py", line 535, in invoke
return callback(*args, **kwargs)
File "/usr/local/lib/python3.6/site-packages/dfm/cli.py", line 182, in add
add_file(f, profile)
File "/usr/local/lib/python3.6/site-packages/dfm/lib/init.py", line 147, in add_file
link_file(new_path, bytes(old_file), force=True)
File "/usr/local/lib/python3.6/site-packages/dfm/lib/init.py", line 96, in link_file
if CONFIG['verbose']:
KeyError: 'verbose'

It does end up linking the file, but it doesn't look like it adds it to git tracking. I tried to commit afterwards, and my .emacs.d was untracked.

"Modules"

Allow modules which are chunks of configuration pulled from multiple repos / locations. For instance dfm using a specific to dfm yml file can pull my emacs.d from a different repo from my vim config from my bash scripts etc. It allows me to specify where to pull and where to put the files when pulled.

Pluggable backends

Make backends pluggable so you can use either Dropbox, git, mercurial, or some other syncing-ish solution with dfm.

Interrupting with ctrl+c throws an ugly stack trace

  File "/home/chasinglogic/.local/bin/dfm", line 10, in <module>
    sys.exit(main())
  File "/home/chasinglogic/.local/lib/python3.7/site-packages/dfm/cli/__init__.py", line 81, in main
    command_mod.run(docopt(command_mod.__doc__, argv=argv))
  File "/home/chasinglogic/.local/lib/python3.7/site-packages/dfm/cli/utils.py", line 71, in wrapper
    return wrapped(*args, **kwargs)
  File "/home/chasinglogic/.local/lib/python3.7/site-packages/dfm/cli/sync_cmd.py", line 18, in run
    profile.sync()
  File "/home/chasinglogic/.local/lib/python3.7/site-packages/dfm/dotfile.py", line 434, in sync
    super().sync()
  File "/home/chasinglogic/.local/lib/python3.7/site-packages/dfm/dotfile.py", line 248, in sync
    self.commit_msg = input("Commit message: ")
KeyboardInterrupt

Should probably just catch KeyboardInterrupts and exit with code 1

dfm ls fails if no profiles have been initialized

[chasinglogic@localhost ~]$ dfm ls
Traceback (most recent call last):
  File "/home/chasinglogic/.local/bin/dfm", line 10, in <module>
    sys.exit(main())
  File "/home/chasinglogic/.local/lib/python3.7/site-packages/dfm/cli/__init__.py", line 81, in main
    command_mod.run(docopt(command_mod.__doc__, argv=argv))
  File "/home/chasinglogic/.local/lib/python3.7/site-packages/dfm/cli/list_cmd.py", line 14, in run
    for profile in os.listdir(os.path.join(dfm_dir(), 'profiles')):
FileNotFoundError: [Errno 2] No such file or directory: '/home/chasinglogic/.config/dfm/profiles'
[chasinglogic@localhost ~]$ 

needs to create the requisite directories or error more cleanly

Add remove file command

Should be able to remote a file from the profile easily using dfm

should probably look like
dfm rmf file-name

since rm is already used.

This issue would be perfect for fixing at Ohio Linux Fest!

cannot add dotfiles to profile

I am trying to add .bashrc and my .doom.d directory to a new profile. I used dfm init to create a completely new profile since i don't have an existing dotfile repo.

[ynavott-gram@ynavott-gram-ubuntu: ~]$ dfm init personal-dotfiles
Initialized empty Git repository in /home/ynavott-gram/.config/dfm/profiles/personal-dotfiles/.git/
[ynavott-gram@ynavott-gram-ubuntu:~]$ dfm -v link personal-dotfiles
[ynavott-gram@ynavott-gram-ubuntu:~]$ dfm add ~/.bashrc
/home/ynavott-gram/.config/dfm/profiles/personal-dotfiles:
fatal: 'origin' does not appear to be a git repository
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

For some reason the link command didn't give any output even with the -v option, but i tried continuing and trying to add .bashrc to the profile. it did not work. I then tried adding a directory instead of a file and got a different error.

[ynavott-gram@ynavott-gram-ubuntu:~]$ dfm add ~/.doom.d
Traceback (most recent call last):
  File "/usr/local/bin/dfm", line 11, in <module>
    load_entry_point('dfm==7.4.1', 'console_scripts', 'dfm')()
  File "/usr/local/lib/python3.7/dist-packages/dfm-7.4.1-py3.7.egg/dfm/cli/__init__.py", line 81, in main
    command_mod.run(docopt(command_mod.__doc__, argv=argv))
  File "/usr/local/lib/python3.7/dist-packages/dfm-7.4.1-py3.7.egg/dfm/cli/utils.py", line 71, in wrapper
    return wrapped(*args, **kwargs)
  File "/usr/local/lib/python3.7/dist-packages/dfm-7.4.1-py3.7.egg/dfm/cli/add_cmd.py", line 30, in run
    shutil.copytree(oldfile, newfile)
  File "/usr/lib/python3.7/shutil.py", line 324, in copytree
    os.makedirs(dst)
  File "/usr/lib/python3.7/os.py", line 221, in makedirs
    mkdir(name, mode)
FileExistsError: [Errno 17] File exists: '/home/ynavott-gram/.config/dfm/profiles/personal-dotfiles/..'

Considering the content of the first error i got for trying to add .bashrc, I tried running the add command with sudo, and the response is that there is no linked profile.

[ynavott-gram@ynavott-gram-ubuntu:~]$ sudo dfm -v add ~/.bashrc
[sudo] password for ynavott-gram: 
no profile active, run dfm link to make one active

I then tried linking the profile using sudo and got an error as well.


[ynavott-gram@ynavott-gram-ubuntu:~]$ sudo dfm -v link personal-dotfiles
Traceback (most recent call last):
  File "/usr/local/bin/dfm", line 11, in <module>
    load_entry_point('dfm==7.4.1', 'console_scripts', 'dfm')()
  File "/usr/local/lib/python3.7/dist-packages/dfm-7.4.1-py3.7.egg/dfm/cli/__init__.py", line 81, in main
    command_mod.run(docopt(command_mod.__doc__, argv=argv))
  File "/usr/local/lib/python3.7/dist-packages/dfm-7.4.1-py3.7.egg/dfm/cli/link_cmd.py", line 24, in run
    profile = switch_profile(args["<profile>"])
  File "/usr/local/lib/python3.7/dist-packages/dfm-7.4.1-py3.7.egg/dfm/cli/utils.py", line 33, in switch_profile
    with open(path, "w+") as state_file:
FileNotFoundError: [Errno 2] No such file or directory: '/root/.config/dfm/state.json'
Looks like running `dfm link` with sudo isn't even supposed to work in the first place. 

Seems like the link command is to blame and doesn't execute properly and the profile isn't actually linked.

Error on `dfm init`

Error

I just installed dfm. The folder ~/.config/dfm does not exist.

[:~] $ dfm --version
dfm version 8.4.2
[:~] $ dfm init default
fatal: not a git repository (or any of the parent directories): .git
Traceback (most recent call last):
  File "/usr/local/bin/dfm", line 8, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.10/site-packages/dfm/cli/__init__.py", line 95, in main
    command_mod.run(docopt(command_mod.__doc__, argv=argv))
  File "/usr/local/lib/python3.10/site-packages/dfm/cli/init_cmd.py", line 25, in run
    Profile.new(new_profile_dir)
  File "/usr/local/lib/python3.10/site-packages/dfm/profile.py", line 270, in new
    profile = cls.default(where)
  File "/usr/local/lib/python3.10/site-packages/dfm/profile.py", line 179, in default
    return cls(df_repo, link_manager, hooks, **extras)
  File "/usr/local/lib/python3.10/site-packages/dfm/profile.py", line 88, in __init__
    self.repo = self.df_repo.get_remote()
  File "/usr/local/lib/python3.10/site-packages/dfm/repo.py", line 45, in get_remote
    return subprocess.check_output(
  File "/usr/lib64/python3.10/subprocess.py", line 420, in check_output
    return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
  File "/usr/lib64/python3.10/subprocess.py", line 524, in run
    raise CalledProcessError(retcode, process.args,
subprocess.CalledProcessError: Command '['git', 'remote', 'get-url', 'origin']' returned non-zero exit status 128.

Apparently dfm is trying to get the URL of the remote origin, which clearly I haven't had the time to define.

What am I doing wrong? Is this the wrong procedure to start using dfm with a new profile for the first time?

issue might be I am an idiot

I tried to follow the starting with a blank repo instructions. First, I forgot my github password. I usually use ssh. I have a password on my ssh key, no big deal. I added bash as an ssh agent, ran ssh-add, but now dfm would not allow me to add the remote. I thought to myself, "dude, you watched this guy's git talk, you got this," and dove down into my meager git skills. cd'ing into the profile i had added for dfm, i was able to run git remote add origin, and I am in business! Well, sort of. Because I created the new dotfile repo, I had to pull down the readme. I ran into a merge conflict message I had to google, and then added --allow-unrelated-histories to my git pull command. Then finally, dfm push worked as expected.

I have to say, I have tried yadm as well (another dotfiles manager) and couldnt get my head around the concept enough to say I liked using it. I like the concept you have here, it makes more sense to me, but I don't know if the README section needs some caveats for "advanced idiots" like myself, or if I'm just really bad at git in general.

I wont be upset if you just close out this issue with a note that says "user is way too dumb", but if there is something I can add to the readme, or maybe improve some error message, I will be happy to do that and send a pull request. I dont know golang, but how hard can it be for an intelligent chap like myself? :)

If it helps you to suss out what I did wrong, here is a bit of my history, note I skipped to the last, semi-successful attempt;

174 dfm add .dir_colors
175 dfm add .Xresources
176 dfm add .taskrc .tmux.conf .vimrc .kshrc
177 dfm commit -am "init again?"
178 dfm remote #tried to get dfm to tell me what the remote was
179 dfm remote [email protected]:AffableZonkey/differntdots.git
180 dfm remote add spatterjay [email protected]:AffableZonkey/differntdots.git
181 dfm remote add [email protected]:AffableZonkey/differntdots.git
184 cd spatterjay/
186 git remote
187 git remote add origin [email protected]:AffableZonkey/differntdots.git
188 dfm push
189 dfm pull
190 git checkout master
192 git branch -l
193 git pull
194 git pull --set-upstream-to=origin/spatterjay master
196 git branch -l
197 dfm pull
198 git merge
199 git remote add master [email protected]:AffableZonkey/differntdots.git
206 git branch --set-upstream-to=origin/master origin
207 git pull origin master
208 git pull origin master --allow-unrelated-histories
209 dfm push

"ERROR: unexpected end of JSON input"

I have download your nice program, but I could not use it.
I have try with the github version (with 'go get') and the release version (on a Ubuntu 17.04, with go version 1.7.4, and also with go 1.9.2)

The output is the following:
'$ dfm
Which backend would you like to use? [git, dropbox] (Default: git)

NOTE: Some backends will change this automatically and override your setting. (i.e. dropbox)
Where would you like to store profiles? (Default: /mnt/home/daniel/.config/dfm)
{
"Backend": "git",
"ConfigDir": "/mnt/home/daniel/.config/dfm",
"CurrentProfile": "",
"Etc": null
}
Does this look correct? Y/n: Y
ERROR: unexpected end of JSON input
'

Any ideas about the error?

Thanks in advance.

Add support for hooks

dfm should be able to run a hooks file before and after linking / profile switching

Error on `dfm init [profile]`

Version:

Output of dfm -v.

dfm version 1.0-dev

Command:

dfm init [profile]

Error:

mkdir /Users/[user]/.config/dfm/profiles/[profile]: no such file or directory

Steps to Reproduce:

Make sure your /Users/[user]/.config/dfm directory doesn't exist yet, and then try to dfm init a new profile.

Suggested Fix:

Option 1:

Use os.MkdirAll instead of os.Mkdir in init.go on line 20.

Option 2:

Add check to make sure dfm directory exists in getProfileDir() or LoadConfig().

Hooks

dfm should be able to run hooks before and after the commands that it offers.

"init is not a known dfm command"

[ynavott-gram@ynavott-gram-ubuntu: /usr/local/bin]$ ./dfm
Usage:
    dfm [options] <command> [<args>...]
    dfm help
    dfm sync
    dfm link <profile>
[ynavott-gram@ynavott-gram-ubuntu: /usr/local/bin]$ ./dfm init profile-main
init is not a known dfm command.
``` This is a fresh clone which installed totally fine (at least it seems like it). How come init doesn't exist? Have tried using `dfm -v init profie-main`, `dfm -v i profile-main`, `dfm i profile-main` and it also doesn't work.

Add a "run-hook" command

You should be able to run hooks directly via a run hook command. This allows you to run setup scripts on systems without necessarily incurring the side effects of the primary dfm command. For instance I use both Dropbox and Git for my dotfiles but need to run my "after_clone" hooks to install spacemacs despite not needing to clone my dotfiles on this new machine.

Syntax should be:

dfm run-hook <hook_name>

For the example above:

dfm run-hook after_clone

It should accept multiple hooks and run all of them.

clone without --name throws a trace

[chasinglogic@localhost ~]$ dfm clone https://github.com/chasinglogic/dotfiles
Traceback (most recent call last):
  File "/home/chasinglogic/.local/bin/dfm", line 10, in <module>
    sys.exit(main())
  File "/home/chasinglogic/.local/lib/python3.7/site-packages/dfm/cli/__init__.py", line 81, in main
    command_mod.run(docopt(command_mod.__doc__, argv=argv))
  File "/home/chasinglogic/.local/lib/python3.7/site-packages/dfm/cli/clone_cmd.py", line 37, in run
    path = os.path.join(dfm_dir(), 'profiles', name)
  File "/usr/lib64/python3.7/posixpath.py", line 94, in join
    genericpath._check_arg_types('join', a, *p)
  File "/usr/lib64/python3.7/genericpath.py", line 149, in _check_arg_types
    (funcname, s.__class__.__name__)) from None
TypeError: join() argument must be str or bytes, not 'NoneType'

add a command to add a file to a profile

.super-awesome-config is not part of the current profile but it should be I want to be able to run

dfm add .super-awesome-config

and it will add it it to the current profile.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.