Skip to content

Instantly share code, notes, and snippets.

@mmqmzk
Last active May 7, 2021 03:21
Show Gist options
  • Save mmqmzk/dcfda358d06691de65a242b9141e75c0 to your computer and use it in GitHub Desktop.
Save mmqmzk/dcfda358d06691de65a242b9141e75c0 to your computer and use it in GitHub Desktop.
Bash script to maniplute gists.
#!/usr/bin/env bash
while [[ -n "$1" ]]; do
case "$1" in
-d | --desc | --description)
description="$2"
shift
;;
-i | --id | --gist)
gist="$2"
shift
;;
-p | --public)
# public="public@true"
public="true"
;;
-u | --user)
user="$2"
shift
;;
-t | --token)
token="$2"
shift
;;
-f | --filter)
pattern="$2"
shift
;;
-o | --output-dir)
output="$2"
shift
;;
-del | --del | --delete | -r | --remove)
DEL="YES"
;;
-n | --no-content)
NO_CONTENT="YES"
;;
-1 | --auto-select)
SELECT="-1"
;;
*)
if [[ -z "$cmd" ]]; then
cmd="$1"
elif [[ -z "$file" ]]; then
file="$1"
else
newName="$1"
fi
;;
esac
shift
done
user="${user:-"$GITHUB_USER"}"
token="${token:-"$GITHUB_TOKEN"}"
if [[ -n "$user" ]]; then
AUTH="-u ${user}"
[[ -n "$token" ]] && AUTH="$AUTH:$token"
fi
GIST_FILES_MAP="${GIST_FILES_MAP:-"$HOME/.config/gist.list"}"
if [[ -n "$file" ]]; then
filename="$(basename "$file")"
fi
pattern="${pattern:-"$filename"}"
title() {
cat <<<"Filename0GistId0Description" | sed -E 's/0/\t/g'
}
_check() {
which "$1" &>/dev/null && return
echo "Tool $1 not installed." && return 1
}
_checkExit() {
_check "$@" || exit $?
}
show() {
if [[ -s "$GIST_FILES_MAP" ]]; then
title | cat - "$GIST_FILES_MAP"
else
echo "Local data empty, try fetch first."
fi
}
fetch() {
_checkExit jq
[[ -z "$user" ]] && echo "User not specifyed." && exit 1
curl -fsSL ${AUTH:+"$AUTH"} "https://api.github.com/users/${user}/gists" |
jq ".[] | [{id, file: .files[].filename, description}] | .[] \
| .id, .file, .description" | sed -E -n \
'N;N;s/\n/\t/g;s/^\"([0-9a-fA-F]+)\"/\1/;p' >"$GIST_FILES_MAP"
}
__find() {
if _check fzf; then
FOUND="$(fzf "${SELECT:-"+1"}" "${2:-"+m"}" -q "$1" \
--header "$4" --preview "echo {} | awk '{sub(/^\w+\s*/,\"\");print}'" \
<"$GIST_FILES_MAP" | awk "{print $3}")"
if [[ -z "$FOUND" ]]; then
echo "Not found or user abort." && exit 1
fi
else
local id="$(grep -F "$1" "$GIST_FILES_MAP")"
[[ -z "$id" ]] && exit 1
[[ "$(cat <<<"$id" | wc -l)" -eq 1 ]] && FOUND="$(cat <<<"$id" |
awk "{print $3}")" && return
title
echo "$id"
echo "Multiple gist ids found, choose one with -i/--id/--gist."
exit 1
fi
}
_findGist() {
[[ "$gist" == *,* ]] && gist="${gist/,*/}" && return
__find "$1" "$2" "\$1" "${3:-"Select gist to operate:"}" &&
gist="$FOUND"
}
_findName() {
__find "$1" "$2" "\$2" "${3:-"Select file to operate:"}" &&
filename="${FOUND//\"/}"
}
make() {
# _checkExit jo
if [[ -z "$filename" ]] && [[ -z "$pattern" ]]; then
echo "One of file or pattern (--filter) must provide."
exit 1
fi
# if [[ -n "$description" ]]; then
# local desc="description=${description}"
# fi
if [[ ! -f "$file" ]] && [[ "$1" != NEW ]]; then
if ! grep -Fq "\"$filename\"" "$GIST_FILES_MAP"; then
_findName "$pattern"
fi
fi
local format="{\"files\":{\"$filename\":\$file}"
filename="${newName:-$filename}"
local fileObj
if [[ -z "$DEL" ]]; then
if [[ -f "$file" ]] && [[ -z "$NO_CONTENT" ]]; then
# local content="content=@\"$file\""
fileObj="$(jq -n --arg filename "$filename" \
--rawfile content "$file" '{"filename":$filename,"content":$content}')"
else
fileObj="$(jq -n --arg filename "$filename" '{"filename":$filename}')"
fi
# fileObj="$(bash -c "jo -- filename=\"${newName:-$filename}\" \
# ${content:+"$content"}")"
else
fileObj="null"
fi
if [[ -n "$public" ]]; then
local -a pub
pub=("--argjson" "pub" "true")
format="$format,\"public\":\$pub"
fi
if [[ -n "$description" ]]; then
local -a desc
desc=(--arg desc "$description")
format="$format,\"description\":\$desc"
fi
format="$format}"
# data="$(jo -- ${public:+"$public"} ${desc:+"$desc"} files="$(jo -- \
# "$filename"=${fileObj:+"$fileObj"})")"
data="$(jq -n --argjson file "$fileObj" ${pub:+"${pub[@]}"} \
${desc:+"${desc[@]}"} --arg filename "$filename" "$format")"
}
create() {
_checkExit jq
echo "Creating gist \"${newName:-"$filename"}\""
local id="$(curl -fsSL ${AUTH:+"$AUTH"} -X POST \
"https://api.github.com/gists" -d "$data" | jq -r '.id')"
[[ -n "$id" ]] && fetch && echo "Success created ${id}."
}
update() {
[[ -z "$gist" ]] && _findGist "$pattern"
echo "Updating \"${filename}\"${newName:+" to \"$newName\""} in gist ${gist}."
if curl -fsSL ${AUTH:+"$AUTH"} -X PATCH \
"https://api.github.com/gists/${gist}" -d "$data" |
grep -qF "${newName:-"$filename"}"; then
[[ "$1" == NEW ]] && fetch
echo "Success."
elif [[ -n "$DEL" ]]; then
fetch
grep -F "$filename" "$GIST_FILES_MAP" | grep -Fq "$gist" && exit 1
echo "Delete success."
fi
}
remove() {
_findGist "$pattern" -m "Select multiple gist to delete:"
local ids="$(cat <<<"$gist" | awk -F"[\n, ]" -v ORS="|" '{print}')"
cat <<<"$ids" | tr '|' '\n' | xargs -n 1 -I{} \
curl -fsSL ${AUTH:+"$AUTH"} -X DELETE "https://api.github.com/gists/{}"
fetch
grep -Fq "${ids/%|/}" "$GIST_FILES_MAP" || echo "Delete success."
}
_write() {
[[ -d "$output" ]] || mkdir -p "$output"
local outFile="${output}/$1"
[[ -e "$outFile" ]] && mv --backup=t -f "$outFile" "${outFile}.bak"
cat <<<"$data" | jq -r ".files.\"$1\".content" >"$outFile"
}
download() {
_checkExit jq
_checkExit fzf
_findGist "$pattern"
data="$(curl -fsSL ${AUTH:+"$AUTH"} "https://api.github.com/gists/${gist}")"
export -f _write
export data
export output="${output:-./}"
cat <<<"$data" | jq '.files[].filename' |
fzf -m --header "Select multiple files to download:" |
xargs -I{} -n 1 bash -c '_write "{}"'
}
info() {
_findGist "$pattern" && echo "$gist"
}
[[ -f "$GIST_FILES_MAP" ]] && touch "$GIST_FILES_MAP"
case "$cmd" in
show | s)
show
;;
fetch | f)
fetch
show
;;
create | c | new | n)
[[ -n "$NO_CONTENT" || ! -f "$file" ]] &&
echo "Can't create empty new gist." && exit 1
make NEW
create
;;
add | a)
[[ -n "$NO_CONTENT" || ! -f "$file" ]] &&
echo "Can't add empty file." && exit 1
make NEW
update NEW
;;
update | upload | u | rename | mv)
fetch
make
update
;;
delete | del | remove | r)
fetch
remove
;;
download | down | d)
fetch
download
;;
info | id | i | gist)
info
;;
sync)
fetch
info
;;
test | t | make | m)
make
echo "$data"
;;
help | h)
cat <<-'EOF'
Useage:
gist <CMD> [[-i|--id|--gist] <gist id>] [-1|--auto-select] [-del|--delete] \
[-p|--public] [[-f|--filter] <pattern>] [[-u|--user] <github username>] [-n|--no-content]\
[[-t|--token] <github personal token>] [[-o|--output-dir] <download dir>]\
[[-d|--desc|--description] <description>] [file|pattern] [new filename]
CMD can be:
show, s: Print all gists info locally.
fetch, f: Get gists info from Github then show all.
create, c, new, n: Create a new gist.
update, u, add, a, rename mv: Update a gist file or add/deletei/rename file to/from/in a gist.
delete, del, remove, r: Delete a whole gist.
download, down, d: Download one or more files from HEAD commit of a gist to --output-dir.
info, id, i, gist: Find gist ids for a file or a filename locally.
sync: Combined fetch and info, first get gists info from Github then (fuzzy) search.
test, t, make, m: Dry run, build request data then print.
Options:
id, gist: Gist ids separate by comma.
delete: Delete spacify file from a gist.
public: Make gist public.
output-dir: Download location.
no-content: Do not send file content.
Environment variales:
$GITHUB_USER: Githb username, override by --user.
$GITHUB_TOKEN: Github personal access token, override by --token.
$GIST_FILES_MAP: Local file to store gists info, default to $HOME/.config/gist.list.
EOF
;;
*)
echo "Command not found."
bash "$0" help
exit 1
;;
esac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment