Skip to content
Merged

Work #13

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ jobs:
- name: Install dependencies
run: go get .
- name: Test with Go CLI
run: TESTING=true go test -v ./test
run: make unit-test
10 changes: 10 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
clean:
rm -rf test/bender_test 2> /dev/null
rm -rf tmp 2> /dev/null

sandbox:
mkdir -p ./tmp/ 2> /dev/null
cp -r ~/.config/ ./tmp/config 2> /dev/null

unit-test:
TESTING=true go test -v ./test
29 changes: 25 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,36 @@
# Bender
![](assets/bender.png)

A general purpose CLI tool.
A cli tool to manage your dotfiles
## About
Bender is a tool to help you easily manage your dotfiles and sync them across separate machines using
git. It aims to abstract away the manual effort of symlinking your dotfiles to config directories and
updating them with git.

## Installation
- TBD

## Usage
use pretty command to get whitespace for special characters like `\n` and `\t`

e.g.
```bash
bender pretty example.txt
# init sets up the config file and directory to hold all dotfiles
bender init --dotfile-path=/path/to/dotfile/repo
# add a config directory for bender to track
bender add /.config/nvim
# create symlinks
bender link
```

## Development
It's preferable to create a temporary directory and copy your system's config
directory over to avoid making undesirable changes to your system.
A couple of useful makefile scripts exist to set up and tear down this.
It will create a testing directory in `./tmp/config` and copy your system configs
over.

```bash
make sandbox # creates the directory and copies over from ~/.config
make clean # removes directory
```


11 changes: 8 additions & 3 deletions cmd/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,21 @@ func runAddCommand(cmd *cobra.Command, args []string) {
dirs := strings.Split(configSrc, "/")
name := dirs[len(dirs) - 1]
viper.Set(name, configSrc)
viper.WriteConfig()
err := viper.WriteConfig()
if err != nil {
fmt.Printf("Problem updating bender config %s", err)
}

dotfilePath := viper.Get("dotfile-path").(string)

dotfileDest := filepath.Join(DotfilePath, name)
dotfileDest := filepath.Join(dotfilePath, name)

if DryRun {
fmt.Printf("Will copy %s -> %s \n", configSrc, dotfileDest)
return
}

err := tools.CopyDir(fs, configSrc, dotfileDest)
err = tools.CopyDir(fs, configSrc, dotfileDest)
if err != nil {
log.Fatal(err)
}
Expand Down
10 changes: 8 additions & 2 deletions cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ var initCommand = &cobra.Command {

func runInitCommand(cmd *cobra.Command, args []string) {
fs := FileSystem
// if user has passed a dotfile path flag need to add it to
// viper's search path for a config file
viper.AddConfigPath(filepath.Join(DotfilePath, "bender"))

if(viper.Get("testing") == true && fs.Name() != "MemMapFS") {
log.Fatalf("wrong filesystem, got %s", fs.Name())
Expand All @@ -68,7 +71,10 @@ func runInitCommand(cmd *cobra.Command, args []string) {
panic(fmt.Errorf("Unable to create config file %w", err))
}

viper.WriteConfig()
err = viper.WriteConfig()
if err != nil && viper.Get("testing") != true {
log.Fatalf("Unable to write config on init: %s\n", err)
}

if (viper.Get("testing") != "true"){
_, err = git.PlainInit(DotfilePath, false)
Expand All @@ -77,5 +83,5 @@ func runInitCommand(cmd *cobra.Command, args []string) {
}
}

fmt.Fprintf(cmd.OutOrStdout(), "Successfully created dotfiles repository\n")
fmt.Fprintf(cmd.OutOrStdout(), "Successfully created dotfiles repository at %s\n", DotfilePath)
}
30 changes: 22 additions & 8 deletions cmd/link.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/spf13/afero"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

var linkCommand = &cobra.Command {
Expand All @@ -19,18 +20,25 @@ func init() {
}

func runLinkCommand(cmd *cobra.Command, args []string) {
fs := UseFilesystem()
fs := FileSystem
fmt.Println("Symlinking dotfiles...")
entries, err := afero.ReadDir(fs, DotfilePath)
dotfileRoot := viper.Get("dotfile-path").(string)
entries, err := afero.ReadDir(fs, dotfileRoot)
if err != nil {
log.Fatal(err)
log.Fatalf("Could not read dotfiles directory: %s\n",err)
}
for _, entry := range(entries) {
if entry.Name() == ".git" {
configName := entry.Name()
if configName == ".git" || configName == "bender" {
continue
}
dotPath := filepath.Join(DotfilePath, entry.Name())
configPath := filepath.Join(ConfigPath, entry.Name())
dotPath := filepath.Join(dotfileRoot, entry.Name())

configPath := viper.GetString(configName)
if configPath == ""{
fmt.Fprintf(cmd.OutOrStdout(), "Warning: could not find config for %s\n", entry.Name())
}


// destination needs to be removed before symlink
if(DryRun) {
Expand All @@ -40,10 +48,16 @@ func runLinkCommand(cmd *cobra.Command, args []string) {
fs.RemoveAll(configPath)
}

testing := viper.Get("testing")

if(DryRun) {
log.Printf("Will link %s -> %s\n", dotPath, configPath)
log.Printf("Will link %s -> %s\n", configPath, dotPath)
} else {
err = afero.OsFs.SymlinkIfPossible(afero.OsFs{}, dotPath, configPath)
if(testing == true) {
fmt.Fprintf(cmd.OutOrStdout(), "%s,%s", configPath, dotPath)
} else {
err = afero.OsFs.SymlinkIfPossible(afero.OsFs{}, dotPath, configPath)
}
}
if err != nil {
log.Fatalf("Cannot symlink %s: %s", entry.Name(), err.Error())
Expand Down
30 changes: 3 additions & 27 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,11 @@ func init() {

viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath(filepath.Join(defaultDotPath, "bender"))
viper.AddConfigPath("./bender")
viper.AddConfigPath("./tmp/dotfiles/bender")
viper.AddConfigPath(filepath.Join(DotfilePath, "bender"))

err := viper.ReadInConfig()

if err != nil {
fmt.Println("No config detected. You can generate one by using 'bender init'")
}
Expand All @@ -88,29 +89,4 @@ func UseFilesystem() afero.Fs {
}

// TODO: this can probably be removed
func SetUpForTesting() afero.Fs {
viper.Set("testing", true)
fs := UseFilesystem()

homedir := "bender_test/"
fs.MkdirAll(filepath.Join(homedir, ".config/"), 0755)
fs.MkdirAll(filepath.Join(homedir, ".dotfiles/"), 0755)

fs.Mkdir("bin/", 0755)
fs.Create("bin/alacritty")
fs.Create("bin/nvim")
fs.Create("bin/tmux")

fs.Mkdir(filepath.Join(homedir, ".config/alacritty"), 0755)
fs.Mkdir(filepath.Join(homedir, ".config/nvim"), 0755)
fs.Mkdir(filepath.Join(homedir, ".config/tmux"), 0755)

fs.Create(filepath.Join(homedir, ".config/alacritty/alacritty.conf"))
fs.Create(filepath.Join(homedir, ".config/nvim/nvim.conf"))
fs.Create(filepath.Join(homedir, ".config/tmux/tmux.conf"))

FileSystem = fs

return fs
}

4 changes: 2 additions & 2 deletions test/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ func TestInitCommand(t *testing.T) {

bender.SetOut(actual)
bender.SetErr(actual)
bender.SetArgs([]string{"init", "--dotfile-path=bender_test/.dotfiles"})
bender.SetArgs([]string{"init", "--dotfile-path=bender_test/dotfiles"})

bender.Execute()

homedir := "bender_test/"

_, err := afero.ReadFile(fs, filepath.Join(homedir, ".dotfiles/bender/config"))
_, err := afero.ReadFile(fs, filepath.Join(homedir, "dotfiles/bender/config"))
if err != nil {
t.Error(err.Error())
}
Expand Down
54 changes: 54 additions & 0 deletions test/link_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package test

import (
"bytes"
"fmt"
"os"
"path/filepath"
"testing"

"github.com/Marcusk19/bender/cmd"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
)


func TestLinkCommand(t *testing.T) {
setUpTesting()
bender := cmd.RootCmd
actual := new(bytes.Buffer)

bender.SetOut(actual)
bender.SetErr(actual)
bender.SetArgs([]string{"link"})

bender.Execute()

homedir := os.Getenv("HOME")
someconfig := filepath.Join(homedir, ".config/someconfig/")
somedot := filepath.Join(homedir, ".dotfiles/someconfig/")

expected := fmt.Sprintf("%s,%s", someconfig, somedot)

assert.Equal(t, expected, actual.String(), "actual differs from expected")

tearDownTesting()
}

func setUpTesting() {
fs := cmd.FileSystem
homedir := os.Getenv("HOME")
fs.MkdirAll(filepath.Join(homedir, ".dotfiles/bender"), 0755)
fs.Create(filepath.Join(homedir, ".dotfiles/bender/config"))
fs.MkdirAll(filepath.Join(homedir, ".dotfiles/someconfig/"), 0755)

viper.Set("dotfile-path", filepath.Join(homedir, ".dotfiles"))
viper.Set("someconfig", filepath.Join(homedir, ".config/someconfig/"))
viper.Set("testing", true)

}

func tearDownTesting() {
fs := cmd.FileSystem
fs.RemoveAll("bender_test/")
}