diff options
author | typebrook <typebrook@gmail.com> | 2020-02-01 12:29:59 +0800 |
---|---|---|
committer | typebrook <typebrook@gmail.com> | 2020-02-01 12:29:59 +0800 |
commit | cf5e6f9dc5196994aed64dfcba23f47419fc1888 (patch) | |
tree | 8b2e031aede91672eb80790b58e731795a7a811d | |
parent | a2f1012084591cdf1872f02828a922b239d4dd97 (diff) |
update
-rwxr-xr-x | gist | 511 |
1 files changed, 293 insertions, 218 deletions
@@ -15,13 +15,9 @@ | |||
15 | # | 15 | # |
16 | # * list your gists with format: [number] [url] [file_num] [comment_num] [short description] | 16 | # * list your gists with format: [number] [url] [file_num] [comment_num] [short description] |
17 | # gist [star | s] | 17 | # gist [star | s] |
18 | # | ||
19 | # * clone gist repos which are not in local | ||
20 | # * pull master branch if a local repo is behind its remote | ||
21 | # gist (sync | S) | ||
22 | # | 18 | # |
23 | # * Go to local gist repo | 19 | # * show the path of local gist repo and files |
24 | # . gist <number_of_gist_in_list> | 20 | # gist <index_of_gist> |
25 | # | 21 | # |
26 | # * create a new gist with files | 22 | # * create a new gist with files |
27 | # gist (new | n) [-d | --desc "<gist-description>"] <files>... | 23 | # gist (new | n) [-d | --desc "<gist-description>"] <files>... |
@@ -30,13 +26,13 @@ | |||
30 | # gist (new | n) [-d | --desc "<gist-description>"] [-f | --file <file>] < <file-with-content> | 26 | # gist (new | n) [-d | --desc "<gist-description>"] [-f | --file <file>] < <file-with-content> |
31 | # | 27 | # |
32 | # * show the detail of a gist | 28 | # * show the detail of a gist |
33 | # gist (detail | d) <gist_index> | 29 | # gist (detail | d) <index_of_gist> |
34 | # | 30 | # |
35 | # * edit a gist description | 31 | # * edit a gist description |
36 | # gist (edit | e) <gist_index> | 32 | # gist (edit | e) <index_of_gist> |
37 | # | 33 | # |
38 | # * delete a gist | 34 | # * delete a gist |
39 | # gist (delete | D) <gist_index>... | 35 | # gist (delete | D) <index_of_gist>... |
40 | # | 36 | # |
41 | # * clean removed gists in local | 37 | # * clean removed gists in local |
42 | # gist (clean | C) | 38 | # gist (clean | C) |
@@ -46,109 +42,172 @@ | |||
46 | # It is your business to do git commit and git push | 42 | # It is your business to do git commit and git push |
47 | # | 43 | # |
48 | # * configuration | 44 | # * configuration |
49 | # gist (config | c) [token <value>] [user <value>] [folder <value>] [auto-sync false] | 45 | # gist (config | c) [token|user|folder|auto-sync|EDITOR|action [value]] |
50 | # | 46 | # |
51 | # * show this help message | 47 | # * show this help message |
52 | # gist (help | h) | 48 | # gist (help | h) |
53 | 49 | ||
54 | # TODO error handling, unit test | ||
55 | # TODO parallel branch works with wget and other stuff | 50 | # TODO parallel branch works with wget and other stuff |
56 | # TODO completion | 51 | # TODO new command "user" to fetch other user's gists |
57 | # TODO grep mode for description, file content | 52 | # TODO grep mode for description, file content |
53 | # TODO push github.com (may need new token) | ||
54 | # TODO description for current directory | ||
55 | # TODO error handling, unit test | ||
56 | # TODO test on mac and remote machine | ||
57 | # TODO completion | ||
58 | 58 | ||
59 | # Validate settings. | 59 | # Shell configuration |
60 | config=~/.config/gistrc | ||
61 | set -eo pipefail | ||
62 | [ "$TRACE" ] && set -x | 60 | [ "$TRACE" ] && set -x |
63 | 61 | ||
64 | ## parse JSON from STDIN with string of commands | 62 | GITHUB_API=https://api.github.com |
65 | AccessJsonElement() { | 63 | CONFIG=~/.config/gist.conf; mkdir -p ~/.config |
66 | PYTHONIOENCODING=utf-8 python -c "from __future__ import print_function; import sys, json; raw = json.load(sys.stdin); $1" | 64 | configuredClient="" |
67 | return "$?" | 65 | |
66 | # handle configuration cases | ||
67 | _configure() { | ||
68 | local target="" | ||
69 | [[ -z "$@" ]] && (${EDITOR:-vi} $CONFIG) && return 0 | ||
70 | if [[ $1 == 'token' ]]; then | ||
71 | [[ ${#2} -eq 40 ]] && target=$1=$2 \ | ||
72 | || echo -e Invalid token format, it is not 40 chars '\n' > /dev/tty | ||
73 | elif [[ $1 == 'auto_sync' ]]; then | ||
74 | [[ $2 == 'false' ]] && target=$1=$2 \ | ||
75 | || target=$1=true | ||
76 | elif [[ $1 == 'folder' ]]; then | ||
77 | [[ -n "$2" ]] && target=$1=$2 \ | ||
78 | || target=$1=~/gist | ||
79 | elif [[ $1 == 'user' ]]; then | ||
80 | target=$1=$2 | ||
81 | fi | ||
82 | |||
83 | umask 0077 && touch $CONFIG | ||
84 | [[ "$target" =~ [^=]$ ]] && sed -i "/^$1=/ d" $CONFIG && echo $target >> $CONFIG | ||
85 | cat $CONFIG | ||
68 | } | 86 | } |
69 | 87 | ||
70 | _auth() { | 88 | # prompt for username |
71 | echo 'Hi fellow! To access your gists, I need your Github username and a personal token with scope which allows "gist"!' | 89 | _ask_username() { |
90 | while [[ ! $user =~ ^[[:alnum:]]+$ ]]; do | ||
91 | [[ -n $user ]] && echo "Not a valid username" | ||
72 | read -p "Github username: " user < /dev/tty | 92 | read -p "Github username: " user < /dev/tty |
73 | if [[ $user =~ ^[[:alnum:]]+$ ]]; then | 93 | done |
74 | mkdir -p ~/.config | 94 | _configure user $user |
75 | umask 0077 | 95 | } |
76 | echo user=$user > $config | ||
77 | else | ||
78 | echo "Not a valid username" | ||
79 | return 0 | ||
80 | fi | ||
81 | |||
82 | echo -n "Create a new token from web browser? [Y/n] " | ||
83 | read answer < /dev/tty | ||
84 | if [[ ! $answer =~ ^(N|n|No|NO|no)$ ]]; then | ||
85 | python -mwebbrowser https://github.com/settings/tokens/new\?scopes\=gist; | ||
86 | fi | ||
87 | 96 | ||
88 | read -p "Paste your token here: " new_token < /dev/tty | 97 | # prompt for toekn |
89 | [[ $new_token =~ ^[[:alnum:]]{40}$ ]] \ | 98 | # TODO token check, ref: https://developer.github.com/v3/apps/oauth_applications/#check-a-token |
90 | && echo token=$new_token >> $config \ | 99 | _ask_token() { |
91 | || echo "Not a valid token" | 100 | echo -n "Create a new token from web browser? [Y/n] " |
101 | read answer < /dev/tty | ||
102 | if [[ ! $answer =~ ^(N|n|No|NO|no)$ ]]; then | ||
103 | python -mwebbrowser https://github.com/settings/tokens/new\?scopes\=gist | ||
104 | fi | ||
105 | |||
106 | while [[ ! $token =~ ^[[:alnum:]]{40}$ ]]; do | ||
107 | [[ -n $token ]] && echo "Not a valid token" | ||
108 | trap 'echo; return 1' INT | ||
109 | read -p "Paste your token here (Ctrl-C to skip): " token < /dev/tty | ||
110 | done | ||
111 | _configure token $token | ||
92 | } | 112 | } |
93 | 113 | ||
94 | case "$1" in | 114 | _validate_config(){ |
95 | config | c) ;; | 115 | source $CONFIG 2> /dev/null || true |
96 | *) | 116 | if [[ ! -e $CONFIG || -z $user ]]; then |
97 | while ! source $config 2> /dev/null || [[ -z "$token" ]] || [[ -z "$user" ]]; do | 117 | echo 'Hi fellow! To access your gists, I need your Github username' |
98 | _auth | 118 | echo "Also a personal token with scope which allows "gist"!'" |
99 | done;; | 119 | echo |
100 | esac | 120 | _ask_username |
121 | _ask_token | ||
122 | elif [[ -z $token && $1 =~ ^(n|new|e|edit|D|delete)$ ]]; then | ||
123 | if ! (_ask_token); then | ||
124 | echo 'To create/edit/delete a gist, a token is needed' | ||
125 | return 1 | ||
126 | fi | ||
127 | elif [[ -z $token && $1 =~ ^(u|update)$ && $2 =~ ^(s|star) ]]; then | ||
128 | if ! (_ask_token); then | ||
129 | echo 'To get user starred gists, a token is needed' | ||
130 | return 1 | ||
131 | fi | ||
132 | fi | ||
133 | } | ||
134 | |||
135 | # load configuration | ||
136 | _apply_config() { | ||
137 | source $CONFIG && _validate_config | ||
138 | |||
139 | AUTH_HEADER="Authorization: token $token" | ||
140 | [[ -z "$action" ]] && action="${EDITOR:-vi} *" | ||
141 | [[ -z "$folder" ]] && folder=~/gist && mkdir -p $folder | ||
142 | INDEX=$folder/index | ||
143 | } | ||
101 | 144 | ||
102 | github_api=https://api.github.com | 145 | _apply_config "$@" || exit 1 |
103 | auth_header="Authorization: token $token" | 146 | |
147 | ## This function determines which http get tool the system has installed and returns an error if there isnt one | ||
148 | getConfiguredClient() { | ||
149 | if command -v curl &>/dev/null; then | ||
150 | configuredClient="curl" | ||
151 | elif command -v wget &>/dev/null; then | ||
152 | configuredClient="wget" | ||
153 | elif command -v http &>/dev/null; then | ||
154 | configuredClient="httpie" | ||
155 | elif command -v fetch &>/dev/null; then | ||
156 | configuredClient="fetch" | ||
157 | else | ||
158 | echo "Error: This tool requires either curl, wget, httpie or fetch to be installed." >&2 | ||
159 | return 1 | ||
160 | fi | ||
161 | } | ||
104 | 162 | ||
105 | [[ -z "$folder" ]] && folder=~/gist | 163 | ## Allows to call the users configured client without if statements everywhere |
106 | mkdir -p $folder | 164 | httpGet() { |
107 | index=$folder/index | 165 | local header="" |
108 | starred=$folder/starred | 166 | case "$configuredClient" in |
167 | curl) [[ -n $token ]] && header="--header Authorization: token $token"; curl -A curl -s $header "$@" ;; | ||
168 | wget) [[ -n $token ]] && header="--header Authorization: token $token"; wget -qO- $header "$@" ;; | ||
169 | httpie) [[ -n $token ]] && header="Authorization:token $token"; http -b GET "$@" "$header";; | ||
170 | fetch) fetch -q "$@" ;; | ||
171 | esac | ||
172 | } | ||
109 | 173 | ||
110 | # Show the list of gist, but not updated time | 174 | # Show the list of gist, but not updated time |
111 | # TODO a way to show files | 175 | # TODO a way to show files |
112 | # TODO show git status outdated | 176 | # TODO show git status outdated |
113 | _show_list() { | 177 | _show_list() { |
114 | if [[ ! -e "$1" ]]; then | 178 | if [[ ! -e $INDEX ]]; then |
115 | echo No local file found for last update | 179 | echo 'No local file found for last update, please run command:' |
116 | echo Please run command: | 180 | echo ' gist update' |
117 | echo " gist update" | 181 | return 0 |
118 | exit 0 | 182 | fi |
119 | fi | 183 | local filter="" |
120 | cat $1 \ | 184 | if [[ $1 == "s" ]]; then |
121 | | while read line_num link file_url_array file_num extra description; do | 185 | filter='/^[^s]/ d' |
122 | local repo=$folder/$(echo $link | sed 's#.*/##') | 186 | else |
123 | local occupy=0 | 187 | filter='/^s/ d' |
124 | 188 | fi | |
125 | # if repo is not yet cloned, show green message "Not cloned yet" | 189 | cat $INDEX \ |
126 | [[ ! -d $repo ]] && extra="\e[32m[Not cloned yet]\e[0m" && occupy=17 | 190 | | while read index link blob_code file_num extra description; do |
127 | 191 | local repo=$folder/$(echo $link | sed 's#.*/##') | |
128 | # if there are some changes in git index or working directory, show blue message "working" | 192 | local occupy=0 |
129 | [[ -n $(cd $repo && git status --short) ]] 2>/dev/null && extra="\e[36m[working]\e[0m" && occupy=10 | 193 | |
130 | # if there is a commit not yet push, show red message "ahead" | 194 | # if repo is not yet cloned, show green message "Not cloned yet" |
131 | [[ -n $(cd $repo && git cherry) ]] 2>/dev/null && extra="\e[31m[ahead]\e[0m" && occupy=8 | 195 | [[ ! -d $repo ]] && extra="\e[32m[Not cloned yet]\e[0m" && occupy=16 |
132 | 196 | # if there are some changes in git index or working directory, show blue message "working" | |
133 | echo -e $line_num $link $file_num $extra $(echo $description | cut -c -$(( 60 - $occupy )) ) | 197 | [[ -n $(cd $repo && git status --short) ]] 2>/dev/null && extra="\e[36m[working]\e[0m" && occupy=9 |
134 | done | 198 | # if there is a commit not yet push, show red message "ahead" |
199 | [[ -n $(cd $repo && git cherry) ]] 2>/dev/null && extra="\e[31m[ahead]\e[0m" && occupy=7 | ||
200 | |||
201 | echo -e $index $link $file_num $extra $(echo $description | cut -c -$(( 60 -$occupy -1 )) ) | ||
202 | done \ | ||
203 | | sed "$filter" | ||
135 | } | 204 | } |
136 | 205 | ||
137 | # get the list of gists | 206 | # parse JSON from STDIN with string of commands |
138 | _update() { | 207 | AccessJsonElement() { |
139 | echo "fetching from api.github.com..." | 208 | PYTHONIOENCODING=utf-8 \ |
140 | echo | 209 | python -c "from __future__ import print_function; import sys, json; raw = json.load(sys.stdin); $1" 2> /dev/null |
141 | local list_file=$index | 210 | return "$?" |
142 | local route="users/$user/gists" | ||
143 | local mark="" | ||
144 | [[ "$1" =~ ^(star|s)$ ]] && list_file=$starred && route="gists/starred" && mark="s" | ||
145 | |||
146 | curl -s -H "$auth_header" $github_api/$route \ | ||
147 | | _parse_response | nl -s' ' | sed -E "s/^ */$mark/" > $list_file \ | ||
148 | && _show_list $list_file \ | ||
149 | || echo Fail to update gists | ||
150 | |||
151 | if [[ $auto_sync != "false" ]]; then (_sync_repos $1 > /dev/null 2>&1 &); fi | ||
152 | } | 211 | } |
153 | 212 | ||
154 | # equal to: jq '.[] | "\(.html_url) \([.files[] | .raw_url]) \(.files | keys | length) \(.comments) \(.description)"' | 213 | # equal to: jq '.[] | "\(.html_url) \([.files[] | .raw_url]) \(.files | keys | length) \(.comments) \(.description)"' |
@@ -157,6 +216,7 @@ _handle_gists() { | |||
157 | for gist in raw: | 216 | for gist in raw: |
158 | print(gist["html_url"], end=" ") | 217 | print(gist["html_url"], end=" ") |
159 | print([file["raw_url"] for file in gist["files"].values()], end=" ") | 218 | print([file["raw_url"] for file in gist["files"].values()], end=" ") |
219 | print(gist["public"], end=" ") | ||
160 | print(len(gist["files"]), end=" ") | 220 | print(len(gist["files"]), end=" ") |
161 | print(gist["comments"], end=" ") | 221 | print(gist["comments"], end=" ") |
162 | print(gist["description"]) | 222 | print(gist["description"]) |
@@ -164,83 +224,110 @@ for gist in raw: | |||
164 | } | 224 | } |
165 | 225 | ||
166 | # TODO check if a user create a very first gist | 226 | # TODO check if a user create a very first gist |
227 | # parse response from gists require | ||
167 | _parse_response() { | 228 | _parse_response() { |
168 | AccessJsonElement "$(_handle_gists)" \ | 229 | AccessJsonElement "$(_handle_gists)" \ |
169 | | tac | sed 's/, /,/g'\ | 230 | | tac | sed 's/, /,/g' | nl -s' ' \ |
170 | | while read link file_url_array file_num comment_num description; do | 231 | | while read index link file_url_array public file_num comment_num description; do |
171 | local blob_code=$(echo $file_url_array | tr ',' '\n' | sed -E 's#.*raw/(.*)/.*#\1#' | sort | cut -c -7 | paste -sd '-') | 232 | local blob_code=$(echo $file_url_array | tr ',' '\n' | sed -E 's#.*raw/(.*)/.*#\1#' | sort | cut -c -7 | paste -sd '-') |
172 | echo $link $blob_code $file_num $comment_num $description | tr -d '"' | 233 | [[ $public == 'False' ]] && local mark=p |
173 | done | 234 | [[ -n $1 ]] && local index=$1 |
235 | echo $mark$index $link $blob_code $file_num $comment_num $description | tr -d '"' | ||
236 | done | ||
237 | } | ||
238 | |||
239 | # get latest list of gists from Github API | ||
240 | _update() { | ||
241 | echo "fetching $user's gists from $GITHUB_API..." | ||
242 | echo | ||
243 | local route="users/$user/gists" | ||
244 | local mark="" | ||
245 | local filter='/^[^s]/ d' | ||
246 | if [[ "$1" =~ ^(star|s)$ ]];then | ||
247 | route="gists/starred" | ||
248 | mark="s" | ||
249 | filter='/^[s]/ d' | ||
250 | fi | ||
251 | |||
252 | local response=$(httpGet $GITHUB_API/$route) | ||
253 | false && echo Failed to update gists && return 1 | ||
254 | sed -i "$filter" $INDEX | ||
255 | echo $response | _parse_response >> $INDEX | ||
256 | _show_list $mark | ||
257 | |||
258 | if [[ $auto_sync != "false" ]]; then (_sync_repos $1 > /dev/null 2>&1 &); fi | ||
174 | } | 259 | } |
175 | 260 | ||
261 | # update local git repos | ||
176 | _sync_repos() { | 262 | _sync_repos() { |
177 | local list_file=$index | 263 | # clone repos which are not in the local |
178 | [[ "$1" =~ ^(star|s)$ ]] && list_file=$starred && route="gists/starred" | 264 | comm -13 <(find $folder -maxdepth 1 -type d | sed '1d; s#.*/##' | sort) \ |
179 | 265 | <(cat $INDEX | cut -d' ' -f2 | sed 's#.*/##' | sort) \ | |
180 | # clone repos which are not in the local | 266 | | xargs -I{} --max-procs 8 git clone git@github.com:{}.git $folder/{} |
181 | comm -13 <(find $folder -maxdepth 1 -type d | sed '1d; s#.*/##' | sort) \ | 267 | |
182 | <(cat $list_file | cut -d' ' -f2 | sed 's#.*/##' | sort) \ | 268 | # pull if remote repo has different blob objects |
183 | | xargs -I{} --max-procs 8 git clone git@github.com:{}.git $folder/{} | 269 | cat $INDEX | cut -d' ' -f2,3 \ |
184 | 270 | | while read url blob_code_remote; do | |
185 | # pull if remote repo has different blob objects | 271 | local repo=$folder/$(echo $url | sed 's#.*/##') |
186 | cat $index | cut -d' ' -f2,3 \ | 272 | local blob_code_local=$(cd $repo && git ls-tree master | cut -d' ' -f3 | cut -c-7 | sort | paste -sd '-') |
187 | | while read url blob_code_remote; do | 273 | cd $repo \ |
188 | local repo=$folder/$(echo $url | sed 's#.*/##') | 274 | && [[ $blob_code_local != $blob_code_remote ]] \ |
189 | local blob_code_local=$(cd $repo && git ls-tree master | cut -d' ' -f3 | cut -c-7 | sort | paste -sd '-') | 275 | && [[ $(git rev-parse origin/master) == $(git rev-parse master) ]] \ |
190 | cd $repo \ | 276 | && git pull |
191 | && [[ $blob_code_local != $blob_code_remote ]] \ | 277 | done |
192 | &&[[ $(git rev-parse origin/master) == $(git rev-parse master) ]] \ | 278 | echo Everything is fine! |
193 | && git pull | ||
194 | done | ||
195 | echo Everything is fine! | ||
196 | } | 279 | } |
197 | 280 | ||
198 | # get gist id from index files | 281 | # get gist id from index files |
199 | _gist_id() { | 282 | _gist_id() { |
200 | GIST_ID=$( (grep -hs '' $index $starred || true) | sed -n "/^$1 / p" | cut -d' ' -f2 | sed -E 's#.*/##') | 283 | GIST_ID=$( (grep -hs '' $INDEX || true) | sed -n "/^$1 / p" | cut -d' ' -f2 | sed -E 's#.*/##') |
201 | if [[ -z "$GIST_ID" ]]; then | 284 | if [[ -z "$GIST_ID" ]]; then |
202 | echo -e "Not a valid index: \e[31m$1\e[0m" | 285 | echo -e "Not a valid index: \e[31m$1\e[0m" |
203 | echo Use the index number in the first column instead: | 286 | echo Use the index in the first column instead: |
204 | echo | 287 | echo |
205 | _show_list "$index" | 288 | _show_list |
206 | exit 1 | 289 | return 1 |
207 | fi | 290 | fi |
208 | } | 291 | } |
209 | 292 | ||
210 | _goto_gist() { | 293 | _goto_gist() { |
211 | _gist_id $1 | 294 | _gist_id $1 || return 1 |
212 | 295 | ||
213 | if [[ ! -d $folder/$GIST_ID ]]; then | 296 | if [[ ! -d $folder/$GIST_ID ]]; then |
214 | echo 'Cloning gist as repo...' | 297 | echo 'Cloning gist as repo...' |
215 | git clone git@github.com:$GIST_ID.git $folder/$GIST_ID \ | 298 | git clone git@github.com:$GIST_ID.git $folder/$GIST_ID |
216 | && echo 'Repo is cloned' \ | 299 | |
217 | || echo 'Failed to clone the gist' | 300 | if [[ $? -eq 0 ]]; then |
301 | echo 'Repo is cloned' > /dev/tty | ||
302 | else | ||
303 | echo 'Failed to clone the gist' > /dev/tty | ||
304 | return 1 | ||
218 | fi | 305 | fi |
306 | fi | ||
219 | 307 | ||
220 | echo This gist is at $folder/$GIST_ID | 308 | (cd $folder/$GIST_ID && eval "$action") |
221 | echo -e "You can run the following command to jump to this directory: \n" | 309 | echo $folder/$GIST_ID |
222 | echo -e " \e[32m. gist $1\e[0m\n" | ||
223 | cd $folder/$GIST_ID && ls && tig --all 2> /dev/null | ||
224 | } | 310 | } |
225 | 311 | ||
226 | _delete_gist() { | 312 | _delete_gist() { |
227 | for i in "$@"; do | 313 | for i in "$@"; do |
228 | _gist_id "$i" | 314 | _gist_id "$i" |
229 | curl -X DELETE -s -H "$auth_header" $github_api/gists/$GIST_ID \ | 315 | curl -X DELETE -s -H "$AUTH_HEADER" $GITHUB_API/gists/$GIST_ID \ |
230 | && echo "$i" deleted \ | 316 | && echo "$i" deleted \ |
231 | && sed -i -E "/^$i / d" $index | 317 | && sed -i -E "/^$i / d" $INDEX |
232 | done | 318 | done |
233 | } | 319 | } |
234 | 320 | ||
235 | # remove repos which are not in user gists anymore | 321 | # remove repos which are not in user gists anymore |
236 | _clean_repos() { | 322 | _clean_repos() { |
237 | comm -23 <(find $folder -maxdepth 1 -type d | sed '1d; s#.*/##' | sort) \ | 323 | comm -23 <(find $folder -maxdepth 1 -type d | sed '1d; s#.*/##' | sort) \ |
238 | <(cat $index $starred 2> /dev/null | cut -d' ' -f2 | sed 's#.*/##' | sort) \ | 324 | <(cat $INDEX 2> /dev/null | cut -d' ' -f2 | sed 's#.*/##' | sort) \ |
239 | | while read dir; do | 325 | | while read dir; do |
240 | mv $folder/$dir /tmp && echo move $folder/$dir to /tmp | 326 | mv $folder/$dir /tmp && echo move $folder/$dir to /tmp |
241 | done | 327 | done |
242 | } | 328 | } |
243 | 329 | ||
330 | # parse JSON from gist detail | ||
244 | _handle_gist() { | 331 | _handle_gist() { |
245 | echo ' | 332 | echo ' |
246 | print("site:", raw["html_url"]) | 333 | print("site:", raw["html_url"]) |
@@ -269,104 +356,92 @@ for comment in raw: | |||
269 | 356 | ||
270 | # TODO format with simple text | 357 | # TODO format with simple text |
271 | _show_detail() { | 358 | _show_detail() { |
272 | _gist_id $1 | 359 | _gist_id $1 |
273 | curl -s $github_api/gists/$GIST_ID \ | 360 | httpGet $GITHUB_API/gists/$GIST_ID \ |
274 | | AccessJsonElement "$(_handle_gist)" | 361 | | AccessJsonElement "$(_handle_gist)" |
275 | 362 | ||
276 | curl -s $github_api/gists/$GIST_ID/comments \ | 363 | httpGet $GITHUB_API/gists/$GIST_ID/comments \ |
277 | | AccessJsonElement "$(_handle_comment)" | 364 | | AccessJsonElement "$(_handle_comment)" |
278 | } | 365 | } |
279 | 366 | ||
280 | # FIXME put file before parameters | 367 | # set filename/description/permission for a new gist |
281 | _set_gist() { | 368 | _set_gist() { |
282 | while [[ "$1" =~ ^- && "$1" != "--" ]]; do case $1 in | 369 | public=true |
283 | -d | --desc) | 370 | while [[ -n "$@" ]]; do case $1 in |
284 | description="$2" | 371 | -d | --desc) |
285 | shift; shift;; | 372 | description="$2" |
286 | -f | --file) | 373 | shift; shift;; |
287 | filename="$2" | 374 | -f | --file) |
288 | shift; shift;; | 375 | filename="$2" |
289 | esac | 376 | shift; shift;; |
290 | done | 377 | -p) |
291 | if [[ "$1" == '--' ]]; then shift; fi | 378 | public=false |
292 | files="$@" | 379 | shift;; |
293 | ls $files > /dev/null || exit 1 | 380 | *) |
381 | files="$1 $files" | ||
382 | shift;; | ||
383 | esac | ||
384 | done | ||
385 | ls $files > /dev/null || return 1 | ||
294 | } | 386 | } |
295 | 387 | ||
388 | # Let user type the content of gist before setting filename | ||
296 | _new_file() { | 389 | _new_file() { |
297 | [[ -t 0 ]] && echo "Type a gist. <Ctrl-C> to cancel, <Ctrl-D> when done" > /dev/tty | 390 | [[ -t 0 ]] && echo "Type a gist. <Ctrl-C> to cancel, <Ctrl-D> when done" > /dev/tty |
298 | tmp_file=$(mktemp) | 391 | local tmp_file=$(mktemp) |
299 | cat > $tmp_file | 392 | cat > $tmp_file |
300 | echo -e '\n' > /dev/tty | 393 | echo -e '\n' > /dev/tty |
301 | [[ -z "$1" ]] && read -p 'Type file name: ' filename < /dev/tty | 394 | [[ -z "$1" ]] && read -p 'Type file name: ' filename < /dev/tty |
302 | mv $tmp_file /tmp/$filename | 395 | mv $tmp_file /tmp/$filename |
303 | echo /tmp/$filename | 396 | echo /tmp/$filename |
304 | } | 397 | } |
305 | 398 | ||
306 | # create a new gist with files | 399 | # create a new gist with files |
307 | # TODO support secret gist | ||
308 | _create_gist() { | 400 | _create_gist() { |
309 | _set_gist "$@" | 401 | _set_gist "$@" || return 1 |
310 | [[ -z "$files" ]] && files=$(_new_file $filename) | 402 | [[ -z "$files" ]] && files=$(_new_file $filename) |
311 | [[ -z "$description" ]] && read -p 'Type description: ' description < /dev/tty | 403 | [[ -z "$description" ]] && read -p 'Type description: ' description < /dev/tty |
312 | 404 | ||
313 | for file in $files; do | 405 | local index=$(( $(sed '/^s/ d' $INDEX | wc -l) +1 )) |
314 | echo "\"$(basename $file)\": {\"content\": \"$(sed '$ !s/$/\\n/' $file)\"}," | 406 | echo 'Creating a new gist...' |
315 | done | tr -d '\n' | sed 's/^/{/; s/,$/}/' \ | 407 | for file in $files; do |
316 | | echo "{ \"public\": true, \"files\": $(cat -), \"description\": \"$description\"}" \ | 408 | echo "\"$(basename $file)\": {\"content\": \"$(sed '$ !s/$/\\n/' $file)\"}," |
317 | | curl -s -H "$auth_header" --data @- $github_api/gists \ | 409 | done | tr -d '\n' | sed 's/^/{/; s/,$/}/' \ |
318 | | sed '1 s/^/[/; $ s/$/]/' \ | 410 | | echo "{ \"public\": $public, \"files\": $(cat -), \"description\": \"$description\"}" \ |
319 | | _parse_response \ | 411 | | curl -s -H "$AUTH_HEADER" --data @- $GITHUB_API/gists \ |
320 | | sed -E "s/^/$(( $(wc -l $index | cut -d' ' -f1) + 1 )) /" >> $index \ | 412 | | tee jojo \ |
321 | && echo -e '\nGist created' \ | 413 | | sed '1 s/^/[/; $ s/$/]/' \ |
322 | || echo 'Fail to create gist' | 414 | | _parse_response $index >> $INDEX |
323 | 415 | ||
324 | _show_list $index | tail -1 | 416 | if [[ $? -eq 0 ]]; then |
417 | echo 'Gist is created' | ||
418 | _show_list | tail -1 | ||
419 | else | ||
420 | echo 'Failed to create gist' | ||
421 | fi | ||
325 | } | 422 | } |
326 | 423 | ||
327 | # update description of a gist | 424 | # update description of a gist |
328 | _edit_gist() { | 425 | _edit_gist() { |
329 | _gist_id $1 | 426 | _gist_id $1 |
330 | |||
331 | echo -n 'Type new description: ' | ||
332 | read DESC < /dev/tty | ||
333 | echo "{ \"description\": \"$DESC\" }" \ | ||
334 | | curl -X PATCH -H "$auth_header" --data @- $github_api/gists/$GIST_ID > /dev/null \ | ||
335 | && _update | ||
336 | } | ||
337 | |||
338 | _help_message() { | ||
339 | sed -E -n ' /^$/ q; 8,$ s/^#//p' $0 | ||
340 | } | ||
341 | 427 | ||
342 | _cases() { | 428 | echo -n 'Type new description: ' |
343 | if [[ $1 == 'token' ]]; then | 429 | read DESC < /dev/tty |
344 | [[ ${#2} -eq 40 ]] && echo $1=$2 \ | 430 | echo "{ \"description\": \"$DESC\" }" \ |
345 | || echo -e Invalid token format, it is not 40 chars '\n' > /dev/tty | 431 | | curl -X PATCH -H "$AUTH_HEADER" --data @- $GITHUB_API/gists/$GIST_ID > /dev/null \ |
346 | elif [[ $1 == 'auto_sync' ]]; then | 432 | && _update |
347 | [[ $2 == 'false' ]] && echo $1=$2 \ | ||
348 | || echo $1=true | ||
349 | elif [[ $1 == 'folder' ]]; then | ||
350 | [[ -n "$2" ]] && echo $1=$2 \ | ||
351 | || echo $1=~/gist | ||
352 | elif [[ $1 == 'user' ]]; then | ||
353 | echo $1=$2 | ||
354 | fi | ||
355 | } | 433 | } |
356 | 434 | ||
357 | _configure() { | 435 | usage() { |
358 | [[ -z "$@" ]] && (vim $config) && exit 0 | 436 | sed -E -n ' /^$/ q; 8,$ s/^#//p' $0 |
359 | target=$(_cases "$@") | ||
360 | |||
361 | [[ "$target" =~ [^=]$ ]] && sed -i "/^$1=/ d" $config && echo $target >> $config | ||
362 | cat $config | ||
363 | } | 437 | } |
364 | 438 | ||
439 | getConfiguredClient | ||
365 | case "$1" in | 440 | case "$1" in |
366 | "") | 441 | "") |
367 | _show_list $index ;; | 442 | _show_list ;; |
368 | star | s) | 443 | star | s) |
369 | _show_list $starred ;; | 444 | _show_list s ;; |
370 | update | u) | 445 | update | u) |
371 | _update "$2" ;; | 446 | _update "$2" ;; |
372 | new | n) | 447 | new | n) |
@@ -388,7 +463,7 @@ case "$1" in | |||
388 | shift | 463 | shift |
389 | _configure "$@" ;; | 464 | _configure "$@" ;; |
390 | help | h) | 465 | help | h) |
391 | _help_message ;; | 466 | usage ;; |
392 | *) | 467 | *) |
393 | _goto_gist "$1" ;; | 468 | _goto_gist "$1" ;; |
394 | esac | 469 | esac |