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 ;; |