mirror of
https://github.com/jorgebucaran/fisher
synced 2024-08-17 12:31:05 +02:00
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:
parent
56a8d98681
commit
949fc0598e
29 changed files with 247 additions and 42 deletions
13
CHANGELOG.md
13
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
|
||||
|
||||
|
|
10
README.md
10
README.md
|
@ -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/).
|
||||
|
||||
|
|
|
@ -8,6 +8,10 @@ function __fisher_file
|
|||
}
|
||||
}
|
||||
|
||||
/@http/ {
|
||||
gsub("@.*$", "", $1)
|
||||
}
|
||||
|
||||
!/^[ \t]*(#.*)*$/ {
|
||||
gsub("^[|*>]|#.*", "")
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
27
functions/__fisher_gist_to_name.fish
Normal file
27
functions/__fisher_gist_to_name.fish
Normal 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
|
|
@ -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
|
||||
|
|
12
functions/__fisher_plugin_can_enable.fish
Normal file
12
functions/__fisher_plugin_can_enable.fish
Normal 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
|
|
@ -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" \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
46
test/fixtures/gist/foo.json
vendored
Normal 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
|
||||
}
|
||||
}
|
6
test/fixtures/manifest/fishfile
vendored
6
test/fixtures/manifest/fishfile
vendored
|
@ -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
|
||||
|
|
1
test/fixtures/manifest/fishfile-no-comments
vendored
1
test/fixtures/manifest/fishfile-no-comments
vendored
|
@ -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
|
||||
|
|
1
test/fixtures/manifest/fishfile-parsed
vendored
1
test/fixtures/manifest/fishfile-parsed
vendored
|
@ -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
22
test/gist-to-name.fish
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
24
test/plugin-can-enable.fish
Normal file
24
test/plugin-can-enable.fish
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue