diff --git a/CHANGELOG.md b/CHANGELOG.md index 3be77ab..794809b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,16 @@ # Change Log -## [0.7.0][0.7.0] - 2016-02-? -git checkout -b daenney-all-the-commands master +## [0.7.0][v070] - 2016-02-11 -* Use `command(1)` when calling non-builtins. Closes #79. +* Welcome aboard @daenney, the newest Fisherman organization member. If you want to be part of the organization just let [me](https://github.com/bucaran) or @daenney know. + +* Add the ability to install plugins from Gists. You can distribute a very simple, one-single function plugin in the form of a Gist. Your users can install it using `fisher install url` and Fisherman will query the Gist using the GitHub API to get a list of the Gist files and use the name of the first identified `*.fish` file to name the plugin in your system. Since there is no formal way to _name_ a Gist, and you may prefer to keep the "description" field for the actual description and not a name, Fisherman supports only one `fish` file per Gist. Closes #75. + +* Use `command(1)` when calling non-builtins. Thanks @daenney. Closes #79. + +* Add `__fisher_plugin_can_enable` to detect installing a prompt that is not the current one. Closes #78. + +* Remove the ability to install a plugin in a parent directory using `..` or `../` or even worse, `../../` as well as other combinations that navigate to a parent directory. I find the use case odd at best, and more dangerous that useful. If you want to install a local plugin use the full path or a relative path, always top down. `fisher install .` or `fisher install my/plugin` or `fisher install /Users/$USER/path/to/plugin`. Closes #81. ## [0.6.0][v060] - 2016-02-07 diff --git a/README.md b/README.md index 4e7565d..82b2faa 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,23 @@

+
Fisherman + width=660px + src="https://rawgit.com/fisherman/logo/master/fisherman-black-white.svg"> +
+
+

[![Build Status][travis-badge]][travis-link] [![Fisherman Version][version-badge]][version-link] [![Wharf][wharf-badge]][wharf-link] -# Fisherman +
**Fisherman** is a blazing [fast](#performance), modern plugin manager for [Fish](http://fishshell.com/). diff --git a/functions/__fisher_file.fish b/functions/__fisher_file.fish index d07627c..28668bd 100644 --- a/functions/__fisher_file.fish +++ b/functions/__fisher_file.fish @@ -8,6 +8,10 @@ function __fisher_file } } + /@http/ { + gsub("@.*$", "", $1) + } + !/^[ \t]*(#.*)*$/ { gsub("^[|*>]|#.*", "") diff --git a/functions/__fisher_file_contains.fish b/functions/__fisher_file_contains.fish index 2b684aa..dc84538 100644 --- a/functions/__fisher_file_contains.fish +++ b/functions/__fisher_file_contains.fish @@ -1,4 +1,4 @@ function __fisher_file_contains -a plugin set -e argv[1] - grep -E "^(package *|plugin *)?$plugin*\$" $argv + grep -E "^(package *|plugin *)?$plugin.*\$" $argv end diff --git a/functions/__fisher_gist_to_name.fish b/functions/__fisher_gist_to_name.fish new file mode 100644 index 0000000..004a784 --- /dev/null +++ b/functions/__fisher_gist_to_name.fish @@ -0,0 +1,27 @@ +function __fisher_gist_to_name -a url + if test -z "$url" + return 1 + end + + set -l id (printf "%s\n" $url | sed 's|.*/||') + set -l gists https://api.github.com/gists + + set -l name ( + curl -s $gists/$id | awk ' + + /"files": / { files++ } + + /"[^ ]+.fish": / && files { + gsub("^ *\"|\.fish.*", "") + print + } + + ' ^ /dev/null + ) + + if test -z "$name" + return 1 + end + + printf "%s\n" $name +end diff --git a/functions/__fisher_list.fish b/functions/__fisher_list.fish index 6ecca98..169cd0e 100644 --- a/functions/__fisher_list.fish +++ b/functions/__fisher_list.fish @@ -22,8 +22,8 @@ function __fisher_list -a source printf "%s%s\n" ">" $i else if test -L $fisher_cache/$i - printf "%s%s\n" "|" $i - + printf "%s%s\n" "@" $i + else printf "%s%s\n" "*" $i end diff --git a/functions/__fisher_plugin_can_enable.fish b/functions/__fisher_plugin_can_enable.fish new file mode 100644 index 0000000..4cd55b0 --- /dev/null +++ b/functions/__fisher_plugin_can_enable.fish @@ -0,0 +1,12 @@ +function __fisher_plugin_can_enable -a name path + + # Check whether a plugin is the current prompt or not a prompt. We use this + # method when the user is trying to Update or Uninstall a prompt that is not + # currently enabled, and we wish to skip only the enable / disable phase. + + if not __fisher_path_is_prompt $path + return 0 + end + + test "$name" = "$fisher_prompt" +end diff --git a/functions/__fisher_plugin_validate.fish b/functions/__fisher_plugin_validate.fish index 0dbb5ee..b138885 100644 --- a/functions/__fisher_plugin_validate.fish +++ b/functions/__fisher_plugin_validate.fish @@ -1,6 +1,9 @@ function __fisher_plugin_validate -a plugin switch "$plugin" - case . /\* ./\* ../\* + case ..\* + return 1 + + case . /\* ./\* if test ! -e $plugin return 1 end @@ -30,6 +33,7 @@ function __fisher_plugin_validate -a plugin s|^bb[:/]+|https://bitbucket.org/| s|^omf[:/]+|https://github.com/oh-my-fish/| s|^($id+)/($id+)\$|https://github.com/\1/\2| + s|^(gist\.github\.com.*)|https://\1| s|^http(s?)[:/]*|http\1://| s|https://github((.com)?/)?|https://github.com/| s|/*(\.git/*)*\$||g" \ diff --git a/functions/__fisher_url_from_path.fish b/functions/__fisher_url_from_path.fish index 6c0b5b5..03c07ce 100644 --- a/functions/__fisher_url_from_path.fish +++ b/functions/__fisher_url_from_path.fish @@ -6,6 +6,18 @@ function __fisher_url_from_path -a path if test -L "$path" readlink $path else - git -C "$path" ls-remote --get-url ^ /dev/null + set -l url (git -C "$path" ls-remote --get-url ^ /dev/null) + + if test -z "$url" + return 1 + end + + switch "$url" + case \*gist.github.com\* + printf "%s@%s\n" (basename $path) $url + + case \* + printf "%s\n" "$url" + end end end diff --git a/functions/fisher.fish b/functions/fisher.fish index a8b60ca..5008360 100644 --- a/functions/fisher.fish +++ b/functions/fisher.fish @@ -25,8 +25,8 @@ function fisher -d "Fish Plugin Manager" continue end - printf "fisher: '%s' is not a valid option.\n" $1 >& 2 - fisher --help >& 2 + printf "fisher: '%s' is not a valid option.\n" $1 > /dev/stderr + fisher --help > /dev/stderr return 1 end end @@ -44,8 +44,8 @@ function fisher -d "Fish Plugin Manager" end if not functions -q "fisher_$value" - printf "fisher: '%s' is not a valid command\n" "$value" >& 2 - fisher --help >& 2 + printf "fisher: '%s' is not a valid command\n" "$value" > /dev/stderr + fisher --help > /dev/stderr return 1 end diff --git a/functions/fisher_help.fish b/functions/fisher_help.fish index 3086b34..5c937e8 100644 --- a/functions/fisher_help.fish +++ b/functions/fisher_help.fish @@ -42,8 +42,8 @@ function fisher_help -d "Show Help" return case \* - printf "fisher: '%s' is not a valid option.\n" $1 >& 2 - fisher_help --help >& 2 + printf "fisher: '%s' is not a valid option.\n" $1 > /dev/stderr + fisher_help --help > /dev/stderr return 1 end end diff --git a/functions/fisher_install.fish b/functions/fisher_install.fish index 0b4a05b..1427b95 100644 --- a/functions/fisher_install.fish +++ b/functions/fisher_install.fish @@ -25,8 +25,8 @@ function fisher_install -d "Install Plugins" return case \* - printf "fisher: '%s' is not a valid option.\n" $1 >& /dev/stderr - fisher_install -h >& /dev/stderr + printf "fisher: '%s' is not a valid option.\n" $1 > /dev/stderr + fisher_install -h > /dev/stderr return 1 end end @@ -47,11 +47,19 @@ function fisher_install -d "Install Plugins" end | while read -l item if not set item (__fisher_plugin_validate $item) - printf "fisher: '%s' is not a valid name, path or url.\n" $item > $stderr + printf "fisher: '%s' is not a valid name, path or URL.\n" $item > $stderr continue end switch "$item" + case https://gist.github.com\* + if set -l name (wait "__fisher_gist_to_name $item") + printf "%s %s\n" $item $name + else + set total (math $total - 1) + printf "fisher: '%s' is not a valid Gist or URL.\n" $item > $stderr + end + case \*/\* printf "%s %s\n" $item (printf "%s\n" $item | __fisher_name) @@ -124,7 +132,8 @@ function fisher_install -d "Install Plugins" if test ! -z "$skipped" printf "%s plugin/s skipped (%s)\n" (count $skipped) ( - printf "%s\n" $skipped | paste -sd ' ' -) > $stdout + printf "%s\n" $skipped | paste -sd ' ' - + ) > $stdout end if test "$count" -le 0 diff --git a/functions/fisher_search.fish b/functions/fisher_search.fish index 8ae0692..143c075 100644 --- a/functions/fisher_search.fish +++ b/functions/fisher_search.fish @@ -71,8 +71,8 @@ function fisher_search -d "Search Plugins" return case \* - printf "fisher: '%s' is not a valid option.\n" $1 >& 2 - fisher_search -h >& 2 + printf "fisher: '%s' is not a valid option.\n" $1 > /dev/stderr + fisher_search -h > /dev/stderr return 1 end end diff --git a/functions/fisher_uninstall.fish b/functions/fisher_uninstall.fish index c5fa551..9fa3ccb 100644 --- a/functions/fisher_uninstall.fish +++ b/functions/fisher_uninstall.fish @@ -25,8 +25,8 @@ function fisher_uninstall -d "Uninstall Plugins" return case \* - printf "fisher: '%s' is not a valid option.\n" $1 >& /dev/stderr - fisher_uninstall -h >& /dev/stderr + printf "fisher: '%s' is not a valid option.\n" $1 > /dev/stderr + fisher_uninstall -h > /dev/stderr return 1 end end @@ -77,12 +77,7 @@ function fisher_uninstall -d "Uninstall Plugins" set index (math $index + 1) end - if begin not __fisher_path_is_prompt $path; or test "$name" = "$fisher_prompt"; end - - # You can use --force to remove any plugin from the cache. If prompt A is enabled - # you can still uninstall prompt B using --force. This will delete B's repository - # from $fisher_cache. - + if __fisher_plugin_can_enable "$name" "$path" __fisher_plugin_disable "$name" "$path" end diff --git a/functions/fisher_update.fish b/functions/fisher_update.fish index 3b35e74..0f60b13 100644 --- a/functions/fisher_update.fish +++ b/functions/fisher_update.fish @@ -24,8 +24,8 @@ function fisher_update -d "Update Plugins/Fisherman" return case \* - printf "fisher: '%s' is not a valid option.\n" $1 >& /dev/stderr - fisher_update -h >& /dev/stderr + printf "fisher: '%s' is not a valid option.\n" $1 > /dev/stderr + fisher_update -h > /dev/stderr return 1 end end @@ -100,7 +100,9 @@ function fisher_update -d "Update Plugins/Fisherman" end end - fisher_install --quiet --force -- $name + if __fisher_plugin_can_enable "$name" "$path" + fisher_install --quiet --force -- $name + end set count (math $count + 1) end diff --git a/functions/wait.fish b/functions/wait.fish index a75338c..96c6c16 100644 --- a/functions/wait.fish +++ b/functions/wait.fish @@ -38,8 +38,8 @@ function wait -d "Run commands and display a spinner" return case \* - printf "wait: '%s' is not a valid option\n" $1 >& 2 - wait -h >& 2 + printf "wait: '%s' is not a valid option\n" $1 > /dev/stderr + wait -h > /dev/stderr return 1 end end diff --git a/test/fixtures/gist/foo.json b/test/fixtures/gist/foo.json new file mode 100644 index 0000000..095956d --- /dev/null +++ b/test/fixtures/gist/foo.json @@ -0,0 +1,46 @@ +{ + "url": "https://api.github.com/gists/897324897f239847w238974g", + "forks_url": "https://api.github.com/gists/897324897f239847w238974g/forks", + "commits_url": "https://api.github.com/gists/897324897f239847w238974g/commits", + "id": "897324897f239847w238974g", + "git_pull_url": "https://gist.github.com/897324897f239847w238974g.git", + "git_push_url": "https://gist.github.com/897324897f239847w238974g.git", + "html_url": "https://gist.github.com/897324897f239847w238974g", + "files": { + "foo.fish": { + "filename": "foo.fish", + "type": "text/plain", + "language": "fish", + "raw_url": "https://gist.githubusercontent.com/user/897324897f239847w238974g/raw/c4ede9079b2080b0f06bf25a55c0f500de37e7f3/foo.fish", + "size": 530, + "truncated": false, + "content": "...." + } + }, + "public": true, + "created_at": "2016-02-05T20:43:29Z", + "updated_at": "2016-02-10T18:37:28Z", + "description": "foo description", + "comments": 0, + "user": null, + "comments_url": "https://api.github.com/gists/897324897f239847w238974g/comments", + "owner": { + "login": "user", + "id": 0000000, + "avatar_url": "https://avatars.githubusercontent.com/u/8317250?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/user", + "html_url": "https://github.com/user", + "followers_url": "https://api.github.com/users/user/followers", + "following_url": "https://api.github.com/users/user/following{/other_user}", + "gists_url": "https://api.github.com/users/user/gists{/gist_id}", + "starred_url": "https://api.github.com/users/user/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/user/subscriptions", + "organizations_url": "https://api.github.com/users/user/orgs", + "repos_url": "https://api.github.com/users/user/repos", + "events_url": "https://api.github.com/users/user/events{/privacy}", + "received_events_url": "https://api.github.com/users/user/received_events", + "type": "User", + "site_admin": false + } +} diff --git a/test/fixtures/manifest/fishfile b/test/fixtures/manifest/fishfile index 05acaa5..4ac6aad 100644 --- a/test/fixtures/manifest/fishfile +++ b/test/fixtures/manifest/fishfile @@ -39,3 +39,9 @@ https://github.com/quux/fisher-thud # You can install a plugin from a local directory too. /Users/fisherboy/Projects/fisherman/plugins/chomp + +# You can also install a plugin from a gist. There is no formal way to name a gist, +# so a gist plugin is recommended to export a single fish function. The repository +# may include other files too, but there should be only *one* fish file. + +gisty@https://gist.github.com/xxxx diff --git a/test/fixtures/manifest/fishfile-no-comments b/test/fixtures/manifest/fishfile-no-comments index dc6a11b..36e51f3 100644 --- a/test/fixtures/manifest/fishfile-no-comments +++ b/test/fixtures/manifest/fishfile-no-comments @@ -9,3 +9,4 @@ https://github.com/quux/omf-theme-foobar https://github.com/quux/fish-fred https://github.com/quux/fisher-thud /Users/fisherboy/Projects/fisherman/plugins/chomp +gisty@https://gist.github.com/xxxx diff --git a/test/fixtures/manifest/fishfile-parsed b/test/fixtures/manifest/fishfile-parsed index 25d1f3c..1604190 100644 --- a/test/fixtures/manifest/fishfile-parsed +++ b/test/fixtures/manifest/fishfile-parsed @@ -9,3 +9,4 @@ https://github.com/quux/omf-theme-foobar https://github.com/quux/fish-fred https://github.com/quux/fisher-thud /Users/fisherboy/Projects/fisherman/plugins/chomp +gisty diff --git a/test/gist-to-name.fish b/test/gist-to-name.fish new file mode 100644 index 0000000..368a97f --- /dev/null +++ b/test/gist-to-name.fish @@ -0,0 +1,22 @@ +set -g gist $DIRNAME/fixtures/gist + +function -S setup + function -S curl -a flags url + cat $gist/(basename $url).json + end +end + +function -S teardown + functions -e curl +end + +test "$TESTNAME - Fail if URL is an empty string" + 1 -eq ( + __fisher_gist_to_name "" + printf $status + ) +end + +test "$TESTNAME - Retrieve the name of the first *.fish file in the JSON stream" + foo = (__fisher_gist_to_name gist.github.com/foo) +end diff --git a/test/helpers/git-ls-remote.fish b/test/helpers/git-ls-remote.fish index 935d6a2..10be02d 100644 --- a/test/helpers/git-ls-remote.fish +++ b/test/helpers/git-ls-remote.fish @@ -7,6 +7,8 @@ function -S git -a 1 file ctx action echo https://github.com/bar/bar case baz echo https://github.com/baz/baz + case norf + echo https://gist.github.com/norf end end end diff --git a/test/list-fishfile.fish b/test/list-fishfile.fish index 6ae4619..9bd89c7 100644 --- a/test/list-fishfile.fish +++ b/test/list-fishfile.fish @@ -1,6 +1,6 @@ set -l manifest $DIRNAME/fixtures/manifest -set -l plugins foo bar baz norf zerg quux hoge foobar fred thud chomp boo loo +set -l plugins foo bar baz norf zerg quux hoge foobar fred thud chomp boo loo gisty test "$TESTNAME - Parse fishfile and retrieve plugin names with fisher --list=fishfile" diff --git a/test/list.fish b/test/list.fish index bf945d1..0729007 100644 --- a/test/list.fish +++ b/test/list.fish @@ -64,10 +64,12 @@ end test "$TESTNAME - Get URL from path (__fisher_url_from_path) with fisher --list=url" (fisher --list=url) = ( - for plugin in foo bar baz theme + for plugin in foo bar baz theme norf switch "$plugin" case theme echo $path/theme + case norf + echo $plugin@https://gist.github.com/$plugin case \* echo https://github.com/$plugin/$plugin end diff --git a/test/plugin-can-enable.fish b/test/plugin-can-enable.fish new file mode 100644 index 0000000..0eae8be --- /dev/null +++ b/test/plugin-can-enable.fish @@ -0,0 +1,24 @@ +set -l path $DIRNAME/.t-$TESTNAME-(random) +set -l plugins foo bar baz norf +set -l prompts baz norf + +function -S setup + mkdir -p $path/{$plugins} + + touch $path/{$prompts}/fish_prompt.fish + + set -U fisher_prompt $prompts[1] + + for plugin in $plugins + __fisher_plugin_can_enable $plugin $path/$plugin + echo $status + end > $path/output +end + +function -S teardown + rm -rf $path +end + +test "$TESTNAME - Check whether a plugin is the current prompt or not a prompt." + 0 0 0 1 = (cat $path/output) +end diff --git a/test/search.fish b/test/search.fish index f5719f0..de4c1dd 100644 --- a/test/search.fish +++ b/test/search.fish @@ -101,6 +101,6 @@ test "$TESTNAME - Match field and get multiple fields #2" "foosmith;foo" "foobarson;bar" = (fisher search --name~/^f/ --author --tags) end -test "$TESTNAME: Get full index" +test "$TESTNAME - Get full index" (fisher search) = (cat $fisher_cache/.index) end diff --git a/test/url-from-path-git.fish b/test/url-from-path-git.fish index bb66a1c..09c32b2 100644 --- a/test/url-from-path-git.fish +++ b/test/url-from-path-git.fish @@ -1,3 +1,5 @@ +set -l gist_plugin norf + function -S setup source $DIRNAME/helpers/git-ls-remote.fish end @@ -8,10 +10,21 @@ end for plugin in foo bar baz test "$TESTNAME - Get URL from repo's path in the cache ($plugin)" - "https://github.com/$plugin/$plugin" = (__fisher_url_from_path ...cache/$plugin) + "https://github.com/$plugin/$plugin" = ( + __fisher_url_from_path ...cache/$plugin + ) end end -test "$TESTNAME - Fail if path is not given" - (__fisher_url_from_path ""; printf $status) -eq 1 +test "$TESTNAME - Get @ for URLs of GitHub gists" + "$gist_plugin@https://gist.github.com/$gist_plugin" = ( + __fisher_url_from_path ...cache/$gist_plugin + ) +end + +test "$TESTNAME - Fail if path is not given" + 1 -eq ( + __fisher_url_from_path "" + printf $status + ) end diff --git a/test/validate-file.fish b/test/validate-file.fish index 2b2d998..f5bd467 100644 --- a/test/validate-file.fish +++ b/test/validate-file.fish @@ -36,3 +36,9 @@ end test "$TESTNAME - Fail phoney paths" -z (__fisher_plugin_validate /(random)/(random)) end + +for invalid_path in ".." "../" + test "$TESTNAME - Do not allow to install '$invalid_path' like paths" + -z (__fisher_plugin_validate $invalid_path) + end +end diff --git a/test/validate-url.fish b/test/validate-url.fish index 1d97e8c..ac3d94f 100644 --- a/test/validate-url.fish +++ b/test/validate-url.fish @@ -33,3 +33,9 @@ for url in omf/a omf:a "https://github.com/oh-my-fish/a" = (__fisher_plugin_validate $url) end end + +test "$TESTNAME - Add https:// to gist.github.com URLs" + https://gist.github.com/owner/1234567890 = ( + __fisher_plugin_validate gist.github.com/owner/1234567890 + ) +end