Skip to content

Commit

Permalink
Merge pull request #65 from jdelStrother/fish
Browse files Browse the repository at this point in the history
Fish support (closes #8, closes #27)
  • Loading branch information
mroth authored Jan 30, 2022
2 parents 97695e9 + 552076f commit 8a7a651
Show file tree
Hide file tree
Showing 14 changed files with 279 additions and 80 deletions.
9 changes: 5 additions & 4 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ 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 <your-package-list-here>
# Install additional OS packages.
# hadolint ignore=DL3009
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 <your-gem-names-here>

# [Optional] Uncomment this line to install global node packages.
# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g <your-package-here>" 2>&1
# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g <your-package-here>" 2>&1
7 changes: 5 additions & 2 deletions .github/workflows/test-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.


Expand Down
6 changes: 5 additions & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
25 changes: 25 additions & 0 deletions commands/inits/data/git_wrapper.fish
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# 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)

function 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 restore
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
32 changes: 32 additions & 0 deletions commands/inits/data/status_shortcuts.fish
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# 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)
set -l es "$status"

if test $es -ne 0
return $es
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'[0-9]+')

for v in $scmpuff_env_vars
set -e $v
end
end
89 changes: 67 additions & 22 deletions commands/inits/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,80 @@ package inits

import (
"fmt"
"os"
"path/filepath"
"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 outputScript bool
var wrapGit bool

// 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",
Short: "Output initialization script",
Long: `
Outputs the bash/zsh initialization script for scmpuff.
Outputs the shell initialization script for scmpuff.
Initialize scmpuff by adding the following to your ~/.bash_profile or ~/.zshrc:
eval "$(scmpuff init --shell=sh)"
This should probably be evaluated in your shell startup.
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 outputScript {
printScript()
} else {
fmt.Println(helpString())
// If someone's using the old ---show flag, opt-in to the newer --shell defaults
if legacyShow {
shellType = defaultShellType()
}

switch strings.ToLower(shellType) {
case "":
cmd.Help()
os.Exit(0)

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:
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)
Args: cobra.NoArgs,
}

// --aliases
InitCmd.Flags().BoolVarP(
&includeAliases,
"aliases", "a", true,
"Include short aliases for convenience",
"Include short git aliases",
)

// --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(
Expand All @@ -54,12 +84,27 @@ This should probably be evaluated in your shell startup.
"Wrap standard git commands",
)

// --shell
InitCmd.Flags().StringVarP(
&shellType,
"shell", "s", "",
"Output shell type: sh | bash | zsh | fish",
)
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:
// 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
}
}

eval "$(scmpuff init -s)"`
return "sh"
}
27 changes: 27 additions & 0 deletions commands/inits/init_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
42 changes: 33 additions & 9 deletions commands/inits/scripts.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,52 @@ package inits

import (
_ "embed"
"fmt"
"strings"
)

//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

func printScript() {
if outputScript {
fmt.Println(scriptStatusShortcuts)
}
//go:embed data/git_wrapper.fish
var scriptGitWrapperFish string

if includeAliases {
fmt.Println(scriptAliases)
}
type scriptCollection struct {
statusShortcuts string
gitWrapper string
aliases string
}

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 {
fmt.Println(scriptGitWrapper)
b.WriteRune('\n')
b.WriteString(sc.gitWrapper)
}
if aliases {
b.WriteRune('\n')
b.WriteString(sc.aliases)
}
return b.String()
}
11 changes: 9 additions & 2 deletions features/command_init.feature
Original file line number Diff line number Diff line change
Expand Up @@ -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 <flags>`
Then the output <should?> contain "alias gs='scmpuff_status'"
Expand All @@ -32,12 +37,14 @@ Feature: init command

Scenario Outline: Evaling init -s defines status shortcuts in environment
When I run `<shell>` interactively
And I type `eval "$(scmpuff init -s)"`
And I initialize scmpuff in `<shell>`
And I type "type scmpuff_status"
And I type "type scmpuff_clear_vars"
And I type "exit"
And I close the shell `<shell>`
Then the output should not contain "not found"
Examples:
| shell |
| bash |
| zsh |
| fish |

Loading

0 comments on commit 8a7a651

Please sign in to comment.