diff options
| author | typebrook <typebrook@gmail.com> | 2020-01-27 23:28:00 +0800 |
|---|---|---|
| committer | typebrook <typebrook@gmail.com> | 2020-01-27 23:28:00 +0800 |
| commit | 507e32d7f867db0c44d6f20adae6d30b15eb9584 (patch) | |
| tree | c43e012ce1cb5b81f74ca3e8e74bad05bde088ec | |
| parent | b7ce476f3eb425754427445901ff813925fffdd8 (diff) | |
updateOTP
| -rwxr-xr-x | gist | 182 |
1 files changed, 100 insertions, 82 deletions
| @@ -45,10 +45,12 @@ | |||
| 45 | # Since now a gist is a local cloned repo | 45 | # Since now a gist is a local cloned repo |
| 46 | # It is your business to do git commit and git push | 46 | # It is your business to do git commit and git push |
| 47 | # | 47 | # |
| 48 | # * configuration | ||
| 49 | # gist (config | c) [token <value>] [user <value>] [folder <value>] | ||
| 50 | # | ||
| 48 | # * show this help message | 51 | # * show this help message |
| 49 | # gist (help | h) | 52 | # gist (help | h) |
| 50 | 53 | ||
| 51 | # TODO pipe syntax fix | ||
| 52 | # TODO error handling, unit test | 54 | # TODO error handling, unit test |
| 53 | # TODO parallel branch works with json parsing on python | 55 | # TODO parallel branch works with json parsing on python |
| 54 | # TODO parallel branch works with wget and other stuff | 56 | # TODO parallel branch works with wget and other stuff |
| @@ -56,35 +58,41 @@ | |||
| 56 | 58 | ||
| 57 | # Validate settings. | 59 | # Validate settings. |
| 58 | config=~/.config/gistrc | 60 | config=~/.config/gistrc |
| 61 | set -eo pipefail | ||
| 59 | [ "$TRACE" ] && set -x | 62 | [ "$TRACE" ] && set -x |
| 60 | 63 | ||
| 61 | # TODO error handling while password is not true | 64 | # TODO error handling while password is not true |
| 65 | # TODO support access token from input or web | ||
| 62 | _auth() { | 66 | _auth() { |
| 63 | data="{\"scopes\":[\"gist\"], \"note\": \"gist-$(date -u +'%Y-%m-%dT%H:%M:%SZ')\"}" | 67 | local data="{\"scopes\":[\"gist\"], \"note\": \"gist-$(date -u +'%Y-%m-%dT%H:%M:%SZ')\"}" |
| 64 | read -p "Github username: " user | 68 | read -p "Github username: " user < /dev/tty |
| 65 | read -sp "Github password: " password | 69 | read -sp "Github password: " password < /dev/tty |
| 66 | mkdir -p ~/.config && umask 0077 && echo user=$user > $config | 70 | mkdir -p ~/.config && umask 0077 && echo user=$user > $config |
| 67 | 71 | ||
| 68 | curl https://api.github.com/authorizations \ | 72 | curl -i https://api.github.com/authorizations \ |
| 69 | --user "$user:$password" \ | 73 | --user "$user:$password" \ |
| 70 | --data "$data" > /dev/null | 74 | --data "$data" |
| 71 | 75 | ||
| 72 | read -p "2-factor code: " OTP | 76 | read -p "2-factor code: " OTP < /dev/tty |
| 73 | curl https://api.github.com/authorizations \ | 77 | curl https://api.github.com/authorizations \ |
| 74 | --user "$user:$password" -H "X-GitHub-OTP: $OTP" \ | 78 | --user "$user:$password" -H "X-GitHub-OTP: $OTP" \ |
| 75 | --data "$data" |\ | 79 | --data "$data" \ |
| 76 | sed '1 s/[^{]//g' | jq -r .token |\ | 80 | | sed '1 s/[^{]//g' | jq -r .token \ |
| 77 | sed 's/^/token=/' >> $config | 81 | | sed 's/^/token=/' >> $config |
| 78 | } | 82 | } |
| 79 | 83 | ||
| 80 | while ! source $config; do | 84 | case "$1" in |
| 81 | _auth | 85 | config | c) ;; |
| 82 | done | 86 | *) |
| 87 | while ! source $config 2> /dev/null || [[ -z "$token" ]] || [[ -z "$user" ]]; do | ||
| 88 | _auth | ||
| 89 | done;; | ||
| 90 | esac | ||
| 83 | 91 | ||
| 84 | github_api=https://api.github.com | 92 | github_api=https://api.github.com |
| 85 | auth_header="Authorization: token $token" | 93 | auth_header="Authorization: token $token" |
| 86 | 94 | ||
| 87 | [[ -z "$folder" ]] && folder=~/git/gist | 95 | [[ -z "$folder" ]] && folder=~/gist |
| 88 | mkdir -p $folder | 96 | mkdir -p $folder |
| 89 | index=$folder/index | 97 | index=$folder/index |
| 90 | starred=$folder/starred | 98 | starred=$folder/starred |
| @@ -99,96 +107,105 @@ _show_list() { | |||
| 99 | echo " gist update" | 107 | echo " gist update" |
| 100 | exit 0 | 108 | exit 0 |
| 101 | fi | 109 | fi |
| 102 | cat $1 |\ | 110 | cat $1 \ |
| 103 | while read line_num link file_url_array file_num extra description; do | 111 | | while read line_num link file_url_array file_num extra description; do |
| 104 | repo=$folder/$(echo $link | sed 's#.*/##') | 112 | local repo=$folder/$(echo $link | sed 's#.*/##') |
| 113 | local occupy=0 | ||
| 114 | |||
| 115 | # if repo is not yet cloned, show green message "Not cloned yet" | ||
| 116 | [[ ! -d $repo ]] && extra="\e[32m[Not cloned yet]\e[0m" && occupy=17 | ||
| 105 | 117 | ||
| 106 | # if repo is not yet cloned, show green message "Sync Now" | ||
| 107 | cd $repo 2>/dev/null || extra="\e[32m[Sync Now]\e[0m" | ||
| 108 | # if there are some changes in git index or working directory, show blue message "working" | 118 | # if there are some changes in git index or working directory, show blue message "working" |
| 109 | [[ -n $(git status --short) ]] 2>/dev/null && extra="\e[36m[working]\e[0m" | 119 | [[ -n $(cd $repo && git status --short) ]] 2>/dev/null && extra="\e[36m[working]\e[0m" && occupy=10 |
| 110 | # if there is a commit not yet push, show red message "ahead" | 120 | # if there is a commit not yet push, show red message "ahead" |
| 111 | [[ -n $(git cherry) ]] 2>/dev/null && extra="\e[31m[ahead]\e[0m" | 121 | [[ -n $(cd $repo && git cherry) ]] 2>/dev/null && extra="\e[31m[ahead]\e[0m" && occupy=8 |
| 112 | 122 | ||
| 113 | echo -e $line_num $link $file_num $extra $(echo $description | cut -c -60) | 123 | echo -e $line_num $link $file_num $extra $(echo $description | cut -c -$(( 60 - $occupy )) ) |
| 114 | done | 124 | done |
| 115 | } | 125 | } |
| 116 | 126 | ||
| 117 | # get the list of gists | 127 | # get the list of gists |
| 118 | # TODO support secret gist | ||
| 119 | _update() { | 128 | _update() { |
| 120 | echo "fetching from api.github.com..." | 129 | echo "fetching from api.github.com..." |
| 121 | echo | 130 | echo |
| 122 | list_file=$index | 131 | local list_file=$index |
| 123 | route="users/$user/gists" | 132 | local route="users/$user/gists" |
| 124 | mark="" | 133 | local mark="" |
| 125 | [[ "$1" =~ ^(star|s)$ ]] && list_file=$starred && route="gists/starred" && mark="s" | 134 | [[ "$1" =~ ^(star|s)$ ]] && list_file=$starred && route="gists/starred" && mark="s" |
| 126 | 135 | ||
| 127 | curl -s -H "$auth_header" $github_api/$route |\ | 136 | curl -s -H "$auth_header" $github_api/$route \ |
| 128 | _parse_response | nl -s' ' | sed -E "s/^ */$mark/" > $list_file && \ | 137 | | _parse_response | nl -s' ' | sed -E "s/^ */$mark/" > $list_file \ |
| 129 | _show_list $list_file | 138 | && _show_list $list_file \ |
| 139 | || echo Fail to update gists | ||
| 140 | |||
| 130 | if [[ $auto_sync != "false" ]]; then (_sync_repos $1 > /dev/null 2>&1 &); fi | 141 | if [[ $auto_sync != "false" ]]; then (_sync_repos $1 > /dev/null 2>&1 &); fi |
| 131 | } | 142 | } |
| 132 | 143 | ||
| 133 | # TODO check if a user create a very first gist | 144 | # TODO check if a user create a very first gist |
| 134 | _parse_response() { | 145 | _parse_response() { |
| 135 | jq '.[] | "\(.html_url) \([.files[] | .raw_url]) \(.files | keys | length) \(.comments) \(.description)"' |\ | 146 | jq '.[] | "\(.html_url) \([.files[] | .raw_url]) \(.files | keys | length) \(.comments) \(.description)"' \ |
| 136 | tac |\ | 147 | | tac \ |
| 137 | while read link file_url_array file_num comment_num description; do | 148 | | while read link file_url_array file_num comment_num description; do |
| 138 | blob_code=$(echo $file_url_array | jq -r '.[]' | sed -E 's#.*raw/(.*)/.*#\1#' | sort | cut -c -7 | paste -sd '-') | 149 | local blob_code=$(echo $file_url_array | jq -r '.[]' | sed -E 's#.*raw/(.*)/.*#\1#' | sort | cut -c -7 | paste -sd '-') |
| 139 | echo $link $blob_code $file_num $comment_num $description | tr -d '"' | 150 | echo $link $blob_code $file_num $comment_num $description | tr -d '"' |
| 140 | done | 151 | done |
| 141 | } | 152 | } |
| 142 | 153 | ||
| 143 | _sync_repos() { | 154 | _sync_repos() { |
| 144 | list_file=$index | 155 | local list_file=$index |
| 145 | [[ "$1" == "--star" ]] && list_file=$starred && route="gists/starred" | 156 | [[ "$1" =~ ^(star|s)$ ]] && list_file=$starred && route="gists/starred" |
| 146 | 157 | ||
| 147 | # clone repos which are not in the local | 158 | # clone repos which are not in the local |
| 148 | comm -13 <(find $folder -maxdepth 1 -type d | sed '1d; s#.*/##' | sort) \ | 159 | comm -13 <(find $folder -maxdepth 1 -type d | sed '1d; s#.*/##' | sort) \ |
| 149 | <(cat $list_file | cut -d' ' -f2 | sed 's#.*/##' | sort) |\ | 160 | <(cat $list_file | cut -d' ' -f2 | sed 's#.*/##' | sort) \ |
| 150 | xargs -I{} git clone git@github.com:{}.git $folder/{} | 161 | | xargs -I{} git clone git@github.com:{}.git $folder/{} |
| 151 | 162 | ||
| 152 | # pull if remote repo has different blob objects | 163 | # pull if remote repo has different blob objects |
| 153 | cat $index | cut -d' ' -f2,3 |\ | 164 | cat $index | cut -d' ' -f2,3 \ |
| 154 | while read url blob_code_remote; do | 165 | | while read url blob_code_remote; do |
| 155 | repo=$folder/$(echo $url | sed 's#.*/##') | 166 | local repo=$folder/$(echo $url | sed 's#.*/##') |
| 156 | blob_code_local=$(cd $repo && git ls-tree master | cut -d' ' -f3 | cut -c-7 | sort | paste -sd '-') | 167 | local blob_code_local=$(cd $repo && git ls-tree master | cut -d' ' -f3 | cut -c-7 | sort | paste -sd '-') |
| 157 | 168 | cd $repo \ | |
| 158 | cd $repo && \ | 169 | && [[ $blob_code_local != $blob_code_remote ]] \ |
| 159 | [[ $blob_code_local != $blob_code_remote ]] && \ | 170 | &&[[ $(git rev-parse origin/master) == $(git rev-parse master) ]] \ |
| 160 | [[ $(git rev-parse origin/master) == $(git rev-parse master) ]] && \ | 171 | && git pull |
| 161 | git pull | ||
| 162 | done | 172 | done |
| 163 | echo Everything is fine! | 173 | echo Everything is fine! |
| 164 | } | 174 | } |
| 165 | 175 | ||
| 166 | _gist_id() { | 176 | _gist_id() { |
| 167 | GIST_ID=$(cat $index $starred | sed -n "/^$1 / p" | cut -d' ' -f2 | sed -E 's#.*/##') | 177 | GIST_ID=$(cat $index $starred 2> /dev/null | sed -n "/^$1 / p" | cut -d' ' -f2 | sed -E 's#.*/##') |
| 168 | if [[ -z "$GIST_ID" ]]; then | 178 | if [[ -z "$GIST_ID" ]]; then |
| 169 | echo -e "Not a valid index: \e[31m$1\e[0m" | 179 | echo -e "Not a valid index: \e[31m$1\e[0m" |
| 170 | echo Use the index number in the first column instead: | 180 | echo Use the index number in the first column instead: |
| 171 | echo | 181 | echo |
| 172 | _show_list "$index $starred" | 182 | _show_list "$index" |
| 173 | exit 1 | 183 | exit 1 |
| 174 | fi | 184 | fi |
| 175 | } | 185 | } |
| 176 | 186 | ||
| 177 | # FIXME error handling, if repo not cloned yet | ||
| 178 | _goto_gist() { | 187 | _goto_gist() { |
| 179 | _gist_id $1 | 188 | _gist_id $1 |
| 189 | |||
| 190 | if [[ ! -d $folder/$GIST_ID ]]; then | ||
| 191 | echo 'Cloning gist as repo...' | ||
| 192 | git clone git@github.com:$GIST_ID.git $folder/$GIST_ID \ | ||
| 193 | && echo 'Repo is cloned' \ | ||
| 194 | || echo 'Failed to clone the gist' | ||
| 195 | fi | ||
| 196 | |||
| 180 | echo This gist is at $folder/$GIST_ID | 197 | echo This gist is at $folder/$GIST_ID |
| 181 | echo -e "You can run the following command to jump to this directory: \n" | 198 | echo -e "You can run the following command to jump to this directory: \n" |
| 182 | echo -e " \e[32m. gist $1\e[0m" | 199 | echo -e " \e[32m. gist $1\e[0m\n" |
| 183 | echo | ||
| 184 | cd $folder/$GIST_ID && ls && tig --all 2> /dev/null | 200 | cd $folder/$GIST_ID && ls && tig --all 2> /dev/null |
| 185 | } | 201 | } |
| 186 | 202 | ||
| 203 | # TODO only remove deleted line | ||
| 187 | _delete_gist() { | 204 | _delete_gist() { |
| 188 | for i in "$@"; do | 205 | for i in "$@"; do |
| 189 | _gist_id "$i" | 206 | _gist_id "$i" |
| 190 | curl -X DELETE -s -H "$auth_header" $github_api/gists/$GIST_ID && \ | 207 | curl -X DELETE -s -H "$auth_header" $github_api/gists/$GIST_ID \ |
| 191 | echo "$i" deleted | 208 | && echo "$i" deleted |
| 192 | done | 209 | done |
| 193 | _update | 210 | _update |
| 194 | } | 211 | } |
| @@ -196,8 +213,8 @@ _delete_gist() { | |||
| 196 | # remove repos which are not in user gists anymore | 213 | # remove repos which are not in user gists anymore |
| 197 | _clean_repos() { | 214 | _clean_repos() { |
| 198 | comm -23 <(find $folder -maxdepth 1 -type d | sed '1d; s#.*/##' | sort) \ | 215 | comm -23 <(find $folder -maxdepth 1 -type d | sed '1d; s#.*/##' | sort) \ |
| 199 | <(cat $index | cut -d' ' -f2 | sed 's#.*/##' | sort) |\ | 216 | <(cat $index $starred 2> /dev/null | cut -d' ' -f2 | sed 's#.*/##' | sort) \ |
| 200 | while read dir; do | 217 | | while read dir; do |
| 201 | mv $folder/$dir /tmp && echo move $folder/$dir to /tmp | 218 | mv $folder/$dir /tmp && echo move $folder/$dir to /tmp |
| 202 | done | 219 | done |
| 203 | } | 220 | } |
| @@ -205,11 +222,11 @@ _clean_repos() { | |||
| 205 | # TODO format with simple text | 222 | # TODO format with simple text |
| 206 | _show_detail() { | 223 | _show_detail() { |
| 207 | _gist_id $1 | 224 | _gist_id $1 |
| 208 | curl -s -H "$auth_header" $github_api/gists/$GIST_ID |\ | 225 | curl -s $github_api/gists/$GIST_ID \ |
| 209 | jq '{site: .html_url, description: .description, public: .public, API: .url, created_at: .created_at, updated_at: .updated_at, files: (.files | keys)}' | 226 | | jq '{site: .html_url, description: .description, public: .public, API: .url, created_at: .created_at, updated_at: .updated_at, files: (.files | keys)}' |
| 210 | 227 | ||
| 211 | curl -s -H "$auth_header" $github_api/gists/$GIST_ID/comments |\ | 228 | curl -s $github_api/gists/$GIST_ID/comments \ |
| 212 | jq '.[] | {user: .user.login, created_at: .created_at, updated_at: .updated_at, body: .body}' | 229 | | jq '.[] | {user: .user.login, created_at: .created_at, updated_at: .updated_at, body: .body}' |
| 213 | } | 230 | } |
| 214 | 231 | ||
| 215 | _set_gist() { | 232 | _set_gist() { |
| @@ -223,6 +240,7 @@ _set_gist() { | |||
| 223 | esac | 240 | esac |
| 224 | done | 241 | done |
| 225 | if [[ "$1" == '--' ]]; then shift; fi | 242 | if [[ "$1" == '--' ]]; then shift; fi |
| 243 | # TODO could be simplified? | ||
| 226 | files="$@" && echo $files | xargs ls > /dev/null || exit 1 | 244 | files="$@" && echo $files | xargs ls > /dev/null || exit 1 |
| 227 | } | 245 | } |
| 228 | 246 | ||
| @@ -231,32 +249,34 @@ _new_file() { | |||
| 231 | tmp_file=$(mktemp) | 249 | tmp_file=$(mktemp) |
| 232 | cat > $tmp_file | 250 | cat > $tmp_file |
| 233 | echo -e '\n' > /dev/tty | 251 | echo -e '\n' > /dev/tty |
| 234 | [[ -z "$1" ]] && echo -n 'Type file name: ' > /dev/tty && read filename | 252 | [[ -z "$1" ]] && read -p 'Type file name: ' filename < /dev/tty |
| 235 | mv $tmp_file /tmp/$filename | 253 | mv $tmp_file /tmp/$filename |
| 236 | echo /tmp/$filename | 254 | echo /tmp/$filename |
| 237 | } | 255 | } |
| 238 | 256 | ||
| 239 | # create a new gist with files | 257 | # create a new gist with files |
| 240 | # FIXME error handling if gist is not created, file doesn't exist | 258 | # TODO support secret gist |
| 241 | _create_gist() { | 259 | _create_gist() { |
| 242 | _set_gist "$@" | 260 | _set_gist "$@" |
| 243 | [[ -z "$files" ]] && files=$(_new_file $filename) | 261 | [[ -z "$files" ]] && files=$(_new_file $filename) |
| 244 | [[ -z "$description" ]] && echo -n 'Type description: ' && read description | 262 | [[ -z "$description" ]] && read -p 'Type description: ' description < /dev/tty |
| 245 | 263 | ||
| 246 | for file in $files; do | 264 | for file in $files; do |
| 247 | FILE=$(basename $file) | 265 | FILE=$(basename $file) |
| 248 | jq --arg FILE "$FILE" '. as $content | { ($FILE): {content: $content} }' -Rs $file | 266 | jq --arg FILE "$FILE" '. as $content | { ($FILE): {content: $content} }' -Rs $file |
| 249 | done |\ | 267 | done \ |
| 250 | jq --slurp --arg DESC "$description" '{ | 268 | | jq --slurp --arg DESC "$description" '{ |
| 251 | public: true, | 269 | public: true, |
| 252 | files: add, | 270 | files: add, |
| 253 | description: ($DESC) | 271 | description: ($DESC) |
| 254 | }' |\ | 272 | }' \ |
| 255 | curl -H "$auth_header" --data @- $github_api/gists |\ | 273 | | curl -s -H "$auth_header" --data @- $github_api/gists \ |
| 256 | sed '1 s/^/[/; $ s/$/]/' |\ | 274 | | sed '1 s/^/[/; $ s/$/]/' \ |
| 257 | _parse_response |\ | 275 | | _parse_response \ |
| 258 | sed -E "s/^/$(( $(wc -l $index | cut -d' ' -f1) + 1 )) /" >> $index && \ | 276 | | sed -E "s/^/$(( $(wc -l $index | cut -d' ' -f1) + 1 )) /" >> $index \ |
| 259 | echo -e '\nGist created' | 277 | && echo -e '\nGist created' \ |
| 278 | || echo 'Fail to create gist' | ||
| 279 | |||
| 260 | _show_list $index | tail -1 | 280 | _show_list $index | tail -1 |
| 261 | } | 281 | } |
| 262 | 282 | ||
| @@ -265,10 +285,10 @@ _edit_gist() { | |||
| 265 | _gist_id $1 | 285 | _gist_id $1 |
| 266 | 286 | ||
| 267 | echo -n 'Type new description: ' | 287 | echo -n 'Type new description: ' |
| 268 | read DESC | 288 | read DESC < /dev/tty |
| 269 | jq -n --arg DESC "$DESC" '{ description: ($DESC) }' |\ | 289 | jq -n --arg DESC "$DESC" '{ description: ($DESC) }' \ |
| 270 | curl -X PATCH -H "$auth_header" --data @- $github_api/gists/$GIST_ID > /dev/null && \ | 290 | | curl -X PATCH -H "$auth_header" --data @- $github_api/gists/$GIST_ID > /dev/null \ |
| 271 | _update | 291 | && _update |
| 272 | } | 292 | } |
| 273 | 293 | ||
| 274 | _help_message() { | 294 | _help_message() { |
| @@ -277,20 +297,19 @@ _help_message() { | |||
| 277 | 297 | ||
| 278 | _cases() { | 298 | _cases() { |
| 279 | if [[ $1 == 'token' ]]; then | 299 | if [[ $1 == 'token' ]]; then |
| 280 | [[ ${#2} -eq 40 ]] && echo token=$2 ||\ | 300 | [[ ${#2} -eq 40 ]] && echo $1=$2 \ |
| 281 | echo Invalid token format, it is not 40 chars '\n' > /dev/tty | 301 | || echo -e Invalid token format, it is not 40 chars '\n' > /dev/tty |
| 282 | elif [[ $1 == 'auto_sync' ]]; then | 302 | elif [[ $1 == 'auto_sync' ]]; then |
| 283 | [[ $2 == 'false' ]] && echo $1=$2 ||\ | 303 | [[ $2 == 'false' ]] && echo $1=$2 \ |
| 284 | echo $1=true | 304 | || echo $1=true |
| 285 | elif [[ $1 == 'folder' ]]; then | 305 | elif [[ $1 == 'folder' ]]; then |
| 286 | [[ -n "$2" ]] && echo $1=$2 ||\ | 306 | [[ -n "$2" ]] && echo $1=$2 \ |
| 287 | echo $1=~/gist | 307 | || echo $1=~/gist |
| 288 | elif [[ $1 == 'user' ]]; then | 308 | elif [[ $1 == 'user' ]]; then |
| 289 | echo $1=$2 | 309 | echo $1=$2 |
| 290 | fi | 310 | fi |
| 291 | } | 311 | } |
| 292 | 312 | ||
| 293 | # TODO consider the case that $2 is empty -> remove original setting | ||
| 294 | _configure() { | 313 | _configure() { |
| 295 | [[ -z "$@" ]] && (vim $config) && exit 0 | 314 | [[ -z "$@" ]] && (vim $config) && exit 0 |
| 296 | target=$(_cases "$@") | 315 | target=$(_cases "$@") |
| @@ -299,7 +318,6 @@ _configure() { | |||
| 299 | cat $config | 318 | cat $config |
| 300 | } | 319 | } |
| 301 | 320 | ||
| 302 | |||
| 303 | case "$1" in | 321 | case "$1" in |
| 304 | "") | 322 | "") |
| 305 | _show_list $index ;; | 323 | _show_list $index ;; |