Skip to content

Instantly share code, notes, and snippets.

@mattdanielbrown
Forked from itayd/gist
Created February 24, 2023 18:58
Show Gist options
  • Save mattdanielbrown/7aa94827278683934c5f22efdbe2858d to your computer and use it in GitHub Desktop.
Save mattdanielbrown/7aa94827278683934c5f22efdbe2858d to your computer and use it in GitHub Desktop.
bash gist cli
#!/bin/bash
require() {
for what in $*; do
if !(which $what >& /dev/null); then
echo "error: $what is required to run this script, please install it"
exit 1
fi
done
}
require xmlstarlet curl git
URL_BASE=https://gist.github.com
user=`git config gist.user`
[[ -z $user ]] && user=$GIST_USER
[[ -z $user ]] && user=`whoami`
token=`git config gist.token`
[[ -z $token ]] && token=$GIST_TOKEN
repo=
rest=
op=
verbosity=0
desc=
cmdout=
err=
metadata=
verbose() {
level=$1
shift
msg="$@"
(( $verbosity >= $level )) && echo $msg
return 0
}
info() {
verbose 0 $@
}
error() {
info "error: $@" && return 0
}
warn() {
info "warning: $@" && return 0
}
trace() {
verbose 1 "$@"
}
debug() {
local level=`expr $1 + 2`
shift
verbose $level "$@"
}
usage() {
local exe=`basename $0`
cat <<eom
gist bash cli
usage: $exe operation [-options] [files...]
operations:
help - this text
ls - lists files and repositories
put - create/update repository
rm - remove a repository
cat - prints the request files into stdout
metadata - prints raw metadata
options:
-h - this text
-v, -vv, -vvv... - increase verbosity
-r <repos> - specify repository
-u <user> - specify gist username
-t <token> - specify gist token
-d <description> - repository description for put command
notes:
default username is determined with the following priority:
1. specified by -u (HIGHEST)
2. content of \$GIST_USER
3. content of git config gist.user
4. \`whoami\` (LOWEST)
default token is determined with the following priority:
4. specified by -t (HIGHEST)
2. content of \$GIST_TOKEN
3. content of git config gist.token
examples:
$ $exe put -d "a neet file" file1
creates a new repository with files file1 and file2
$ $exe put -r 31337 -d "two neet files" file1 file2
updates repository 31337 so that it would have file1 and file2
$ $exe ls
lists all files in all repositories
$ $exe ls -r 31337
lists all files in repository 3133
$ $exe ls -v
listal all files in all repositories along with the repository description
$ $exe cat -u testuser testfile.py
prints contents of file testfile.py (in any repository) for user testuser
$ $exe cat -r 31337 testfile.py
prints contents of file testfile.py that is in repository 31337
$ $exe cat -r 31337
prints contents of entrie repository 31337
$ $exe metadata
prints metadata of gist in xml format
$ $exe metadata -r 31337
prints metadata of repository 31337, subset of the entire user's metadata
$ $exe rm -r 31337
remove a repository
eom
}
opts() {
if [[ ${1:0:1} != "-" ]]; then
op=$1
OPTIND=2
fi
while getopts "hu:r:t:d:v" opt; do
case $opt in
h) usage; exit 0;;
u) user=$OPTARG;;
t) token=$OPTARG;;
r) repo=$OPTARG;;
d) desc=$OPTARG;;
v) verbosity=`expr $verbosity + 1`;;
*) usage; exit 1;;
esac
done
shift `expr $OPTIND - 1`
rest=$*
if [[ -z $op ]]; then
error "operation must be specified"
return 1
else
return 0
fi
}
copyall() {
while read line; do echo $line; done
}
notags() {
copyall | sed -e "s/<[^>]*>/ /g" -e "s/ / /g"
}
xmlsel() {
copyall | xmlstarlet sel -t -c "$@"
}
execute_raw() {
cmd=$*
debug 0 -n "exec: $cmd"
echo $cmd > /tmp/.gist.$$
source /tmp/.gist.$$
rc=$?
rm -f /tmp/.gist.$$
debug 0 -e "\x20-> $rc"
return $rc
}
execute() {
cmd=$*
debug 0 -n "exec: $cmd"
echo $cmd > /tmp/.gist.$$
cmdout=`source /tmp/.gist.$$`
rc=$?
rm -f /tmp/.gist.$$
debug 0 -e "\x20-> $rc"
return $rc
}
do_curl_api() {
do_curl "/api/v1/xml/$*"
return $?
}
do_curl() {
err=
execute "curl -s $URL_BASE$*"
debug 2 $cmdout
rc=$?
if (( $rc )); then
err="curl failed"
elif [[ ${cmdout:0:1} != "<" ]]; then
if [[ ${cmdout} == "error" ]]; then
err="general failure"
else
err="invalid format"
fi
return 1
else
error=`echo $cmdout | xmlstarlet sel -t -c "error" | notags`
if [[ -n $error ]]; then
err=$error
return 1
else
return 0
fi
fi
}
download_metadata() {
[[ -z $metadata ]] && {
trace "downloading user gists metadata"
do_curl_api "gists/$user"
(( $? )) && error "download_metadata: $err" && return 1
metadata=$cmdout
debug 2 $metadata
}
return 0
}
repo_files() {
repo=$1
echo $metadata | xmlsel "gists/gist[repo=${repo}]/files/file/file" | notags
return $?
}
repos_list() {
echo $metadata | xmlsel "gists/gist/repo" | notags
}
do_ls() {
do_ls_core
return $?
}
do_ls_core() {
download_metadata
(( $? )) && return 1
local repos=$repo
[[ -z $repos ]] && repos=`repos_list`
for r in $repos; do
local files=`repo_files $r`
local fs=$files
[[ -n $rest ]] && {
fs=
for f in $files; do
local found=`echo " $rest " | grep -o " $f "`
[[ -n $found ]] && fs="${fs}${f} "
done
}
[[ -n $fs ]] && {
info -n "$r: $fs"
local d=`echo $metadata | xmlsel "gists/gist[repo=${r}]/description" | notags`
[[ -n $d ]] && trace -ne "\x20# $d"
info
}
done
return 0
}
for_files() {
local action=$1
[[ -z $rest && -z $repo ]] && error "no files or repository specified" && return 1
download_metadata
(( $? )) && return 1
[[ -z $rest ]] && {
rest=`repo_files $repo`
[[ -z $rest ]] && error "no files in repository"
}
for f in $rest; do
local lines=`do_ls_core | cut -d# -f1 | egrep "\<${f}\>"`
local n=`echo $lines | sed -e "s/[^:]//g" | wc -c`
if [[ $n -lt 2 ]]; then
warn "$f not found"
elif [[ $n -gt 2 ]]; then
warn "$f found in multiple repositories, specify -r to select repository"
else
local r=`echo $lines | cut -d: -f1`
$action $r $f
(( $? )) && return 1
fi
done
return 0
}
do_cat_fetch() {
local r=$1
local f=$2
trace "downloading $f from repository $r"
execute_raw "curl -s $URL_BASE/raw/$r/$f"
info
}
do_cat() {
for_files do_cat_fetch
}
do_rm() {
[[ -n $rest ]] && error "file names must not be specified" && exit 1
[[ -z $repo ]] && error "repository must be specified" && exit 1
[[ -z $token ]] && error "authentication token must be specified" && exit 1
do_curl "/delete/$repo --include --data login=$user --data token=$token --data _method=delete"
return 0
}
do_put() {
[[ -z $rest ]] && error "file names must be specified" && exit 1
[[ -z $token ]] && error "authentication token must be specified" && exit 1
local files_data=
local i=0
for f in $rest; do
i=`expr $i + 1`
local ii="gistfile$i"
[[ ! -r $f ]] && error "file $f is not readable" && exit 1
local name=`basename $f`
local ext=${name##*.}
[[ $ext == $name ]] && ext=
files_data="$files_data --data 'file_ext[$ii]=${ext}' --data 'file_name[$ii]=$name' --data-urlencode description=$desc --data-urlencode file_contents[$ii]@$f"
done
[[ -n $repo ]] && repo_url="/$repo" && method="--data _method=put"
do_curl "/gists${repo_url} --include $method --data 'login=$user' --data 'token=$token' --data 'action_button=public' $files_data"
repo=`echo $cmdout | egrep -o "Location: [^ ]*" | grep -o [0-9]*`
if [[ -z $repo ]]; then
error "failed"
else
info repository $repo
rest=
do_ls
fi
}
do_metadata() {
download_metadata
(( $? )) && return 1
if [[ -z $repo ]]; then
info -e $metadata | xmlstarlet fo
else
info $metadata | xmlsel "gists/gist[repo=$repo]" | xmlstarlet fo
fi
return $?
}
act() {
rc=0
case $op in
help) usage; rc=0;;
metadata) do_metadata; rc=$?;;
ls) do_ls; rc=$?;;
cat) do_cat; rc=$?;;
put) do_put; rc=$?;;
rm) do_rm; rc=$?;;
*) error "unrecognized operation $op"; rc=1;;
esac
return $rc
}
dump() {
debug 2 "parameters: op=${op}, user=${user}, token=${token}, repo=${repo}, rest=${rest}, verbosity=${verbosity}"
return 0
}
main() {
opts "$@" && dump && act
return $?
}
main "$@"
exit $?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment