From fd110204d35f4d66a0deb9dadf74d81603f59a23 Mon Sep 17 00:00:00 2001 From: typebrook Date: Sun, 9 Feb 2020 20:43:56 +0800 Subject: update --- gist | 305 +++++++++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 186 insertions(+), 119 deletions(-) diff --git a/gist b/gist index 9807a18..07962cd 100755 --- a/gist +++ b/gist @@ -8,45 +8,139 @@ # Description: Manage your gists with git and Github API v3 # Usage: gist [command] [] # -# [star | s] list your gists with format below, star for your starred gists: +# [star | s] List your gists with format below, star for your starred gists: # [index_of_gist] [url] [file_num] [comment_num] [short description] -# update, u [star | s] update the local list of your gists, star for your starred gists -# [--no-action] show the path of local gist repo and do custom actions +# fetch, f [star | s] Update the local list of your gists, star for your starred gists +# [--no-action] Show the path of local gist repo and do custom actions # new, n [-d | --desc ] [-p] ... create a new gist with files # new, n [-d | --desc ] [-p] [-f | --file ] create a new gist from STDIN -# detail, d show the detail of a gist -# edit, e edit a gist's description -# delete, D ... delete a gist -# clean, C clean removed gists in local -# config, c [token | user | folder | auto_sync | EDITOR | action [value] ] do configuration -# user, U get gists from a given Github user -# grep, g grep gists by a given pattern -# github, G Import this gist as a new Github repo -# push, p push changes by git -# help, h show this help message +# detail, d Show the detail of a gist +# edit, e Edit a gist's description +# delete, D ... Delete a gist +# clean, C Clean removed gists in local +# config, c [token | user | folder | auto_sync | EDITOR | action [value] ] Do configuration +# user, U Get gists from a given Github user +# grep, g Grep gists by a given pattern +# push, p Push changes by git (well, better to make commit by youself) +# github, G Import selected gist as a new Github repo +# help, h Show this help message +# version Get the tool version +# update Update Bash-Snippet Tools # # Example: # gist (Show your gists) # gist update (update the list of gists from github.com) # gist 3 (show the repo path of your 3rd gist, and do custom actions) # gist 3 --no-action (show the repo path of your 3rd gist, and do not perform actions) -# gist new foo --desc bar (create a new gist with file and description) +# gist new --desc bar foo (create a new gist with file and description) # # Since now a gist is a local cloned repo # It is your business to do git commit and git push # TODO test on bats, mac and remote machine -# TODO completion -# FIXME mac: tac=tail -r +currentVersion="1.23.0" +configuredClient="" + +GITHUB_API=https://api.github.com +CONFIG=~/.config/gist.conf; mkdir -p ~/.config + +folder=~/gist && mkdir -p $folder +action="${EDITOR:-vi} ." +auto_sync=true # automatically clone the gist repo # Shell configuration set -o pipefail [ "$TRACE" ] && set -x +[ $(uname) == 'Darwin' ] && alias tac='tail -r' trap 'rm -f "$http_data" "$tmp_file"' EXIT -GITHUB_API=https://api.github.com -CONFIG=~/.config/gist.conf; mkdir -p ~/.config -configuredClient="" +# This function determines which http get tool the system has installed and returns an error if there isnt one +getConfiguredClient() { + if command -v curl &>/dev/null; then + configuredClient="curl" + elif command -v wget &>/dev/null; then + configuredClient="wget" + elif command -v http &>/dev/null; then + configuredClient="httpie" + else + echo "Error: This tool requires either curl, wget, or httpie to be installed." >&2 + return 1 + fi +} + +# Allows to call the users configured client without if statements everywhere +http_method() { + local METHOD=$1; shift + case "$configuredClient" in + curl) [[ -n $token ]] && local extra="--header" local header="Authorization: token $token" + [[ $METHOD =~ (POST|PATCH) ]] && extra2="--data" + curl -X $METHOD -A curl -s $extra "$header" $extra2 @$http_data "$@" ;; + wget) [[ -n $token ]] && local extra="--header" local header="Authorization: token $token" + [[ $METHOD =~ (POST|PATCH) ]] && extra2='--body-file' + wget --method=$METHOD -qO- $extra "$header" $extra2 $http_data "$@" ;; + httpie) [[ -n $token ]] && header="Authorization:token $token" + [[ $METHOD =~ (POST|PATCH) ]] && extra2="@$http_data" + http -b $METHOD "$@" "$header" $extra2 ;; + esac +} + +httpGet(){ + http_method GET "$@" +} + +# parse JSON from STDIN with string of commands +_process_json() { + PYTHONIOENCODING=utf-8 \ + python -c "from __future__ import print_function; import sys, json; $1" + return "$?" +} + +checkInternet() { + httpGet github.com 2>&1 || { echo "Error: no active internet connection" >&2; return 1; } # query github with a get request +} + +update() { + # Author: Alexander Epstein https://github.com/alexanderepstein + # Update utility version 2.2.0 + # To test the tool enter in the defualt values that are in the examples for each variable + repositoryName="Bash-Snippets" #Name of repostiory to be updated ex. Sandman-Lite + githubUserName="alexanderepstein" #username that hosts the repostiory ex. alexanderepstein + nameOfInstallFile="install.sh" # change this if the installer file has a different name be sure to include file extension if there is one + latestVersion=$(httpGet https://api.github.com/repos/$githubUserName/$repositoryName/tags | grep -Eo '"name":.*?[^\\]",'| head -1 | grep -Eo "[0-9.]+" ) #always grabs the tag without the v option + + if [[ $currentVersion == "" || $repositoryName == "" || $githubUserName == "" || $nameOfInstallFile == "" ]]; then + echo "Error: update utility has not been configured correctly." >&2 + exit 1 + elif [[ $latestVersion == "" ]]; then + echo "Error: no active internet connection" >&2 + exit 1 + else + if [[ "$latestVersion" != "$currentVersion" ]]; then + echo "Version $latestVersion available" + echo -n "Do you wish to update $repositoryName [Y/n]: " + read -r answer + if [[ "$answer" == [Yy] ]]; then + cd ~ || { echo 'Update Failed'; exit 1; } + if [[ -d ~/$repositoryName ]]; then rm -r -f $repositoryName || { echo "Permissions Error: try running the update as sudo"; exit 1; } ; fi + echo -n "Downloading latest version of: $repositoryName." + git clone -q "https://github.com/$githubUserName/$repositoryName" && touch .BSnippetsHiddenFile || { echo "Failure!"; exit 1; } & + while [ ! -f .BSnippetsHiddenFile ]; do { echo -n "."; sleep 2; };done + rm -f .BSnippetsHiddenFile + echo "Success!" + cd $repositoryName || { echo 'Update Failed'; exit 1; } + git checkout "v$latestVersion" 2> /dev/null || git checkout "$latestVersion" 2> /dev/null || echo "Couldn't git checkout to stable release, updating to latest commit." + chmod a+x install.sh #this might be necessary in your case but wasnt in mine. + ./$nameOfInstallFile "update" || exit 1 + cd .. + rm -r -f $repositoryName || { echo "Permissions Error: update succesfull but cannot delete temp files located at ~/$repositoryName delete this directory with sudo"; exit 1; } + else + exit 1 + fi + else + echo "$repositoryName is already the latest version" + fi + fi +} # handle configuration cases _configure() { @@ -66,7 +160,7 @@ _configure() { fi umask 0077 && touch $CONFIG - sed -i'' -e "/^$1=/ d" $CONFIG && [[ "$target" =~ [^=]$ ]] && echo $target >> $CONFIG + sed -i'' -e "/^$1=/ d" $CONFIG && [[ -n "$target" ]] && echo $target >> $CONFIG cat $CONFIG } @@ -97,8 +191,9 @@ _ask_token() { # check configuration is fine with user setting _validate_config(){ - source $CONFIG 2> /dev/null || true - if [[ ! -e $CONFIG || -z $user ]]; then + source $CONFIG 2> /dev/null + [[ $1 =~ ^(c|config|h|help|u|user|update|version) ]] && return 0 + if [[ -z $user ]]; then echo 'Hi fellow! To access your gists, I need your Github username' echo "Also a personal token with scope which allows "gist"!'" echo @@ -108,7 +203,7 @@ _validate_config(){ echo 'To create/edit/delete a gist, a token is needed' return 1 fi - elif [[ -z $token && $1 =~ ^(u|update)$ && $2 =~ ^(s|star) ]]; then + elif [[ -z $token && $1 =~ ^(f|fetch)$ && $2 =~ ^(s|star) ]]; then if ! (_ask_token); then echo 'To get user starred gists, a token is needed' return 1 @@ -121,46 +216,27 @@ _apply_config() { _validate_config "$@" || return 1 AUTH_HEADER="Authorization: token $token" - [[ -z "$action" ]] && action="${EDITOR:-vi} ." - [[ -z "$folder" ]] && folder=~/gist && mkdir -p $folder INDEX=$folder/index; [[ -e $INDEX ]] || touch $INDEX } -# This function determines which http get tool the system has installed and returns an error if there isnt one -getConfiguredClient() { - if command -v curl &>/dev/null; then - configuredClient="curl" - elif command -v wget &>/dev/null; then - configuredClient="wget" - elif command -v http &>/dev/null; then - configuredClient="httpie" - elif command -v fetch &>/dev/null; then - configuredClient="fetch" - else - echo "Error: This tool requires either curl, wget, httpie or fetch to be installed." >&2 - return 1 - fi -} - -# Allows to call the users configured client without if statements everywhere -http_method() { - local METHOD=$1; shift - case "$configuredClient" in - curl) [[ -n $token ]] && local extra="--header" local header="Authorization: token $token" - [[ $METHOD =~ (POST|PATCH) ]] && extra2="--data" - curl -X $METHOD -A curl -s $extra "$header" $extra2 @$http_data "$@" ;; - wget) [[ -n $token ]] && local extra="--header" local header="Authorization: token $token" - [[ $METHOD =~ (POST|PATCH) ]] && extra2='--body-file' - wget --method=$METHOD -qO- $extra "$header" $extra2 $http_data "$@" ;; - httpie) [[ -n $token ]] && header="Authorization:token $token" - [[ $METHOD =~ (POST|PATCH) ]] && extra2="@$http_data" - http -b $METHOD "$@" "$header" $extra2 ;; - # TODO add other methods - fetch) fetch -q "$@" ;; - esac +_check_repo_status() { + if [[ ! -d $1 ]]; then + if $auto_sync; then + echo "\e[32m[cloning]\e[0m"; + else + echo "\e[32m[Not cloned yet]\e[0m"; + fi + else + cd $1 + if [[ -n $(git status --short) ]] &>/dev/null; then + echo "\e[36m[working]\e[0m" + else + [[ $(_blob_code $1) != $2 ]] 2>/dev/null && echo "\e[31m[outdated]\e[0m" + [[ -n $(git cherry) ]] 2>/dev/null && echo "\e[31m[ahead]\e[0m" + fi + fi } -# TODO Split into 2 funcs for filter and display # Show the list of gist, but not updated time _show_list() { if [[ ! -e $INDEX ]]; then @@ -168,28 +244,22 @@ _show_list() { echo ' gist update' return 0 fi - [[ -z $1 ]] && local filter='/^ *s/ d; /^$/ d' - [[ $1 == "s" ]] && local filter='/^ *[^ s]/ d; /^$/ d' + local filter='/^ *s/ d; /^$/ d' + [[ $mark == "s" ]] && filter='/^ *[^ s]/ d; /^$/ d' - while read index link blob_code file_num comment_num author description; do + sed -e "$filter" $INDEX \ + | while read index link blob_code file_num comment_num author description; do [[ $1 == "s" ]] && local name=$author local repo=$folder/$(echo $link | sed 's#.*/##') - local occupy=0 - local extra="$file_num $comment_num" + local extra=$(_check_repo_status $repo $blob_code) + [[ -z $extra ]] && extra="$file_num $comment_num" - # if repo is not yet cloned, show green message "Not cloned yet" - [[ ! -d $repo ]] && extra="\e[32m[Not cloned yet]\e[0m" && occupy=16 - # if there are some changes in git index or working directory, show blue message "working" - [[ -n $(cd $repo && git status --short) ]] 2>/dev/null && extra="\e[36m[working]\e[0m" && occupy=9 - # if there files are different, show red message "outdated" - [[ $(_blob_code $repo) != $blob_code ]] 2>/dev/null && extra="\e[31m[outdated]\e[0m" && occupy=10 - # if there is a commit not yet push, show red message "ahead" - [[ -n $(cd $repo && git cherry) ]] 2>/dev/null && extra="\e[31m[ahead]\e[0m" && occupy=7 + echo -e "$(printf "% 3s" $index)" $link $name $extra $description \ + | cut -c -$(tput cols) + done - echo -e "$(printf "% 3s" $index)" $link $name $extra $(echo $description | cut -c -$(( 60 -$occupy -1 )) ) - done < $INDEX \ - | sed "$filter" - echo -e '\nrun "gist help" or "gist h" for more details' > /dev/tty + $hint && echo -e '\nrun "gist fetch" to update gists or "gist help" for more details' > /dev/tty \ + || return 0 } # TODO support filenames, file contents @@ -197,7 +267,6 @@ _grep_content() { _show_list | grep -i $1 } -# TODO support filenames, file contents _import_to_github() { _gist_id $1 echo put the folowing URL into webpage: @@ -211,15 +280,8 @@ _push_to_remote() { && git commit --allow-empty-message -m '' && git push origin master } -# parse JSON from STDIN with string of commands -AccessJsonElement() { - PYTHONIOENCODING=utf-8 \ - python -c "from __future__ import print_function; import sys, json; $1" - return "$?" -} - -_handle_gists() { - echo ' +_parse_gists() { + _process_json ' raw = json.load(sys.stdin) for gist in raw: print(gist["html_url"], end=" ") @@ -233,63 +295,58 @@ for gist in raw: } # TODO check if a user has no gist -# FIXME replace space with sed # parse response from gists require _parse_response() { - tee a \ - | AccessJsonElement "$(_handle_gists)" \ - | tee b \ - | tail -r | sed -e 's/, /,/g' | nl -s' ' \ - | tee ba \ + _parse_gists \ + | tac | sed -e 's/, /,/g' | nl -s' ' \ | while read index link file_url_array public file_num comment_num author description; do local blob_code=$(echo $file_url_array | tr ',' '\n' | sed -E -e 's#.*raw/(.*)/.*#\1#' | sort | cut -c -7 | paste -s -d '-' -) [[ $public == 'False' ]] && local mark=p [[ -n $1 ]] && local index=$1 echo $mark$index $link $blob_code $file_num $comment_num $author $description | tr -d '"' - done | tee c + done } # TODO pagnation for more than 30 gists # TODO add files and date of a gist # get latest list of gists from Github API -_update() { +_fetch_gists() { echo "fetching $user's gists from $GITHUB_API..." echo local route="users/$user/gists" - local mark="" local filter='/^[^s]/ d; /^$/ d' if [[ "$1" =~ ^(star|s)$ ]];then route="gists/starred" - mark="s" + local mark="s" filter='/^[s]/ d; /^$/ d' fi - result=$(http_method GET $GITHUB_API/$route | _parse_response) + result=$(http_method GET $GITHUB_API/$route | mark=$mark _parse_response) [[ -z $result ]] && echo Failed to update gists && return 1 sed -i'' -e "$filter" $INDEX && echo "$result" >> $INDEX - _show_list $mark + mark=$mark hint=true _show_list - if [[ $auto_sync != 'false' ]]; then (_sync_repos $1 > /dev/null 2>&1 &); fi + $auto_sync && (_sync_repos $1 > /dev/null 2>&1 &) } _query_user() { local route="users/$1/gists" result=$(http_method GET $GITHUB_API/$route | _parse_response) - [[ -z $result ]] && echo Failed to update gists && return 1 + [[ -z $result ]] && echo "Failed to query $1's gists" && return 1 echo "$result" \ | while read index link blob_code file_num extra description; do - echo $link $file_num $extra $(echo $description | cut -c -70 ) + echo $link $file_num $extra $description | cut -c -$(tput cols) done } _blob_code() { - cd $1 \ - && git ls-tree master | cut -d' ' -f3 | cut -c-7 | sort | paste -sd '-' + cd $1 && git ls-tree master | cut -d' ' -f3 | cut -c-7 | sort | paste -sd '-' } # update local git repos +# TODO support HTTPS protocol _sync_repos() { # clone repos which are not in the local comm -13 <(find $folder -maxdepth 1 -type d | sed -e '1d; s#.*/##' | sort) \ @@ -363,8 +420,8 @@ _clean_repos() { } # parse JSON from gist detail -_handle_gist() { - echo ' +_parse_gist() { + _process_json ' raw = json.load(sys.stdin) print("site:", raw["html_url"]) print("description:", raw["description"]) @@ -375,12 +432,12 @@ print("updated_at:", raw["updated_at"]) print("files:") for file in raw["files"].keys(): print(" ", file) - ' + ' } # equal to jq '.[] | {user: .user.login, created_at: .created_at, updated_at: .updated_at, body: .body}' -_handle_comment() { - echo ' +_parse_comment() { + _process_json ' raw = json.load(sys.stdin); for comment in raw: print() @@ -388,16 +445,16 @@ for comment in raw: print("|", "created_at:", comment["created_at"]) print("|", "updated_at:", comment["updated_at"]) print("|", comment["body"]) - ' + ' } _show_detail() { _gist_id $1 http_method GET $GITHUB_API/gists/$GIST_ID \ - | AccessJsonElement "$(_handle_gist)" + | _parse_gist http_method GET $GITHUB_API/gists/$GIST_ID/comments \ - | AccessJsonElement "$(_handle_comment)" + | _parse_comment } # set filename/description/permission for a new gist @@ -433,7 +490,7 @@ _new_file() { } _gist_body(){ - echo " + _process_json " import os.path files_json = {} files = sys.stdin.readline().split() @@ -455,7 +512,7 @@ _create_gist() { http_data=$(mktemp) echo -e "$files\n$description" \ - | AccessJsonElement "$(_gist_body)" > $http_data \ + | _gist_body > $http_data \ && http_method POST $GITHUB_API/gists \ | sed -e '1 s/^/[/; $ s/$/]/' \ | _parse_response $(( $(sed -e '/^s/ d' $INDEX | wc -l) +1 )) \ @@ -481,7 +538,7 @@ _edit_gist() { http_data=$(mktemp) echo { \"description\": \"$(echo $DESC | sed -e 's/"/\\"/g')\" } > $http_data http_method PATCH $http_data $GITHUB_API/gists/$GIST_ID > /dev/null \ - && _update + && _fetch_gists } usage() { @@ -489,15 +546,17 @@ usage() { } _apply_config "$@" || exit 1 -getConfiguredClient -if [[ $init ]]; then _update; exit 0; fi +getConfiguredClient || exit 1 +if [[ $init ]]; then _fetch_gists; exit 0; fi case "$1" in "") - _show_list ;; + [[ -z "$hint" ]] && hint=true + hint=$hint _show_list ;; star | s) - _show_list s ;; - update | u) - _update "$2" ;; + [[ -z "$hint" ]] && hint=true + hint=$hint mark=s _show_list ;; + fetch | f) + _fetch_gists "$2" ;; new | n) shift _create_gist "$@" ;; @@ -528,6 +587,14 @@ case "$1" in push | p) shift _push_to_remote "$1" ;; + version) + echo "Version $currentVersion" + exit 0 ;; + update) + checkInternet || exit 1 + update + exit 0 + ;; help | h) usage ;; *) -- cgit v1.2.3-70-g09d2