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