-
Notifications
You must be signed in to change notification settings - Fork 936
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
Merged
Merged
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
7235e3a
chore: propose coder dotfiles command
f0ssel 6cd9f17
simplify example
f0ssel 6a2bd87
add command skeleton
f0ssel 3417628
Merge branch 'f0ssel/dotfiles-poc' of github.com:coder/coder into f0s…
f0ssel e127fe7
do clone/checkout
f0ssel 17490a4
add install and symlinking
f0ssel 2cf43d6
fix lint
f0ssel 3302497
spruce
f0ssel 250d10a
revert tf
f0ssel 675f7e6
lint
f0ssel eae24d0
ignore git files
f0ssel 8cf1c4f
pr comments
f0ssel 42e9028
remove shorthand
f0ssel 0b6480b
fixup
f0ssel 2769e0a
Add tests
f0ssel 051a361
lint
f0ssel 23c950a
try removing parallel
f0ssel 43c8b4f
formatting
f0ssel 2197126
pr comments
f0ssel 9e073ab
save dotfiles url config
f0ssel 0467613
fixup
f0ssel 3b505b0
%q
f0ssel fd9ba2a
testing
f0ssel 624265f
testing
f0ssel 44efa67
testing
f0ssel b059fdf
organize:
f0ssel b265149
add symlink backup test
f0ssel 581d6b8
handle script for windows
f0ssel 6dfc469
switch to require
f0ssel df5f6cd
pr comments
f0ssel b29ddf4
skip install script test on windows
f0ssel a435224
fix command
f0ssel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
pr comments
- Loading branch information
commit 8cf1c4f7bff2397b693ef60cb60ce890840bc5d1
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ package cli | |
|
||
import ( | ||
"fmt" | ||
"io/fs" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
|
@@ -10,14 +11,19 @@ import ( | |
"github.com/spf13/cobra" | ||
"golang.org/x/xerrors" | ||
|
||
"github.com/coder/coder/cli/cliflag" | ||
"github.com/coder/coder/cli/cliui" | ||
) | ||
|
||
func dotfiles() *cobra.Command { | ||
var ( | ||
homeDir string | ||
) | ||
cmd := &cobra.Command{ | ||
Use: "dotfiles [git_repo_url]", | ||
Args: cobra.ExactArgs(1), | ||
Short: "Checkout and install a dotfiles repository.", | ||
Use: "dotfiles [git_repo_url]", | ||
Args: cobra.ExactArgs(1), | ||
Short: "Checkout and install a dotfiles repository.", | ||
Example: "coder dotfiles [-y] git@github.com:example/dotfiles.git", | ||
Comment on lines
+27
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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! |
||
RunE: func(cmd *cobra.Command, args []string) error { | ||
var ( | ||
dotfilesRepoDir = "dotfiles" | ||
|
@@ -26,7 +32,7 @@ func dotfiles() *cobra.Command { | |
dotfilesDir = filepath.Join(cfgDir, dotfilesRepoDir) | ||
subcommands = []string{"clone", args[0], dotfilesRepoDir} | ||
gitCmdDir = cfgDir | ||
promtText = fmt.Sprintf("Cloning %s into directory %s.\n Continue?", gitRepo, dotfilesDir) | ||
promptText = fmt.Sprintf("Cloning %s into directory %s.\n Continue?", gitRepo, dotfilesDir) | ||
// This follows the same pattern outlined by others in the market: | ||
// https://github.com/coder/coder/pull/1696#issue-1245742312 | ||
installScriptSet = []string{ | ||
|
@@ -52,13 +58,13 @@ func dotfiles() *cobra.Command { | |
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Found dotfiles repository at %s\n", dotfilesDir) | ||
gitCmdDir = dotfilesDir | ||
subcommands = []string{"pull", "--ff-only"} | ||
promtText = fmt.Sprintf("Pulling latest from %s into directory %s.\n Continue?", gitRepo, dotfilesDir) | ||
promptText = fmt.Sprintf("Pulling latest from %s into directory %s.\n Continue?", gitRepo, dotfilesDir) | ||
f0ssel marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} else { | ||
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Did not find dotfiles repository at %s\n", dotfilesDir) | ||
f0ssel marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
_, err = cliui.Prompt(cmd, cliui.PromptOptions{ | ||
Text: promtText, | ||
Text: promptText, | ||
IsConfirm: true, | ||
}) | ||
if err != nil { | ||
|
@@ -94,57 +100,21 @@ func dotfiles() *cobra.Command { | |
return xerrors.Errorf("reading files in dir %s: %w", dotfilesDir, err) | ||
} | ||
|
||
var scripts []string | ||
var dotfiles []string | ||
for _, f := range files { | ||
for _, i := range installScriptSet { | ||
if f.Name() == i { | ||
scripts = append(scripts, f.Name()) | ||
} | ||
} | ||
|
||
// make sure we do not copy `.git*` files | ||
if strings.HasPrefix(f.Name(), ".") && !strings.HasPrefix(f.Name(), ".git") { | ||
dotfiles = append(dotfiles, f.Name()) | ||
} | ||
} | ||
|
||
// run found install scripts | ||
if len(scripts) > 0 { | ||
t := "Found install script(s). The following script(s) will be executed in order:\n\n" | ||
for _, s := range scripts { | ||
t = fmt.Sprintf("%s - %s\n", t, s) | ||
} | ||
t = fmt.Sprintf("%s\n Continue?", t) | ||
_, err = cliui.Prompt(cmd, cliui.PromptOptions{ | ||
Text: t, | ||
IsConfirm: true, | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for _, s := range scripts { | ||
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "\nRunning %s...\n", s) | ||
// it is safe to use a variable command here because it's from | ||
// a filtered list of pre-approved install scripts | ||
// nolint:gosec | ||
c := exec.CommandContext(cmd.Context(), fmt.Sprintf("./%s", s)) | ||
c.Dir = dotfilesDir | ||
out, err := c.CombinedOutput() | ||
if err != nil { | ||
_, _ = fmt.Fprintln(cmd.OutOrStdout(), cliui.Styles.Error.Render(string(out))) | ||
return xerrors.Errorf("running %s: %w", s, err) | ||
} | ||
_, _ = fmt.Fprintln(cmd.OutOrStdout(), string(out)) | ||
script := findScript(installScriptSet, files) | ||
if script == "" { | ||
f0ssel marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if len(dotfiles) == 0 { | ||
_, _ = fmt.Fprintln(cmd.OutOrStdout(), "No install scripts or dotfiles found, nothing to do.") | ||
return nil | ||
} | ||
|
||
_, _ = fmt.Fprintln(cmd.OutOrStdout(), "Dotfiles installation complete.") | ||
return nil | ||
} | ||
|
||
// otherwise symlink dotfiles | ||
if len(dotfiles) > 0 { | ||
_, err = cliui.Prompt(cmd, cliui.PromptOptions{ | ||
Text: "No install scripts found, symlinking dotfiles to home directory.\n\n Continue?", | ||
IsConfirm: true, | ||
|
@@ -153,14 +123,16 @@ func dotfiles() *cobra.Command { | |
return err | ||
} | ||
|
||
home, err := os.UserHomeDir() | ||
if err != nil { | ||
return xerrors.Errorf("getting user home: %w", err) | ||
if homeDir == "" { | ||
homeDir, err = os.UserHomeDir() | ||
if err != nil { | ||
return xerrors.Errorf("getting user home: %w", err) | ||
} | ||
} | ||
|
||
for _, df := range dotfiles { | ||
from := filepath.Join(dotfilesDir, df) | ||
to := filepath.Join(home, df) | ||
to := filepath.Join(homeDir, df) | ||
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Symlinking %s to %s...\n", from, to) | ||
// if file already exists at destination remove it | ||
// this behavior matches `ln -f` | ||
|
@@ -182,11 +154,34 @@ func dotfiles() *cobra.Command { | |
return nil | ||
} | ||
|
||
_, _ = fmt.Fprintln(cmd.OutOrStdout(), "No install scripts or dotfiles found, nothing to do.") | ||
// run found install scripts | ||
_, err = cliui.Prompt(cmd, cliui.PromptOptions{ | ||
Text: fmt.Sprintf("Running install script %s.\n\n Continue?", script), | ||
IsConfirm: true, | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Running %s...\n", script) | ||
// it is safe to use a variable command here because it's from | ||
// a filtered list of pre-approved install scripts | ||
// nolint:gosec | ||
scriptCmd := exec.CommandContext(cmd.Context(), fmt.Sprintf("./%s", script)) | ||
scriptCmd.Dir = dotfilesDir | ||
out, err = scriptCmd.CombinedOutput() | ||
if err != nil { | ||
_, _ = fmt.Fprintln(cmd.OutOrStdout(), cliui.Styles.Error.Render(string(out))) | ||
return xerrors.Errorf("running %s: %w", script, err) | ||
} | ||
_, _ = fmt.Fprintln(cmd.OutOrStdout(), string(out)) | ||
f0ssel marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
_, _ = fmt.Fprintln(cmd.OutOrStdout(), "Dotfiles installation complete.") | ||
return nil | ||
}, | ||
} | ||
cliui.AllowSkipPrompt(cmd) | ||
cliflag.StringVarP(cmd.Flags(), &homeDir, "home-dir", "-d", "CODER_HOME_DIR", "", "Specifies the home directory for the dotfiles symlink destination. If empty will use $HOME.") | ||
|
||
return cmd | ||
} | ||
|
@@ -203,3 +198,15 @@ func dirExists(name string) (bool, error) { | |
|
||
return true, nil | ||
} | ||
|
||
func findScript(installScriptSet []string, files []fs.DirEntry) string { | ||
for _, i := range installScriptSet { | ||
for _, f := range files { | ||
if f.Name() == i { | ||
return f.Name() | ||
} | ||
} | ||
} | ||
|
||
return "" | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.