Ahoy! an early v0.7.0 is here!

+ 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.
This commit is contained in:
Jorge Bucaran 2016-02-10 08:21:57 +09:00
parent b36ad1f179
commit 4cfd037cc0
29 changed files with 247 additions and 42 deletions

View File

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

View File

@ -1,19 +1,23 @@
<a name="fisherman"></a>
<p align="center">
<br>
<a href="http://fisherman.sh">
<img
alt="Fisherman"
width=720px
src="https://cloud.githubusercontent.com/assets/8317250/10865127/daa0e138-8044-11e5-91f9-f72228974552.png">
width=660px
src="https://rawgit.com/fisherman/logo/master/fisherman-black-white.svg">
</a>
<br>
<br>
<br>
</p>
[![Build Status][travis-badge]][travis-link]
[![Fisherman Version][version-badge]][version-link]
[![Wharf][wharf-badge]][wharf-link]
# Fisherman
<hr>
**Fisherman** is a blazing [fast](#performance), modern plugin manager for [Fish](http://fishshell.com/).

View File

@ -8,6 +8,10 @@ function __fisher_file
}
}
/@http/ {
gsub("@.*$", "", $1)
}
!/^[ \t]*(#.*)*$/ {
gsub("^[|*>]|#.*", "")

View File

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

View File

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

View File

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

View File

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

View File

@ -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" \

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

46
test/fixtures/gist/foo.json vendored Normal file
View File

@ -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
}
}

View File

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

View File

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

View File

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

22
test/gist-to-name.fish Normal file
View File

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

View File

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

View File

@ -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"

View File

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

View File

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

View File

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

View File

@ -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 <plugin>@<URL> 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

View File

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

View File

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