From 492ffba71925d939d58da672c0e70b7d92b3453e Mon Sep 17 00:00:00 2001 From: Jonathan del Strother Date: Sat, 8 Feb 2020 19:55:34 +0000 Subject: [PATCH 01/12] Add fish support To use, append `scmpuff init -s --shell=fish | source` to your `~/.config/fish/config.fish` file. Fish scripts are based on https://github.com/arbelt/fish-plugin-scmpuff --- README.md | 4 +++ commands/inits/data/git_wrapper.fish | 36 +++++++++++++++++++++++ commands/inits/data/status_shortcuts.fish | 33 +++++++++++++++++++++ commands/inits/init.go | 16 ++++++++-- commands/inits/scripts.go | 18 ++++++++++-- 5 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 commands/inits/data/git_wrapper.fish create mode 100644 commands/inits/data/status_shortcuts.fish diff --git a/README.md b/README.md index 01999e9..d86a0f6 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,10 @@ To initialize shell functions, add the following to your `~/.bash_profile` or eval "$(scmpuff init -s)" +or for Fish, add the following to your `~/.config/fish/config.fish` file: + + scmpuff init -s --shell=fish | source + This will define the scmpuff shell functions as well as some handy shortcuts. diff --git a/commands/inits/data/git_wrapper.fish b/commands/inits/data/git_wrapper.fish new file mode 100644 index 0000000..d956930 --- /dev/null +++ b/commands/inits/data/git_wrapper.fish @@ -0,0 +1,36 @@ +# Based on https://github.com/arbelt/fish-plugin-scmpuff, +# with scmpuff-exec support (https://github.com/mroth/scmpuff/pull/49) +set -x SCMPUFF_GIT_CMD (which git) + +if type -q hub + set -x SCMPUFF_GIT_CMD "hub" +end + + +if not type -q scmpuff + exit 1 +end + +functions -e git + +function git + type -q $SCMPUFF_GIT_CMD; or set -x SCMPUFF_GIT_CMD (which git) + + if test (count $argv) -eq 0 + eval $SCMPUFF_GIT_CMD + set -l s $status + return $s + end + + switch $argv[1] + case commit blame log rebase merge + scmpuff exec -- "$SCMPUFF_GIT_CMD" $argv + case checkout diff rm reset + scmpuff exec --relative -- "$SCMPUFF_GIT_CMD" $argv + case add + scmpuff exec -- "$SCMPUFF_GIT_CMD" $argv + scmpuff_status + case '*' + eval command "$SCMPUFF_GIT_CMD" (string escape -- $argv) + end +end diff --git a/commands/inits/data/status_shortcuts.fish b/commands/inits/data/status_shortcuts.fish new file mode 100644 index 0000000..363ee92 --- /dev/null +++ b/commands/inits/data/status_shortcuts.fish @@ -0,0 +1,33 @@ +# Based on https://github.com/arbelt/fish-plugin-scmpuff, +# with fish3 fix https://github.com/arbelt/fish-plugin-scmpuff/pull/3 +function scmpuff_status + scmpuff_clear_vars + set -lx scmpuff_env_char "e" + set -l cmd_output (/usr/bin/env scmpuff status --filelist $argv ^/dev/null) + set -l es "$status" + + if test $es -ne 0 + git status + return $status + end + + set -l files (string split \t $cmd_output[1]) + if test (count $files) -gt 0 + for e in (seq (count $files)) + set -gx "$scmpuff_env_char""$e" "$files[$e]" + end + end + + for line in $cmd_output[2..-1] + echo $line + end +end + +function scmpuff_clear_vars + set -l scmpuff_env_char "e" + set -l scmpuff_env_vars (set -x | awk '{print $1}' | grep -E '^'$scmpuff_env_char'\d+') + + for v in $scmpuff_env_vars + set -e $v + end +end diff --git a/commands/inits/init.go b/commands/inits/init.go index 78c8f5c..e2b1091 100644 --- a/commands/inits/init.go +++ b/commands/inits/init.go @@ -12,6 +12,7 @@ import ( var includeAliases bool var outputScript bool var wrapGit bool +var shellType string // CommandInit generates the command handler for `scmpuff init` func CommandInit() *cobra.Command { @@ -20,7 +21,7 @@ func CommandInit() *cobra.Command { Use: "init", Short: "Output initialization script", Long: ` -Outputs the bash/zsh initialization script for scmpuff. +Outputs the bash/zsh/fish initialization script for scmpuff. This should probably be evaluated in your shell startup. `, @@ -54,6 +55,13 @@ This should probably be evaluated in your shell startup. "Wrap standard git commands", ) + // --shell + InitCmd.Flags().StringVarP( + &shellType, + "shell", "", "sh", + "Set shell type - 'sh' (for bash/zsh), or 'fish'", + ) + return InitCmd } @@ -61,5 +69,9 @@ This should probably be evaluated in your shell startup. func helpString() string { return `# Initialize scmpuff by adding the following to ~/.bash_profile or ~/.zshrc: -eval "$(scmpuff init -s)"` +eval "$(scmpuff init -s --shell=sh)" + +# or the following to ~/.config/fish/config.fish: + +scmpuff init -s --shell=fish | source` } diff --git a/commands/inits/scripts.go b/commands/inits/scripts.go index ee20f80..7bd7834 100644 --- a/commands/inits/scripts.go +++ b/commands/inits/scripts.go @@ -8,15 +8,25 @@ import ( //go:embed data/status_shortcuts.sh var scriptStatusShortcuts string +//go:embed data/status_shortcuts.fish +var scriptStatusShortcutsFish string + //go:embed data/aliases.sh var scriptAliases string //go:embed data/git_wrapper.sh var scriptGitWrapper string +//go:embed data/git_wrapper.fish +var scriptGitWrapperFish string + func printScript() { if outputScript { - fmt.Println(scriptStatusShortcuts) + if shellType == "fish" { + fmt.Println(scriptStatusShortcutsFish) + } else { + fmt.Println(scriptStatusShortcuts) + } } if includeAliases { @@ -24,6 +34,10 @@ func printScript() { } if wrapGit { - fmt.Println(scriptGitWrapper) + if shellType == "fish" { + fmt.Println(scriptGitWrapperFish) + } else { + fmt.Println(scriptGitWrapper) + } } } From 867661ad910138c3f801f62428ce4b3f2e271ad5 Mon Sep 17 00:00:00 2001 From: Jonathan del Strother Date: Sun, 2 Jan 2022 17:24:52 +0000 Subject: [PATCH 02/12] Add fish to the feature tests --- .github/workflows/test-integration.yml | 7 ++-- features/command_init.feature | 6 ++-- features/shell_functions.feature | 47 ++++++++++++++------------ features/shell_wrappers.feature | 37 ++++++++++---------- features/support/env.rb | 16 +++++++++ 5 files changed, 70 insertions(+), 43 deletions(-) diff --git a/.github/workflows/test-integration.yml b/.github/workflows/test-integration.yml index dd0cc98..51c8c1a 100644 --- a/.github/workflows/test-integration.yml +++ b/.github/workflows/test-integration.yml @@ -42,8 +42,11 @@ jobs: bundler-cache: true - name: Build binary run: rake build - - name: Install ZSH on ubuntu + - name: Install shells on ubuntu if: matrix.os == 'ubuntu-latest' - run: sudo apt-get -y install zsh + run: sudo apt-get -y install zsh fish + - name: Install shells on macos + if: matrix.os == 'macos-latest' + run: brew install fish - name: Run integration Tests run: bundle exec cucumber -s --tags="not @wip" --color diff --git a/features/command_init.feature b/features/command_init.feature index f12e024..6b9fdf8 100644 --- a/features/command_init.feature +++ b/features/command_init.feature @@ -32,12 +32,14 @@ Feature: init command Scenario Outline: Evaling init -s defines status shortcuts in environment When I run `` interactively - And I type `eval "$(scmpuff init -s)"` + And I initialize scmpuff in `` And I type "type scmpuff_status" And I type "type scmpuff_clear_vars" - And I type "exit" + And I close the shell `` Then the output should not contain "not found" Examples: | shell | | bash | | zsh | + | fish | + diff --git a/features/shell_functions.feature b/features/shell_functions.feature index 929cb4d..85b0fa0 100644 --- a/features/shell_functions.feature +++ b/features/shell_functions.feature @@ -17,11 +17,11 @@ Feature: scmpuff_status function non-zero exit codes from the underlying process are preserved. When I run `` interactively - And I type `eval "$(scmpuff init -ws)"` + And I initialize scmpuff in `` And I type "scmpuff_status" - And I type "exit $?" + And I close the shell `` Then the exit status should be 128 - And the output should contain: + Then the stderr should contain: """ Not a git repository (or any of the parent directories) """ @@ -29,29 +29,30 @@ Feature: scmpuff_status function | shell | | bash | | zsh | + | fish | Scenario Outline: Basic functionality works with shell wrapper. Given I am in a git repository When I run `` interactively - And I type `eval "$(scmpuff init -ws)"` + And I initialize scmpuff in `` And I type "scmpuff_status" - And I type "exit $?" + And I close the shell `` Then the exit status should be 0 And the output should contain "No changes (working directory clean)" Examples: | shell | | bash | | zsh | + | fish | Scenario Outline: Sets proper environment variables in shell Given I am in a complex working tree status matching scm_breeze tests And the scmpuff environment variables have been cleared When I run `` interactively - And I type `eval "$(scmpuff init -s)"` + And I initialize scmpuff in `` And I type "scmpuff_status" And I type `echo -e "e1:$e1\ne2:$e2\ne3:$e3\ne4:$e4\ne5:$e5\n"` - And I type "exit" - And I stop the command "" + And I close the shell `` Then the output should match /^e1:.*new_file$/ And the output should match /^e2:.*deleted_file$/ And the output should match /^e3:.*new_file$/ @@ -61,6 +62,7 @@ Feature: scmpuff_status function | shell | | bash | | zsh | + | fish | Scenario Outline: Sets proper environment variables in shell with weird filenames Given I am in a git repository @@ -68,11 +70,10 @@ Feature: scmpuff_status function And an empty file named "bb|cc" And an empty file named "cc*dd" When I run `` interactively - And I type `eval "$(scmpuff init -s)"` + And I initialize scmpuff in `` And I type "scmpuff_status" And I type `echo -e "e1:$e1\ne2:$e2\ne3:$e3\ne4:$e4\n"` - And I type "exit" - And I stop the command "" + And I close the shell `` Then the output should match /^e1:.*aa bb$/ And the output should match /^e2:.*bb\|cc$/ And the output should match /^e3:.*cc\*dd$/ @@ -81,18 +82,19 @@ Feature: scmpuff_status function | shell | | bash | | zsh | + | fish | Scenario Outline: Clears extra environment variables from before Given I am in a complex working tree status matching scm_breeze tests And the scmpuff environment variables have been cleared When I run `` interactively - And I type `eval "$(scmpuff init -s)"` + And I initialize scmpuff in `` And I type "scmpuff_status" And I type "git add new_file" And I type "git commit -m 'so be it'" And I type "scmpuff_status" And I type `echo -e "e1:$e1\ne2:$e2\ne3:$e3\ne4:$e4\ne5:$e5\n"` - And I type "exit" + And I close the shell `` Then the output should match /^e1:.*deleted_file$/ And the output should match /^e2:.*untracked_file$/ And the output should match /^e3:$/ @@ -102,43 +104,44 @@ Feature: scmpuff_status function | shell | | bash | | zsh | + | fish | Scenario Outline: default SCMPUFF_GIT_CMD is set to absolute path of a git command When I run `` interactively - And I type `eval "$(scmpuff init -s)"` + And I initialize scmpuff in `` And I type "echo $SCMPUFF_GIT_CMD" - And I type "exit" - And I stop the command "" + And I close the shell `` Then the output should match %r<^/.+/git$> # ^^ is absolute path to git: begins with a /, and ends with /git Examples: | shell | | bash | | zsh | + | fish | Scenario Outline: SCMPUFF_GIT_CMD is set to absolute path of a git command, eliminating aliases When I run `` interactively And I type "alias git=/foo/bar" - And I type `eval "$(scmpuff init -s)"` + And I initialize scmpuff in `` And I type "echo $SCMPUFF_GIT_CMD" - And I type "exit" - And I stop the command "" + And I close the shell `` Then the output should match %r<^/.+/git$> # ^^ is absolute path to git: begins with a /, and ends with /git Examples: | shell | | bash | | zsh | + | fish | Scenario Outline: SCMPUFF_GIT_CMD respects existing environment variables When I run `` interactively And I type "export SCMPUFF_GIT_CMD=/foo/hub" - And I type `eval "$(scmpuff init -s)"` + And I initialize scmpuff in `` And I type "echo $SCMPUFF_GIT_CMD" - And I type "exit" - And I stop the command "" + And I close the shell `` Then the output should contain exactly "/foo/hub" Examples: | shell | | bash | | zsh | + | fish | diff --git a/features/shell_wrappers.feature b/features/shell_wrappers.feature index d493650..f6fa30e 100644 --- a/features/shell_wrappers.feature +++ b/features/shell_wrappers.feature @@ -10,10 +10,10 @@ Feature: optional wrapping of normal git cmds in the shell And a 4 byte file named "foo.bar" And a 4 byte file named "bar.foo" When I run `` interactively - And I type `eval "$(scmpuff init -ws)"` + And I initialize scmpuff in `` And I type "scmpuff_status" And I type "git add 1" - And I type "exit" + And I close the shell `` Then the output should contain: """ # On branch: master | [*] => $e* @@ -31,22 +31,24 @@ Feature: optional wrapping of normal git cmds in the shell | shell | | bash | | zsh | + | fish | Scenario Outline: Wrapped `git add` can handle files with spaces properly Given I am in a git repository And an empty file named "file with spaces.txt" When I run `` interactively - And I type `eval "$(scmpuff init -ws)"` + And I initialize scmpuff in `` And I type "scmpuff_status" And I type "git add 1" - And I type "exit" + And I close the shell `` Then the exit status should be 0 And the output should match /new file:\s+\[1\] file with spaces.txt/ Examples: | shell | | bash | | zsh | + | fish | Scenario Outline: Wrapped `git reset` can handle files with spaces properly @@ -58,11 +60,10 @@ Feature: optional wrapping of normal git cmds in the shell And an empty file named "file with spaces.txt" And I successfully run `git add "file with spaces.txt"` When I run `` interactively - And I type `eval "$(scmpuff init -ws)"` + And I initialize scmpuff in `` And I type "scmpuff_status" And I type "git reset 1" - And I type "exit" - And I stop the command "" + And I close the shell `` Then the exit status should be 0 When I run `scmpuff status` Then the stdout from "scmpuff status" should contain: @@ -73,6 +74,7 @@ Feature: optional wrapping of normal git cmds in the shell | shell | | bash | | zsh | + | fish | @recent-git-only @@ -84,11 +86,10 @@ Feature: optional wrapping of normal git cmds in the shell And a 4 byte file named "foo.bar" And I successfully run `git add foo.bar` When I run `` interactively - And I type `eval "$(scmpuff init -ws)"` + And I initialize scmpuff in `` And I type "scmpuff_status" And I type "git restore --staged 1" - And I type "exit" - And I stop the command "" + And I close the shell `` Then the exit status should be 0 When I run `scmpuff status` Then the stdout from "scmpuff status" should contain: @@ -101,6 +102,7 @@ Feature: optional wrapping of normal git cmds in the shell | shell | | bash | | zsh | + | fish | Scenario Outline: Wrapped `git add` can handle shell expansions Given I am in a git repository @@ -108,12 +110,11 @@ Feature: optional wrapping of normal git cmds in the shell And an empty file named "file2.txt" And an empty file named "untracked file.txt" When I run `` interactively - And I type `eval "$(scmpuff init -ws)"` + And I initialize scmpuff in `` And I type "scmpuff_status" - And I type `FILE="file with spaces.txt"` + And I type `` And I type `git add "$FILE" 2` - And I type "exit" - Then the exit status should be 0 + And I close the shell `` And the output should contain: """ new file: [1] file with spaces.txt @@ -126,7 +127,9 @@ Feature: optional wrapping of normal git cmds in the shell """ untracked: [3] untracked file.txt """ + Then the exit status should be 0 Examples: - | shell | - | bash | - | zsh | + | shell | setfile | + | bash | FILE="file with spaces.txt" | + | zsh | FILE="file with spaces.txt" | + | fish | set FILE "file with spaces.txt" | diff --git a/features/support/env.rb b/features/support/env.rb index 8530f59..2f8a23f 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -15,3 +15,19 @@ tmpdir = Dir.mktmpdir("aruba") cd tmpdir end + +When(/I initialize scmpuff in `(.*)`/) do |shell| + if shell == "fish" + type %{scmpuff init -s --shell=fish | source} + else + type %{eval "$(scmpuff init -ws)"} + end +end + +When(/I close the shell `(.*)`/) do |shell| + status_var = shell == "fish" ? "$status" : "$?" + type "exit #{status_var}" + # fish doesn't run the inputted commands until stdin is closed + close_input + step("I stop the command \"#{shell}\"") +end From 4528273a73ff53b8cf96d794df0e10a87ce996d6 Mon Sep 17 00:00:00 2001 From: Jonathan del Strother Date: Sun, 2 Jan 2022 17:25:10 +0000 Subject: [PATCH 03/12] Fixes for fish test failures --- commands/inits/data/git_wrapper.fish | 16 +++------------- commands/inits/data/status_shortcuts.fish | 7 +++---- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/commands/inits/data/git_wrapper.fish b/commands/inits/data/git_wrapper.fish index d956930..b6d687f 100644 --- a/commands/inits/data/git_wrapper.fish +++ b/commands/inits/data/git_wrapper.fish @@ -1,21 +1,11 @@ # Based on https://github.com/arbelt/fish-plugin-scmpuff, # with scmpuff-exec support (https://github.com/mroth/scmpuff/pull/49) -set -x SCMPUFF_GIT_CMD (which git) - -if type -q hub - set -x SCMPUFF_GIT_CMD "hub" -end - - -if not type -q scmpuff - exit 1 -end functions -e git -function git - type -q $SCMPUFF_GIT_CMD; or set -x SCMPUFF_GIT_CMD (which git) +set -q SCMPUFF_GIT_CMD; or set -x SCMPUFF_GIT_CMD (which git) +function git if test (count $argv) -eq 0 eval $SCMPUFF_GIT_CMD set -l s $status @@ -25,7 +15,7 @@ function git switch $argv[1] case commit blame log rebase merge scmpuff exec -- "$SCMPUFF_GIT_CMD" $argv - case checkout diff rm reset + case checkout diff rm reset restore scmpuff exec --relative -- "$SCMPUFF_GIT_CMD" $argv case add scmpuff exec -- "$SCMPUFF_GIT_CMD" $argv diff --git a/commands/inits/data/status_shortcuts.fish b/commands/inits/data/status_shortcuts.fish index 363ee92..0c9a2cb 100644 --- a/commands/inits/data/status_shortcuts.fish +++ b/commands/inits/data/status_shortcuts.fish @@ -3,12 +3,11 @@ function scmpuff_status scmpuff_clear_vars set -lx scmpuff_env_char "e" - set -l cmd_output (/usr/bin/env scmpuff status --filelist $argv ^/dev/null) + set -l cmd_output (/usr/bin/env scmpuff status --filelist $argv) set -l es "$status" if test $es -ne 0 - git status - return $status + return $es end set -l files (string split \t $cmd_output[1]) @@ -25,7 +24,7 @@ end function scmpuff_clear_vars set -l scmpuff_env_char "e" - set -l scmpuff_env_vars (set -x | awk '{print $1}' | grep -E '^'$scmpuff_env_char'\d+') + set -l scmpuff_env_vars (set -x | awk '{print $1}' | grep -E '^'$scmpuff_env_char'[0-9]+') for v in $scmpuff_env_vars set -e $v From 3cfc4ad2a4e4d0366bea05d71f7745954c406639 Mon Sep 17 00:00:00 2001 From: Jonathan del Strother Date: Wed, 5 Jan 2022 17:59:52 +0000 Subject: [PATCH 04/12] Add fish to the devcontainer --- .devcontainer/Dockerfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 74da96b..200e8a0 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -6,12 +6,12 @@ FROM mcr.microsoft.com/vscode/devcontainers/ruby:0-${VARIANT} ARG NODE_VERSION="none" RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi -# [Optional] Uncomment this section to install additional OS packages. -# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ -# && apt-get -y install --no-install-recommends +# Install additional OS packages. +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install --no-install-recommends fish # [Optional] Uncomment this line to install additional gems. # RUN gem install # [Optional] Uncomment this line to install global node packages. -# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 \ No newline at end of file +# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 From 6f30a2ce82e75db3e69ea5aa18545bb6e091050b Mon Sep 17 00:00:00 2001 From: Jonathan del Strother Date: Mon, 10 Jan 2022 09:50:14 +0000 Subject: [PATCH 05/12] Deprecate --show in favor of --shell --- commands/inits/init.go | 26 +++++++++++++++++--------- commands/inits/scripts.go | 10 ++++------ features/support/env.rb | 2 +- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/commands/inits/init.go b/commands/inits/init.go index e2b1091..96d9214 100644 --- a/commands/inits/init.go +++ b/commands/inits/init.go @@ -10,7 +10,7 @@ import ( // define a variable outside with the correct scope to assign the flag to work // with. var includeAliases bool -var outputScript bool +var legacyShow bool var wrapGit bool var shellType string @@ -26,12 +26,18 @@ Outputs the bash/zsh/fish initialization script for scmpuff. This should probably be evaluated in your shell startup. `, Run: func(cmd *cobra.Command, args []string) { - if outputScript { + // If someone's using the old -s/--show flag, opt-in to the newer --shell=sh option + if legacyShow { + shellType = "sh" + } + if shellType != "" { printScript() } else { fmt.Println(helpString()) } }, + // Watch out for accidental args caused by NoOptDefVal (https://github.com/spf13/cobra/issues/866) + Args: cobra.NoArgs, } // --aliases @@ -41,12 +47,13 @@ This should probably be evaluated in your shell startup. "Include short aliases for convenience", ) - // --show - InitCmd.Flags().BoolVarP( - &outputScript, - "show", "s", false, + // --show (deprecated in favor of --shell) + InitCmd.Flags().BoolVar( + &legacyShow, + "show", false, "Output scmpuff initialization scripts", ) + InitCmd.Flags().MarkHidden("show") // --wrap InitCmd.Flags().BoolVarP( @@ -58,9 +65,10 @@ This should probably be evaluated in your shell startup. // --shell InitCmd.Flags().StringVarP( &shellType, - "shell", "", "sh", + "shell", "s", "", "Set shell type - 'sh' (for bash/zsh), or 'fish'", ) + InitCmd.Flag("shell").NoOptDefVal = "sh" return InitCmd } @@ -69,9 +77,9 @@ This should probably be evaluated in your shell startup. func helpString() string { return `# Initialize scmpuff by adding the following to ~/.bash_profile or ~/.zshrc: -eval "$(scmpuff init -s --shell=sh)" +eval "$(scmpuff init --shell=sh)" # or the following to ~/.config/fish/config.fish: -scmpuff init -s --shell=fish | source` +scmpuff init --shell=fish | source` } diff --git a/commands/inits/scripts.go b/commands/inits/scripts.go index 7bd7834..fb86fa8 100644 --- a/commands/inits/scripts.go +++ b/commands/inits/scripts.go @@ -21,12 +21,10 @@ var scriptGitWrapper string var scriptGitWrapperFish string func printScript() { - if outputScript { - if shellType == "fish" { - fmt.Println(scriptStatusShortcutsFish) - } else { - fmt.Println(scriptStatusShortcuts) - } + if shellType == "fish" { + fmt.Println(scriptStatusShortcutsFish) + } else { + fmt.Println(scriptStatusShortcuts) } if includeAliases { diff --git a/features/support/env.rb b/features/support/env.rb index 2f8a23f..dd96ffc 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -18,7 +18,7 @@ When(/I initialize scmpuff in `(.*)`/) do |shell| if shell == "fish" - type %{scmpuff init -s --shell=fish | source} + type %{scmpuff init -w --shell=fish | source} else type %{eval "$(scmpuff init -ws)"} end From 7edf091dda1a4e9b17297fa0aaa80c6f749c2099 Mon Sep 17 00:00:00 2001 From: Jonathan del Strother Date: Mon, 10 Jan 2022 10:10:45 +0000 Subject: [PATCH 06/12] Add some argument-validation to `scmpuff init --shell=foo` --- commands/inits/init.go | 15 +++++++++++---- features/command_init.feature | 5 +++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/commands/inits/init.go b/commands/inits/init.go index 96d9214..dbd9b1e 100644 --- a/commands/inits/init.go +++ b/commands/inits/init.go @@ -2,6 +2,7 @@ package inits import ( "fmt" + "os" "github.com/spf13/cobra" ) @@ -27,13 +28,19 @@ This should probably be evaluated in your shell startup. `, Run: func(cmd *cobra.Command, args []string) { // If someone's using the old -s/--show flag, opt-in to the newer --shell=sh option - if legacyShow { + if legacyShow { shellType = "sh" } - if shellType != "" { - printScript() - } else { + switch shellType { + case "": fmt.Println(helpString()) + + case "sh", "bash", "zsh", "fish": + printScript() + + default: + fmt.Fprintf(os.Stderr, "Unrecognized shell '%s'\n", shellType) + os.Exit(1) } }, // Watch out for accidental args caused by NoOptDefVal (https://github.com/spf13/cobra/issues/866) diff --git a/features/command_init.feature b/features/command_init.feature index 6b9fdf8..bb299f2 100644 --- a/features/command_init.feature +++ b/features/command_init.feature @@ -7,6 +7,11 @@ Feature: init command When I successfully run `scmpuff init -s` Then the output should contain "scmpuff_status()" + Scenario: init with an unrecognized shell should produce an error + When I run `scmpuff init --shell=oil` + Then the exit status should be 1 + Then the output should contain "Unrecognized shell 'oil'" + Scenario Outline: --aliases controls short aliases in output (default: yes) When I successfully run `scmpuff init ` Then the output contain "alias gs='scmpuff_status'" From 3a5029236b75f6d21fa8ee744bb84d1abb505ba5 Mon Sep 17 00:00:00 2001 From: Matthew Rothenberg Date: Sun, 16 Jan 2022 13:12:46 -0500 Subject: [PATCH 07/12] detailed setup info in scmpuff init --help --- commands/inits/init.go | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/commands/inits/init.go b/commands/inits/init.go index dbd9b1e..697ccec 100644 --- a/commands/inits/init.go +++ b/commands/inits/init.go @@ -22,21 +22,31 @@ func CommandInit() *cobra.Command { Use: "init", Short: "Output initialization script", Long: ` -Outputs the bash/zsh/fish initialization script for scmpuff. +Outputs the shell initialization script for scmpuff. -This should probably be evaluated in your shell startup. +Initialize scmpuff by adding the following to your ~/.bash_profile or ~/.zshrc: + + eval "$(scmpuff init --shell=sh)" + +For fish shell, add the following to ~/.config/fish/config.fish instead: + + scmpuff init --shell=fish | source + +There are a number of flags to customize the shell integration. `, Run: func(cmd *cobra.Command, args []string) { - // If someone's using the old -s/--show flag, opt-in to the newer --shell=sh option + // If someone's using the old ---show flag, opt-in to the newer --shell defaults if legacyShow { - shellType = "sh" + shellType = defaultShellType() } switch shellType { case "": - fmt.Println(helpString()) + cmd.Help() + os.Exit(0) case "sh", "bash", "zsh", "fish": printScript() + os.Exit(0) default: fmt.Fprintf(os.Stderr, "Unrecognized shell '%s'\n", shellType) @@ -51,7 +61,7 @@ This should probably be evaluated in your shell startup. InitCmd.Flags().BoolVarP( &includeAliases, "aliases", "a", true, - "Include short aliases for convenience", + "Include short git aliases", ) // --show (deprecated in favor of --shell) @@ -73,20 +83,15 @@ This should probably be evaluated in your shell startup. InitCmd.Flags().StringVarP( &shellType, "shell", "s", "", - "Set shell type - 'sh' (for bash/zsh), or 'fish'", + "Output shell type: sh | bash | zsh | fish", ) - InitCmd.Flag("shell").NoOptDefVal = "sh" + InitCmd.Flag("shell").NoOptDefVal = defaultShellType() return InitCmd } -// TODO: check for proper shell version -func helpString() string { - return `# Initialize scmpuff by adding the following to ~/.bash_profile or ~/.zshrc: - -eval "$(scmpuff init --shell=sh)" - -# or the following to ~/.config/fish/config.fish: - -scmpuff init --shell=fish | source` +// defaultShell returns the shellType assumed if user does not specify. +// in the future, we may wish to customize this based on the $SHELL variable. +func defaultShellType() string { + return "sh" } From d57871637a679a8e2e17c0b80e302854c8a5a61a Mon Sep 17 00:00:00 2001 From: Matthew Rothenberg Date: Mon, 17 Jan 2022 14:34:33 -0500 Subject: [PATCH 08/12] init: refactor script collection concatenation --- commands/inits/data/git_wrapper.fish | 1 - commands/inits/init.go | 26 +++++++++-------- commands/inits/scripts.go | 42 ++++++++++++++++++---------- 3 files changed, 42 insertions(+), 27 deletions(-) diff --git a/commands/inits/data/git_wrapper.fish b/commands/inits/data/git_wrapper.fish index b6d687f..5b1436e 100644 --- a/commands/inits/data/git_wrapper.fish +++ b/commands/inits/data/git_wrapper.fish @@ -1,6 +1,5 @@ # Based on https://github.com/arbelt/fish-plugin-scmpuff, # with scmpuff-exec support (https://github.com/mroth/scmpuff/pull/49) - functions -e git set -q SCMPUFF_GIT_CMD; or set -x SCMPUFF_GIT_CMD (which git) diff --git a/commands/inits/init.go b/commands/inits/init.go index 697ccec..e8360df 100644 --- a/commands/inits/init.go +++ b/commands/inits/init.go @@ -3,20 +3,19 @@ package inits import ( "fmt" "os" + "strings" "github.com/spf13/cobra" ) -// Since the flags are defined and used in different locations, we need to -// define a variable outside with the correct scope to assign the flag to work -// with. -var includeAliases bool -var legacyShow bool -var wrapGit bool -var shellType string - // CommandInit generates the command handler for `scmpuff init` func CommandInit() *cobra.Command { + var ( + shellType string + includeAliases bool + wrapGit bool + legacyShow bool + ) var InitCmd = &cobra.Command{ Use: "init", @@ -39,13 +38,18 @@ There are a number of flags to customize the shell integration. if legacyShow { shellType = defaultShellType() } - switch shellType { + + switch strings.ToLower(shellType) { case "": cmd.Help() os.Exit(0) - case "sh", "bash", "zsh", "fish": - printScript() + case "sh", "bash", "zsh": + fmt.Println(bashCollection.Output(wrapGit, includeAliases)) + os.Exit(0) + + case "fish": + fmt.Println(fishCollection.Output(wrapGit, includeAliases)) os.Exit(0) default: diff --git a/commands/inits/scripts.go b/commands/inits/scripts.go index fb86fa8..6787679 100644 --- a/commands/inits/scripts.go +++ b/commands/inits/scripts.go @@ -2,7 +2,7 @@ package inits import ( _ "embed" - "fmt" + "strings" ) //go:embed data/status_shortcuts.sh @@ -20,22 +20,34 @@ var scriptGitWrapper string //go:embed data/git_wrapper.fish var scriptGitWrapperFish string -func printScript() { - if shellType == "fish" { - fmt.Println(scriptStatusShortcutsFish) - } else { - fmt.Println(scriptStatusShortcuts) - } +type scriptCollection struct { + statusShortcuts string + gitWrapper string + aliases string +} - if includeAliases { - fmt.Println(scriptAliases) - } +var bashCollection = scriptCollection{ + statusShortcuts: scriptStatusShortcuts, + gitWrapper: scriptGitWrapper, + aliases: scriptAliases, +} +var fishCollection = scriptCollection{ + statusShortcuts: scriptStatusShortcutsFish, + gitWrapper: scriptGitWrapperFish, + aliases: scriptAliases, +} + +func (sc scriptCollection) Output(wrapGit, aliases bool) string { + var b strings.Builder + b.WriteString(sc.statusShortcuts) if wrapGit { - if shellType == "fish" { - fmt.Println(scriptGitWrapperFish) - } else { - fmt.Println(scriptGitWrapper) - } + b.WriteRune('\n') + b.WriteString(sc.gitWrapper) + } + if aliases { + b.WriteRune('\n') + b.WriteString(sc.aliases) } + return b.String() } From 377cc7f377c09f82dabbfda58e8f1630ea9a1a45 Mon Sep 17 00:00:00 2001 From: Matthew Rothenberg Date: Mon, 17 Jan 2022 15:46:07 -0500 Subject: [PATCH 09/12] init: auto default shell detection based on $SHELL --- commands/inits/init.go | 9 +++++++++ commands/inits/init_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 commands/inits/init_test.go diff --git a/commands/inits/init.go b/commands/inits/init.go index e8360df..5aaca0f 100644 --- a/commands/inits/init.go +++ b/commands/inits/init.go @@ -3,6 +3,7 @@ package inits import ( "fmt" "os" + "path/filepath" "strings" "github.com/spf13/cobra" @@ -97,5 +98,13 @@ There are a number of flags to customize the shell integration. // defaultShell returns the shellType assumed if user does not specify. // in the future, we may wish to customize this based on the $SHELL variable. func defaultShellType() string { + if shellenv, ok := os.LookupEnv("SHELL"); ok { + base := filepath.Base(shellenv) + switch base { + case "sh", "bash", "zsh", "fish": + return base + } + } + return "sh" } diff --git a/commands/inits/init_test.go b/commands/inits/init_test.go new file mode 100644 index 0000000..029db41 --- /dev/null +++ b/commands/inits/init_test.go @@ -0,0 +1,27 @@ +package inits + +import "testing" + +func Test_defaultShellType(t *testing.T) { + tests := []struct { + shellenv string + want string + }{ + // supported shells at a bunch of different locations + {"/bin/zsh", "zsh"}, + {"/usr/bin/zsh", "zsh"}, + {"/usr/local/bin/zsh", "zsh"}, + {"/bin/bash", "bash"}, + {"/usr/local/bin/fish", "fish"}, + + // edge cases + {"", "sh"}, + {"/bin/unsupported", "sh"}, + } + for _, tt := range tests { + t.Setenv("SHELL", tt.shellenv) + if got := defaultShellType(); got != tt.want { + t.Errorf("defaultShellType(%v) = %v, want %v", tt.shellenv, got, tt.want) + } + } +} From 39218cd4d0510b276105825723f3689d27a2ce8b Mon Sep 17 00:00:00 2001 From: Matthew Rothenberg Date: Mon, 17 Jan 2022 15:50:33 -0500 Subject: [PATCH 10/12] chore(ci): appease codefactor on devcontainer The entire directory *should* be ignored in our settings, but it doesnt seem to be picking up, so manually ignoring the one rule for now. --- .devcontainer/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 200e8a0..54d9bab 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -7,6 +7,7 @@ ARG NODE_VERSION="none" RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi # Install additional OS packages. +# hadolint ignore=DL3009 RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ && apt-get -y install --no-install-recommends fish From d519cd97acb113d0e614633291dc54410db91a4d Mon Sep 17 00:00:00 2001 From: Matthew Rothenberg Date: Mon, 17 Jan 2022 15:53:24 -0500 Subject: [PATCH 11/12] build: set min go version to go1.17 (using TB.Setenv in init tests) --- go.mod | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index b6b48e7..1299043 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,10 @@ module github.com/mroth/scmpuff -go 1.16 +go 1.17 require github.com/spf13/cobra v1.3.0 + +require ( + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect +) From 552076f706c2b02aff1aa05ecf33781ae4ef81b8 Mon Sep 17 00:00:00 2001 From: Jonathan del Strother Date: Tue, 18 Jan 2022 10:23:01 +0000 Subject: [PATCH 12/12] Fix `rake install` on M1 macs With the stricter code-signing requirements on M1 macs, `cp`ing the binary exposes a bug in Apple code-signing where trying to execute the resulting binary immediately exits with "Killed: 9" By rm-ing then cp-ing, the destination file gets a new inode number and avoids this problem. https://developer.apple.com/documentation/security/updating_mac_software --- Rakefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index 73b4301..c3945fd 100644 --- a/Rakefile +++ b/Rakefile @@ -10,7 +10,11 @@ end desc "builds & installs the binary to $GOPATH/bin" task :install => :build do - cp "bin/scmpuff", "#{ENV['GOPATH']}/bin/scmpuff" + # Don't cp directly over an existing file - it causes problems with Apple code signing. + # https://developer.apple.com/documentation/security/updating_mac_software + destination = "#{ENV['GOPATH']}/bin/scmpuff" + rm destination if File.exist?(destination) + cp "bin/scmpuff", destination end desc "run unit tests"