BASENAME=$( basename "$0" )
NOW=$(date +%s)
-VERSION="0.3"
+VERSION="0.4git"
DBVERSION=${VERSION}_${NOW}
set_defaults() {
JQ=${JQ:-/usr/bin/jq}
EDITOR=${EDITOR:-/usr/bin/vim}
TERMINAL=${TERMINAL:-/usr/bin/alacritty}
+# Git binary only used if $USEGIT is true - See below
+GIT=${GIT:-/usr/bin/git}
# add options for your terminal. Remember to add the last option to execute
# your editor program, otherwise the script will fail.
# see example in the addnote function
# directory containing the actual notes
NOTESDIR=${BASEDIR}/notes
+### GIT SUPPORT
+
+# If you want to store your notes in a git repository set this to true
+USEGIT=true
+# Address of your remote repository. Without this GIT will refuse to work
+GITREMOTE=${GITREMOTE:-""}
+# How long should we wait (in seconds) between sync on the git remote. Default 3600 (1 hour)
+GITSYNCDELAY=${GITSYNCDELAY:-"3600"}
+# The name of this client. If left empty, defaults to the output of hostname
+GITCLIENT=${GITCLIENT:-""}
+
} # end set_defaults, do not change this line.
set_defaults
echo $PID > "$PIDFILE"
# Export config to file
-function export_config() {
+export_config() {
if [ -r ${RCFILE} ]; then
echo "Backing up current '${RCFILE}'...."
mv -f ${RCFILE} ${RCFILE}.$(date +%Y%m%d_%H%M)
# we should expand on this function to add a sample note and explain a little bit
# how the program works.
-function firstrun() {
+firstrun() {
[ -f $RCFILE ] && RC=$RCFILE || RC="none"
clear
jq executable: ${JQ}
"
+ echo "Now I'll create the needed files and directories."
read -r -p "Do you wish to continue? (y/N) " ANSWER
case $ANSWER in
y|Y )
"version": "${VERSION}",
"dbversion": "${DBVERSION}"
},
+ "git": {
+ "lastpull": ""
+ },
"notes": []
}
__EOL__
firstrun
fi
# check if input is a number, returns false or the number itself
-function check_noteID() {
+check_noteID() {
IN=$1
case $IN in
''|*[!0-9]*)
- return 1
+ false
;;
*)
echo "$IN"
esac
}
-function helptext() {
+helptext() {
echo "Usage:"
- echo " $0 [PARAMS] ..."
+ echo " $0 [PARAMS] [note ID]..."
+ echo ""
+ echo "${BASENAME} parameters are:"
+ echo -e " -h | --help\t\t\t: This help text"
+ echo -e " -p | --plain\t\t\t: Output is in plain text"
+ echo -e "\t\t\t\t (without this option the output is formatted)"
+ echo -e "\t\t\t\t (this option must precede all others)"
+ echo -e " -l | --list\t\t\t: List existing notes"
+ echo -e " -a | --add=[\"<title>\"]\t: Add new note"
+ echo -e " -e | --edit=[<note>]\t\t: Edit note"
+ echo -e " -d | --delete=[<note> | all] : Delete single note or all notes at once"
+ echo -e " -s | --show=[<note>]\t\t: Display note using your favourite PAGER"
+ echo -e " -r | --restore=[<dir>]\t: Restore a previous backup from dir"
+ echo -e " -v | --version\t\t: Print version"
+ echo -e " --userconf\t\t\t: Export User config file"
+ echo -e " --backup [<dest>]\t\t: Backup your data in your destination folder"
+ echo -e " --showconf\t\t\t: Display running options"
+ echo -e " --sync\t\t\t: Sync notes to git repository"
echo ""
- cat << __NOWCONF__
-${BASENAME} configuration is:
+ echo -e "if a non option is passed and is a valid note ID, the note will be displayed."
+}
-base directory: ${BASEDIR}/
-notes archive: ${NOTESDIR}/
-notes database: ${DB}
-rc file: $RCFILE
-debug file: /tmp/debug_bash-note.log
+configtext() {
+ [ $USEGIT ] && GITUSE="enabled" || GITUSE="disabled"
+ if [ -n $GITCLIENT ]; then
+ CLIENTGIT="$( hostname )"
+ else
+ CLIENTGIT="$GITCLIENT"
+ fi
+ clear
+ echo -e "${BASENAME} configuration is:"
+
+ echo -e "\tbase directory: ${BASEDIR}/"
+ echo -e "\tnotes archive: ${NOTESDIR}/"
+ echo -e "\tnotes database: ${DB}"
+ echo -e "\trc file: $RCFILE"
+ echo -e "\tdebug file: /tmp/debug_bash-note.log"
+ echo
+ echo -e "\ttext editor: ${EDITOR}"
+ echo -e "\tterminal: ${TERMINAL}"
+ echo -e "\tjq executable: ${JQ}"
+ echo -e "\tPAGER: ${PAGER}"
+ echo
+ echo -e "\tGIT: ${GITUSE} - ${GIT}"
+ echo -e "\tGIT remote: ${GITREMOTE}"
+ echo -e "\tGIT sync delay: ${GITSYNCDELAY}"
+ echo -e "\tGIT client name: ${CLIENTGIT}"
+}
-text editor: ${EDITOR}
-terminal: ${TERMINAL}
-jq executable: ${JQ}
-__NOWCONF__
+# this function returns a random 2 words title
+random_title() {
+ # Constants
+ X=0
+ DICT=/usr/share/dict/words
+ OUTPUT=""
+
+ # total number of non-random words available
+ COUNT=$(cat $DICT | wc -l)
+
+ # while loop to generate random words
+ while [ "$X" -lt 2 ]
+ do
+ RAND=$(od -N3 -An -i /dev/urandom | awk -v f=0 -v r="$COUNT" '{printf "%i\n", f + r * $1 / 16777216}')
+ OUTPUT+="$(sed `echo $RAND`"q;d" $DICT)"
+ (("X = X + 1"))
+ [[ $X -eq 1 ]] && OUTPUT+=" "
+ done
+
+ echo $OUTPUT
+}
- echo ""
- echo "${BASENAME} parameters are:"
- echo " -h | --help : This help text"
- echo " -p | --plain : Output is in plain text"
- echo " (without this option the output is formatted)"
- echo " (this option must precede all others)"
- echo " -l | --list : List existing notes"
- echo " -a | --add [\"<title>\"] : Add new note"
- echo " -e | --edit [<note>] : Edit note"
- echo " -d | --delete [<note> | all] : Delete single note or all notes at once"
- echo " -s | --show [<note>] : Display note using your favourite PAGER"
- echo " -v | --version : Print version"
- echo " --userconf : Export User config file"
- echo ""
+# check if GITCLIENT has been set or set it to the output of hostname
+if [ -z "$GITCLIENT" ]; then
+ GITCLIENT=$( hostname )
+fi
+# returns true if the argument provided directory is a git repository
+is_git_repo() {
+ DIR=$1
+ if [[ -d $DIR ]]; then
+ cd $DIR
+ if git rev-parse 2>/dev/null; then
+ true
+ else
+ false
+ fi
+ fi
+}
+
+# sync local repository to remote
+# accepts -f parameter to skip last sync check
+gitsync() {
+ FORCE=$1
+ if [[ $USEGIT && -n $GITREMOTE ]]; then
+ [ $PLAIN == false ] && echo "Syncing notes with git on remote \"$GITREMOTE\""
+ NOWSYNC=$(date +%s)
+ if [[ $FORCE == "-f" ]]; then
+ $JQ --arg n "$NOWSYNC" '.git["lastpull"] = $n' "$DB" > $TMPDB
+ mv $TMPDB $DB
+ cd $BASEDIR
+ [ $PLAIN == false ] && $GIT pull || $GIT pull -q
+ else
+ # LASTSYNC is the last time we synced to the remote, or 0 if it's the first time.
+ LASTSYNC=$($JQ -r '.git["lastpull"] // 0' "$DB")
+ SYNCDIFF=$(( ${NOWSYNC} - ${LASTSYNC} ))
+ if (( $SYNCDIFF > $GITSYNCDELAY )); then
+ #more than our delay time has passed. We can sync again.
+ $JQ --arg n "$NOWSYNC" '.git["lastpull"] = $n' "$DB" > $TMPDB
+ mv $TMPDB $DB
+ cd $BASEDIR
+ [ $PLAIN == false ] && $GIT pull || $GIT pull -q
+ else
+ # Last synced less than $GITSYNCDELAY seconds ago. We shall wait
+ [ $PLAIN == false ] && echo "Last synced less than $GITSYNCDELAY seconds ago. We shall wait"
+ fi
+ fi
+ else
+ # no git, so we just keep going
+ true
+ fi
+}
+
+# add note to git and push it to remote
+gitadd() {
+ if [[ $USEGIT && -n $GITREMOTE ]]; then
+ [ $PLAIN == false ] && echo "Adding note to remote \"$GITREMOTE\""
+ cd $BASEDIR
+ $GIT add .
+ $GIT commit -m "$(basename $0) - adding note from ${GITCLIENT}"
+ $GIT push origin master
+ else
+ # no git, so we just keep going
+ true
+ fi
+}
+
+# edited note added to git and pushed it to remote
+gitedit() {
+ if [[ $USEGIT && -n $GITREMOTE ]]; then
+ [ $PLAIN == false ] && echo "Editing note on remote \"$GITREMOTE\""
+ cd $BASEDIR
+ $GIT add .
+ $GIT commit -m "$(basename $0) - ${GITCLIENT} note edited."
+ $GIT push origin master
+ else
+ # no git, so we just keep going
+ true
+ fi
}
-function addnote() {
+
+# add note to git and push it to remote
+gitremove() {
+ NOTE=$1
+ FILE=$2
+ if [[ $USEGIT && -n $GITREMOTE ]]; then
+ [ $PLAIN == false ] && echo "Deleting notes from remote \"$GITREMOTE\""
+ if [ "all" == $NOTE ];then
+ echo "Deleting all notes"
+ cd $BASEDIR
+ $GIT rm notes/*
+ $GIT commit -m "$(basename $0) - ${GITCLIENT} removing all notes."
+ $GIT push origin master
+ else
+ local OK=$(check_noteID "$NOTE")
+ if [[ "$OK" ]]; then
+ echo "Deleting note ID ${NOTE}"
+ cd $BASEDIR
+ $GIT rm notes/${FILE}
+ $GIT add .
+ $GIT commit -m "$(basename $0) - ${GITCLIENT} removing note ID ${NOTE}."
+ $GIT push origin master
+ fi
+ fi
+ else
+ # no git, so we just keep going
+ true
+ fi
+}
+
+# check for USEGIT and subsequent variables
+if [[ $USEGIT && -n $GITREMOTE ]]; then
+ # GIT is a go.
+ if ! is_git_repo $BASEDIR; then
+ # initializing git repository
+ cd $BASEDIR
+ $GIT init
+ echo "adding all files to git"
+ $GIT add .
+ $GIT commit -m "$(basename $0) - initial commit from ${GITCLIENT}"
+ $GIT remote add origin $GITREMOTE
+ $GIT push -u origin master
+ fi
+elif [[ $USEGIT && -z $GITREMOTE ]]; then
+ echo "GITREMOTE variable not set. reverting USEGIT to false"
+ USEGIT=false
+fi
+
+addnote() {
+ # attempt syncing before adding a note
+ gitsync -f
# remove eventually existing temp DB file
if [[ -f $TMPDB ]]; then
rm $TMPDB
fi
- NOTETITLE="$1"
+ # RANDOM TITLE
+ RTITLE=$(random_title)
+
+ if [[ -z $1 ]]; then
+ read -r -p "Title: " TITLE
+ case "$TITLE" in
+ '' )
+ NOTETITLE="$RTITLE"
+ ;;
+ * )
+ NOTETITLE=$TITLE
+ ;;
+ esac
+ fi
+
+ # [[ -z "$1" ]] && NOTETITLE="$RTITLE" || NOTETITLE="$1"
echo "adding new note - \"$NOTETITLE\""
# shellcheck disable=SC2086
LASTID=$($JQ '.notes[-1].id // 0 | tonumber' $DB)
# alacritty --class notes --title notes -e /usr/bin/vim ...
# shellcheck disable=SC2086,SC2091
$(${TERMINAL} ${TERM_OPTS} ${EDITOR} ${NOTESDIR}/${NOW})
+ # add note to git and push to remote
+ gitadd
+}
+backup_data() {
+ BACKUPDIR="$1"
+ echo "backing up data in $BACKUPDIR"
+
+
+ if [ -d $BACKUPDIR ]; then
+ if [ $(/bin/ls -A $BACKUPDIR) ]; then
+ echo "$BACKUPDIR is not empty. Cannot continue"
+ exit
+ else
+ echo "$BACKUPDIR is ok. Continuing!"
+ fi
+ else
+ # BACKUPDIR doesn't exists
+ echo "$BACKUPDIR doesn't exists"
+ read -r -p "Do you want me to create it for you? (y/N) " ANSWER
+ case $ANSWER in
+ y|Y )
+ mkdir -p $BACKUPDIR
+ ;;
+ * )
+ echo "No changes made. Exiting"
+ exit
+ ;;
+ esac
+ fi
+ # ok, we have a backup directory
+ if [ -r $RCFILE ]; then
+ BCKUP_COMM=$(rsync -avz --progress ${RCFILE}* ${BASEDIR}/ ${BACKUPDIR})
+ else
+ BCKUP_COMM=$(rsync -avz --progress ${BASEDIR}/ ${BACKUPDIR})
+ fi
+ # run the command
+ if [ "$BCKUP_COMM" ]; then
+ echo -e "All files backed up."
+ echo -e "BACKUP directory:\t$BACKUPDIR"
+ tree $BACKUPDIR | $PAGER
+ echo; echo "BACKUP COMPLETED"
+ fi
}
-function editnote() {
+
+backup_restore() {
+ BACKUPDIR="$1"
+ echo "restoring backup from $BACKUPDIR"
+ echo "This will overwrite all your notes and configurations with the backup."
+ read -r -p "Do you want to continue? (y/N) " ANSWER
+ case $ANSWER in
+ y|Y )
+ # restoring rc file
+ BACKUPRC=$(basename $RCFILE)
+ if [ -r ${BACKUPDIR}/${BACKUPRC} ]; then
+ if [ -r ${RCFILE} ]; then
+ echo "Backing up current '${RCFILE}'...."
+ mv -f ${RCFILE} ${RCFILE}.$(date +%Y%m%d_%H%M)
+ fi
+ cp --verbose ${BACKUPDIR}/${BACKUPRC} $RCFILE
+ fi
+ # restoring notes directory
+ if [ -d $BACKUPDIR/notes ]; then
+ if [ $(/bin/ls -A $NOTESDIR) ]; then
+ rm --verbose $NOTESDIR/*
+ fi
+ cp -r --verbose $BACKUPDIR/notes $BASEDIR
+ fi
+ # restoring database
+ BACKUPDB=$(basename $DB)
+ if [ -f ${BACKUPDIR}/${BACKUPDB} ]; then
+ if [ -r ${DB} ]; then
+ echo "Backing up current '${DB}'...."
+ mv -f ${DB} ${DB}.$(date +%Y%m%d_%H%M)
+ fi
+ cp --verbose ${BACKUPDIR}/${BACKUPDB} $DB
+ fi
+ # restoring git repo subdirectory
+ if [ -d $BACKUPDIR/.git ]; then
+ if [ /bin/ls -A ${BASEDIR}/.git ]; then
+ rm -rf ${BASEDIR}/.git
+ fi
+ cp -r --verbose ${BACKUPDIR}/.git ${BASEDIR}/
+ fi
+ ;;
+ * )
+ echo "No changes made. Exiting"
+ exit
+ ;;
+ esac
+}
+
+editnote() {
NOTE=$1
# shellcheck disable=SC2155
local OK=$(check_noteID "$NOTE")
echo "editing note $TITLE"
# shellcheck disable=SC2086,SC2091
$(${TERMINAL} ${TERM_OPTS} ${EDITOR} ${NOTESDIR}/${FILE})
+ gitedit
else
echo "note not found"
exit 1
fi
}
-function listnotes() {
+listnotes() {
+ # attempt syncing before listing all notes
+ gitsync
# [ $PLAIN == true ] && echo "output is plain text" || echo "output is colored"
if [[ $(ls -A "$NOTESDIR") ]]; then
if [ $PLAIN == false ]; then
echo "no notes yet. You can add your first one with: ${BASENAME} -a \"your note title\""
fi
}
-function rmnote() {
+rmnote() {
# remove eventually existing temp DB file
if [[ -f $TMPDB ]]; then
rm $TMPDB
mv $TMPDB $DB
# shellcheck disable=SC2086
rm $NOTESDIR/*
+ gitremove "all"
echo "Deleted all notes"
;;
* )
if [ ! "$OK" ]; then
echo "invalid note \"$NOTE\""
echo "Use the note ID that you can fetch after listing your notes"
+ sleep 1
exit 1
fi
# shellcheck disable=SC2086
mv $TMPDB $DB
rm $NOTESDIR/$FILE
+ gitremove $OK $FILE
echo "Deleted note $TITLE"
+ sleep 1
+ exit
else
echo "note not found"
+ sleep 1
exit 1
fi
fi
}
-function shownote() {
+shownote() {
NOTE=$1
# shellcheck disable=SC2155
fi
}
# shellcheck disable=SC2006
-GOPT=$(getopt -o hvpla::e::d::s:: --long help,version,list,plain,userconf,backup::,add::,edit::,delete::,show:: -n 'bash-notes' -- "$@")
+GOPT=$(getopt -o hvplr:a::e:d:s: --long help,version,list,plain,userconf,showconf,sync,restore:,backup:,add::,edit:,delete:,show: -n 'bash-notes' -- "$@")
# shellcheck disable=SC2181
if [ $? != 0 ] ; then helptext >&2 ; exit 1 ; fi
exit
;;
-a | --add )
- case "$2" in
- '' )
- read -r -p "Title: " TITLE
- ;;
- * )
- TITLE=$2
- ;;
- esac
+ TITLE=$2
shift 2
addnote "$TITLE"
+ exit
;;
-e | --edit )
- case "$2" in
- '' )
- read -r -p "Note ID: " NOTE
- ;;
- * )
- NOTE=$2
- ;;
- esac
+ NOTE=$2
shift 2
editnote "$NOTE"
+ exit
;;
-d | --delete )
- case "$2" in
- '' )
- read -r -p "Note ID: " NOTE
- ;;
- * )
- NOTE=$2
- ;;
- esac
+ NOTE=$2
shift 2
rmnote "$NOTE"
+ exit
;;
-s | --show )
- case "$2" in
- '' )
- read -r -p "Note ID: " NOTE
- ;;
- * )
- NOTE=$2
- ;;
- esac
+ NOTE=$2
shift 2
shownote "$NOTE"
+ exit
+ ;;
+ -r | --restore )
+ RDIR=$2
+ shift 2
+ backup_restore $RDIR
+ exit
+ ;;
+ --sync )
+ # I'm forcing it because if you run it manually, chances are that you need to.
+ gitsync -f
+ shift
+ exit
;;
--userconf )
export_config
# shellcheck disable=SC2317
exit
;;
+ --showconf )
+ configtext
+ exit
+ ;;
+ --backup )
+ BDIR=$2
+ shift 2
+ backup_data $BDIR
+ exit
+ ;;
-- )
shift
break
esac
done
-if [ -z $1 ]; then
- helptext
-fi
+for arg; do
+ if [ $(check_noteID $arg) ]; then
+ shownote $arg
+ else
+ helptext
+ exit
+ fi
+done