Last active
March 13, 2016 10:58
-
-
Save criztovyl/788d80624e6c0641d465 to your computer and use it in GitHub Desktop.
Sets up Gists
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# A script for initializing gists | |
# Copyright (C) 2015 Christoph "criztovyl" Schulz | |
# | |
# This program is free software: you can redistribute it and/or modify | |
# it under the terms of the GNU General Public License as published by | |
# the Free Software Foundation, either version 3 of the License, or | |
# (at your option) any later version. | |
# | |
# This program is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
# GNU General Public License for more details. | |
# | |
# You should have received a copy of the GNU General Public License | |
# along with this program. If not, see <http://www.gnu.org/licenses/>. | |
# | |
if [ "$1" ] && ( [ "$1" == "help" ] || [ "$1" == "-h" ] || [ "$1" == "--help" ] ); then | |
echo "Usage: gist-init [GitHubUsername]" | |
fi | |
# Args: GitHubUsername | |
username=$1 | |
method="ssh" # change it to http(s) if you want | |
[ -z "$USER" ] && USER=$USERNAME; # local user not GitHub | |
## | |
# Constants | |
## | |
config=~/.gist-init | |
api_accept="application/vnd.github.v3+json" | |
XGH="X-GitHub-OTP: required; " # Git Hub OTP Header | |
AUTH_DATA="{\"scopes\": [\"gist\"], \"note\": \"gist-init.sh $USER@$HOSTNAME\"}" | |
TRUE=0 | |
FALSE=1 | |
OPT_KEYRING_ID="oauth-keyring-id" | |
OPT_USERNAME="username" | |
CONTINUE_LATER='To continue later, you have to `git commit`, then you can publish the gist via `'$0'`.' | |
GIST_STUB_INFO="The Gist itself is already created, so it will show up in your gist.github.com/$username list as a stub." | |
EXIT_NO_CREATE_REPO=5 | |
EXIT_MISSING_TOKEN=6 | |
EXIT_BAD_CREDENTIALS=7 | |
EXIT_TOKEN_ALREADY_OPTAINED=8 | |
EXIT_UNKNOWN_HTTP_ERR=9 | |
EXIT_WRONG_OTP=10 | |
EXIT_DESCRIPTION_EMPTY=11 | |
EXIT_SIGINT=12 | |
EXIT_GIT_ERROR=13 | |
EXIT_ERROR_GIST_CREATE=14 | |
EXIT_SHELL_CANCEL=15 | |
EXIT_COMMIT_ABORTED=16 | |
# CTRL-C | |
trap "echo >&2; exit $EXIT_SIGINT" SIGINT | |
## | |
# Functions | |
## | |
# Checks wether OTP is required | |
# Args: filteredResponse resultVar | |
otp_required() | |
{ | |
local filteredResponse=$1 | |
local resultVar=$2 | |
local _otp=`echo $filteredResponse | grep "$XGH" | wc -l` | |
[ $_otp -eq 1 ] && eval $resultVar=$TRUE || eval $resultVar=$FALSE | |
} | |
# Determines the OTP type (app or sms) | |
# Args: filteredResponse resultVar | |
otp_type() | |
{ | |
local filteredResponse=$1 | |
local resultVar=$2 | |
local _type=`echo $filteredResponse | grep "$XGH" | sed "s/.\+$XGH\(\w\+\).\+/\1/"` | |
eval $resultVar=$_type | |
} | |
# Set an option | |
# Args: optName optValue | |
set_opt() | |
{ | |
local name=$1 | |
local value=$2 | |
[ `grep "$name" $config | wc -l` -gt 0 ] && sed "s/$name .\+/$name $value/" $config -i || echo "$name $value" >> $config | |
} | |
# Read an option | |
# Args: optName | |
read_opt() | |
{ | |
local name=$1 | |
eval 'read_opt_callback(){ | |
local regex="'$name' (.+)" | |
[[ "$2" =~ $regex ]] && echo ${BASH_REMATCH[1]} | |
}' | |
echo `exec 3<$config; mapfile -u 3 -C read_opt_callback -c 1; exec 3<&-` | |
} | |
# Construct POST data | |
# Args: resultVar | |
gist_post() | |
{ | |
data='{"description": "'$description'", "public": true, "files": {"initializing.txt": {"content": "Initializing..."}}}' | |
eval $1="'"$data"'" | |
} | |
# Git Error quit | |
# Args: info | |
git_error() | |
{ | |
info=$1 | |
echo "Git Error." >&2 | |
[ "$info" ] && echo $info >&2 | |
echo $GIST_STUB_INFO >&2 | |
exit $EXIT_GIT_ERROR | |
} | |
## | |
# Script | |
## | |
[ ! -f $config ] && > $config | |
username=`read_opt $OPT_USERNAME` | |
# Try to obtain OAuth token from GNOME keyring | |
keyring_item_id=`read_opt $OPT_KEYRING_ID` | |
if [ "$keyring_item_id" ]; then | |
token=`echo -e "import gnomekeyring as gk; print(gk.item_get_info_sync(None, $keyring_item_id).get_secret())" | python 2>/dev/null` | |
[ "$token" ] || echo "Token missing in keyring" >&2 | |
else | |
echo "No keyring item ID in config." >&2 | |
fi | |
# If can't obtain from keyring, ask GitHub and save into keyring | |
if [ ! "$token" ]; then | |
if [ -z $username ]; then | |
read -p "GitHub username: " username | |
set_opt username $username | |
else | |
echo "Username: $username" >&2 | |
fi | |
read -sp "GitHub password: " password && echo | |
# Authenticate with GitHub | |
# Pick only data we need with grep and RegEx | |
response=`\ | |
curl -is https://api.github.com/authorizations -X POST -u "$username:$password" \ | |
-H "Accept: $api_accept" -H "application/json" -d "$AUTH_DATA" \ | |
| grep 'Status: [45][0-9]\{2\}\|X-GitHub-OTP: required; .\+\|message\|"code": "already_exists"\|"token": "[[:alnum:]]"' | tr -d "\r"` | |
# X-GitHub-OTP contains OTP type (if required); code: already_exists appears if we already registered a token. | |
# Last one picks the token we require. Also remove carriage return which leads to data errors. | |
#Errors | |
[ `echo "$response" | grep 'Status: 401\|Bad credentials' | wc -l` -eq 2 ] && { echo "Wrong password." >&2; exit $EXIT_BAD_CREDENTIALS; } | |
[ `echo "$response" | grep 'Status: 422\|Unprocessable Entity\|"code": "already_exists"' | wc -l` -eq 2 ] && { echo "Already optained OAuth token." >&2; exit $EXIT_TOKEN_ALREADY_OPTAINED; } | |
#[ `echo "$response" | grep 'Status: 4[0-9]\{2\}' | wc -l` -eq 1 ] && echo -e "Unknown error.\n$response"; exit 1; | |
# Check whether we need to provide an one-time-password | |
otp_required "$response" otp | |
if [ "$otp" == "$TRUE" ]; then | |
otp_type "$response" "type" # app or sms | |
read -p "Enter your OTP code (check your $type): " code | |
# Retry request with OTP | |
response=`\ | |
curl -is https://api.github.com/authorizations -X POST -u "$username:$password" \ | |
-H "Accept: $api_accept" -H "X-GitHub-OTP: $code" -H "application/json" -d "$AUTH_DATA" \ | |
| grep 'Status: [45][0-9]\{2\}\|X-GitHub-OTP: required; .\+\|message\|"code": "already_exists"\|"token": "[[:alnum:]]\+"' | tr -d "\r"` | |
[ `echo "$response" | grep 'Status: 422\|Unprocessable Entity\|"code": "already_exists"' | wc -l` -eq 2 ] && { echo "Already optained OAuth token." >&2; exit $EXIT_TOKEN_ALREADY_OPTAINED; } | |
[ `echo "$response" | grep 'Status: 4[0-9]\{2\}' | wc -l` -eq 1 ] && echo -e "Unknown error.\n$response" >&2; exit $EXIT_UNKNOWN_HTTP_ERR; | |
otp_required "$response" otp; [ "$otp" == "$TRUE" ] && { echo "Wrong OTP." >&2; exit $EXIT_WRONG_OTP; } | |
fi | |
# Extract OAuth token | |
regex='"token": "([[:alnum:]]+)",' | |
[[ "$response" =~ $regex ]] && echo "Found token" >&2 || echo "No token found!" >&2 | |
token=${BASH_REMATCH[1]} | |
# Store token into GNOME keyring, and remember item id | |
if [ "$token" ]; then | |
keyring_item_id=`echo -e "import gnomekeyring as gk; print(gk.item_create_sync(None, 0, \"gist-init.sh GitHub OAuth\", {}, \"$token\", False))" | python 2>/dev/null` | |
set_opt $OPT_KEYRING_ID $keyring_item_id | |
fi | |
fi | |
# Exit if token is still missing | |
[ -z "$token" ] && { echo "Missing token!" >&2; exit $EXIT_MISSING_TOKEN; } | |
# Check if is already an repo, if not ask whether to create one | |
if [ ! -d .git ]; then | |
read -p "Initialise repository? (Y/n)" | |
if [[ "$REPLY" =~ ^[yY]$ ]] || [ -z "$REPLY" ]; then | |
git init | |
git add . | |
git status | |
read -p "Open a shell? (y/N)" | |
if [[ "$REPLY" =~ ^[yY]$ ]]; then | |
echo "If you're ready, quit shell with \"exit\". Cancel with non-zero exit code (i.e \"exit 1\")." >&2 | |
if ! bash; then | |
echo 'You cancelled. '$CONTINUE_LATER >&2 | |
exit $EXIT_SHELL_CANCEL | |
fi | |
fi | |
if ! git commit -a; then | |
echo "You aborted the commit, we'll stop here." >&2 | |
echo $CONTINUE_LATER >&2 | |
exit $EXIT_COMMIT_ABORTED | |
fi | |
else | |
echo "Cancelling." >&2 | |
exit $EXIT_NO_CREATE_REPO | |
fi | |
fi | |
read -p "Description (one line): " description | |
[ "$description" ] || { echo "Description cannot be empty!" >&2; exit $EXIT_DESCRIPTION_EMPTY; } | |
gist_post data | |
# Finally create gist | |
response=`curl -s https://api.github.com/gists -u $user:$token -d "$data"` | |
# Extract Gist ID | |
id=`echo $response | grep -o '"id": "[[:alnum:]]\+"'` | |
regex='"id": "([[:alnum:]]+)"' | |
if ! [[ "$id" =~ $regex ]]; then | |
echo "Error Creating Gist: Cannot match Gist ID." >&2 | |
echo $GIST_STUB_INFO >&2 | |
exit $EXIT_ERROR_GIST_CREATE | |
fi | |
id=${BASH_REMATCH[1]} | |
[ "$method" == "https" ] && remote="https://gist.github.com/"$id".git" | |
# Default is ssh, so if no remote is set, use ssh. (or if ssh is set, isn't handled special) | |
[ -z "$remote" ] && remote="[email protected]:/"$id".git" | |
# Add remote | |
git remote add origin $remote || git_error | |
# force update, set upstream | |
git push -fu origin master || git_error | |
# Fetch origin for that nice "Your branch is up-to-date with 'origin/master'". | |
git fetch origin || git_error |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment