-
Notifications
You must be signed in to change notification settings - Fork 881
feat: add dotfiles command #1723
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
I will attempt to get some tests going for this, but I expect it to be pretty difficult with all the shell callouts. I'll see how it goes. |
I've added some cool tests that use local git repos to check for the different logic paths. |
Is the idea to run this from within the workspace? |
Yeah, mostly through the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work! Some minor comments.
cli/dotfiles.go
Outdated
to := filepath.Join(symlinkDir, df) | ||
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Symlinking %s to %s...\n", from, to) | ||
|
||
isRegular, err := isRegular(to) | ||
if err != nil { | ||
return xerrors.Errorf("checking symlink for %s: %w", to, err) | ||
} | ||
// move conflicting non-symlink files to file.ext.bak | ||
if isRegular { | ||
backup := fmt.Sprintf("%s.bak", to) | ||
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Moving %s to %s...\n", to, backup) | ||
err = os.Rename(to, backup) | ||
if err != nil { | ||
return xerrors.Errorf("renaming dir %s: %w", to, err) | ||
} | ||
} | ||
|
||
err = os.Symlink(from, to) | ||
if err != nil { | ||
return xerrors.Errorf("symlinking %s to %s: %w", from, to, err) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion, non-blocking: this looks ripe for ripping out into a utility function and testing independently
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I already check this happy path in the tests, but I agree this can get hairy, will circle back if I have time.
cli/dotfiles_test.go
Outdated
t.Run("MissingArg", func(t *testing.T) { | ||
cmd, _ := clitest.New(t, "dotfiles") | ||
err := cmd.Execute() | ||
assert.Error(t, err) | ||
}) | ||
t.Run("NoInstallScript", func(t *testing.T) { | ||
_, root := clitest.New(t) | ||
testRepo := testGitRepo(t, root) | ||
|
||
// nolint:gosec | ||
err := os.WriteFile(filepath.Join(testRepo, ".bashrc"), []byte("wow"), 0750) | ||
assert.NoError(t, err) | ||
|
||
c := exec.Command("git", "add", ".bashrc") | ||
c.Dir = testRepo | ||
err = c.Run() | ||
assert.NoError(t, err) | ||
|
||
c = exec.Command("git", "commit", "-m", `"add .bashrc"`) | ||
c.Dir = testRepo | ||
out, err := c.CombinedOutput() | ||
assert.NoError(t, err, string(out)) | ||
|
||
cmd, _ := clitest.New(t, "dotfiles", "--global-config", string(root), "--symlink-dir", string(root), "-y", testRepo) | ||
err = cmd.Execute() | ||
assert.NoError(t, err) | ||
|
||
b, err := os.ReadFile(filepath.Join(string(root), ".bashrc")) | ||
assert.NoError(t, err) | ||
assert.Equal(t, string(b), "wow") | ||
}) | ||
t.Run("InstallScript", func(t *testing.T) { | ||
_, root := clitest.New(t) | ||
testRepo := testGitRepo(t, root) | ||
|
||
// nolint:gosec | ||
err := os.WriteFile(filepath.Join(testRepo, "install.sh"), []byte("#!/bin/bash\necho wow > "+filepath.Join(string(root), ".bashrc")), 0750) | ||
assert.NoError(t, err) | ||
|
||
c := exec.Command("git", "add", "install.sh") | ||
c.Dir = testRepo | ||
err = c.Run() | ||
assert.NoError(t, err) | ||
|
||
c = exec.Command("git", "commit", "-m", `"add install.sh"`) | ||
c.Dir = testRepo | ||
err = c.Run() | ||
assert.NoError(t, err) | ||
|
||
cmd, _ := clitest.New(t, "dotfiles", "--global-config", string(root), "--symlink-dir", string(root), "-y", testRepo) | ||
err = cmd.Execute() | ||
assert.NoError(t, err) | ||
|
||
b, err := os.ReadFile(filepath.Join(string(root), ".bashrc")) | ||
assert.NoError(t, err) | ||
assert.Equal(t, string(b), "wow\n") | ||
}) | ||
t.Run("SymlinkBackup", func(t *testing.T) { | ||
_, root := clitest.New(t) | ||
testRepo := testGitRepo(t, root) | ||
|
||
// nolint:gosec | ||
err := os.WriteFile(filepath.Join(testRepo, ".bashrc"), []byte("wow"), 0750) | ||
assert.NoError(t, err) | ||
|
||
// add a conflicting file at destination | ||
// nolint:gosec | ||
err = os.WriteFile(filepath.Join(string(root), ".bashrc"), []byte("backup"), 0750) | ||
assert.NoError(t, err) | ||
|
||
c := exec.Command("git", "add", ".bashrc") | ||
c.Dir = testRepo | ||
err = c.Run() | ||
assert.NoError(t, err) | ||
|
||
c = exec.Command("git", "commit", "-m", `"add .bashrc"`) | ||
c.Dir = testRepo | ||
out, err := c.CombinedOutput() | ||
assert.NoError(t, err, string(out)) | ||
|
||
cmd, _ := clitest.New(t, "dotfiles", "--global-config", string(root), "--symlink-dir", string(root), "-y", testRepo) | ||
err = cmd.Execute() | ||
assert.NoError(t, err) | ||
|
||
b, err := os.ReadFile(filepath.Join(string(root), ".bashrc")) | ||
assert.NoError(t, err) | ||
assert.Equal(t, string(b), "wow") | ||
|
||
// check for backup file | ||
b, err = os.ReadFile(filepath.Join(string(root), ".bashrc.bak")) | ||
assert.NoError(t, err) | ||
assert.Equal(t, string(b), "backup") | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
praise: nice tests!
Need to get the install script path happy running on windows, looking into it |
After speaking with a few people, I'm just going to skip the windows install script test. Windows can still work in general, but specifically the install script case is weird enough to defer for now. |
Short: "Checkout and install a dotfiles repository.", | ||
Example: "coder dotfiles [-y] git@github.com:example/dotfiles.git", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should improve the usage here. It will not be immediately obvious to users how we intend for them to use this (inside or outside of a workspace).
A few examples will go a long way!
This PR is the implementation of the
coder dotfiles
command.Refer to #1696 for more discussion.