diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..496ee2c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store \ No newline at end of file diff --git a/README.md b/README.md index 75426cf..03628e6 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,25 @@ -# commonCore -A collection of Bash scripts/utilities that one can source in their own bash scripts. +# commonCoreSh +A collection of Bash scripts/utilities that one can source in their own bash scripts. This feature set is currently UNSTABLE and is not recommended for use in production. + +# HOW TO USE +Use at your own direction, so long it is NOT utilized in closed source applications. + +Make sure you download the repository as is, and to not change any files in the resulting directories. +If you want to take advantage of these scripts, source `sourcecore.sh`, or consider sourcing it in your profile via `source sourcecore.sh` + +# FEATURES +## comphash +comphash performs an md5sum comparison between two files. It will not perform this operation if it detects either argument as a directory. + +## dutil (docker util) +The most useful utility so far. Performs some docker compose based actions, such as bouncing a service, building then bouncing, or allowing for easier container ps searching. +Might be ported over as it's own standalone package. maybe not. + +One of the most useful is the shortcut of `dutil shell [container name]`, which does the equivalent of `docker exec -it [container name] /bin/bash`. If the container does not support /bin/bash, it will fallback to using /bin/sh instead. + +## initdocker +initdocker creates a couple of Docker files on your behalf, such as a skeleton Compose file, an empty Dockerfile, and some starter Bash scripts. +If you're intimately familiar with Docker, you probably won't need to use the bash scripts and can rely on the functions `dutil` offers. + +## logging +Sourcing the library will let you utilize a couple logging notations in your scripts diff --git a/bin/README.md b/bin/README.md new file mode 100644 index 0000000..52904e0 --- /dev/null +++ b/bin/README.md @@ -0,0 +1,3 @@ +Files under this directory get installed into /usr/local/bin + +HIGHLY HIGHLY HIGHLY UNSTABLE FOR NOW \ No newline at end of file diff --git a/bin/dutil.go b/bin/dutil.go new file mode 100644 index 0000000..48124fc --- /dev/null +++ b/bin/dutil.go @@ -0,0 +1,159 @@ +package main + +import ( + "fmt" + "os" + "os/exec" + "strings" +) + +func main() { + if len(os.Args) < 2 { + usage() + os.Exit(2) + } + + // Check if docker compose is installed + if _, err := exec.Command("docker", "compose", "version").CombinedOutput(); err != nil { + fmt.Println("Docker compose v2 is not installed") + os.Exit(1) + } + + command := os.Args[1] + switch command { + case "net", "network", "networks": + runCommand("docker", "network", "ls") + + case "ps": + if len(os.Args) < 3 { + runCommand("docker", "ps", "-a") + } else { + containerName := os.Args[2] + runCommand("docker", "ps", "-a", "-f", "name="+containerName) + } + + case "psg": + if len(os.Args) < 3 { + runCommand("docker", "ps", "-a") + } else { + searchTerm := os.Args[2] + output, err := exec.Command("docker", "ps", "-a").Output() + if err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + + lines := strings.Split(string(output), "\n") + for _, line := range lines { + if strings.Contains(line, searchTerm) { + fmt.Println(line) + } + } + } + + case "rebuild": + runCommandSequence( + []string{"docker", "compose", "down"}, + []string{"docker", "compose", "build", "."}, + []string{"docker", "compose", "up", "-d"}, + ) + + case "reload": + composeFile := "" + if len(os.Args) > 2 { + composeFile = os.Args[2] + runCommandSequence( + []string{"docker", "compose", "-f", composeFile, "down"}, + []string{"docker", "compose", "-f", composeFile, "up", "-d"}, + ) + } else { + runCommandSequence( + []string{"docker", "compose", "down"}, + []string{"docker", "compose", "up", "-d"}, + ) + } + + case "shell": + if len(os.Args) < 3 { + usage() + fmt.Println("Error: Missing container name") + os.Exit(1) + } + + containerName := os.Args[2] + // Try bash first + cmd := exec.Command("docker", "exec", containerName, "/bin/bash") + if err := cmd.Run(); err == nil { + fmt.Println("Starting /bin/bash in", containerName) + runInteractiveCommand("docker", "exec", "-it", containerName, "/bin/bash") + } else { + // Try sh as fallback + cmd = exec.Command("docker", "exec", containerName, "/bin/sh") + if err := cmd.Run(); err == nil { + fmt.Println("Warning:", containerName, "does not support /bin/bash, using /bin/sh") + runInteractiveCommand("docker", "exec", "-it", containerName, "/bin/sh") + } else { + fmt.Println("Error: Unable to spawn shell session for", containerName) + os.Exit(1) + } + } + + case "upgrade": + runCommandSequence( + []string{"docker", "compose", "down"}, + []string{"docker", "compose", "pull"}, + []string{"docker", "compose", "up", "-d"}, + ) + fmt.Println("Complete") + + default: + usage() + os.Exit(2) + } +} + +func usage() { + fmt.Println("usage: dutil [reload|rebuild|networks|ps|psg] [container name]") + fmt.Println(" net|network|networks\n\t\t Returns the list of docker networks") + fmt.Println(" ps|psg\n\t\t If given a container name, perform a search for it. psg performs a grep instead against docker ps -a") + fmt.Println(" rebuild\n\t\t Performs a docker compose down, build, and up, detatched") + fmt.Println(" reload\n\t\t Performs a docker compose down and up, detatched") + fmt.Println(" shell\n\t\t Runs docker exec -it against a given container name and opens a bash shell (as fallback, use /bin/sh).") + fmt.Println("Part of the _subtype common library") +} + +func runCommand(command string, args ...string) { + cmd := exec.Command(command, args...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + if err := cmd.Run(); err != nil { + fmt.Println("Error executing command:", err) + os.Exit(1) + } +} + +func runInteractiveCommand(command string, args ...string) { + cmd := exec.Command(command, args...) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + if err := cmd.Run(); err != nil { + fmt.Println("Error executing interactive command:", err) + os.Exit(1) + } +} + +func runCommandSequence(commands ...[]string) { + for _, cmdArgs := range commands { + cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + if err := cmd.Run(); err != nil { + fmt.Println("Error executing command sequence:", err) + os.Exit(1) + } + } +} \ No newline at end of file diff --git a/bin/dutil.sh b/bin/dutil.sh new file mode 100755 index 0000000..f94ceaf --- /dev/null +++ b/bin/dutil.sh @@ -0,0 +1,120 @@ +#!/bin/bash +# Author: Andrew Subowo +# docker compose based utils -- these can be translated into aliases. It's not that fancy. +# These are things that I somewhat do on a more frequent basis. +# @_subtype / subtype / 2024 + +# TODO: Figure out how to determine if this was called via source or not or blah blah blah +# # If this script is interrupted, just return the user to the pwd they were in +# INITDIR=$(pwd) +# trap "cd $INITDIR" EXIT + + +function usage() { + echo "usage: dutil [reload|rebuild|networks|ps|psg] [container name]" + echo -e " net|network|networks\n\t\t Returns the list of docker networks" + echo -e " ps|psg\n\t\t If given a container name, perform a search for it. psg performs a grep instead against docker ps -a" + echo -e " rebuild\n\t\t Performs a docker compose down, build, and up, detatched" + echo -e " reload\n\t\t Performs a docker compose down and up, detatched" + echo -e " shell\n\t\t Runs docker exec -it against a given container name and opens a bash shell (as fallback, use /bin/sh)." + echo "Part of the _subtype common library" + exit 2 +} + +# TODO: EVENTUALLY MAKE THIS COMPLETELY STANDALONE(?) Should be able to detect if core is installed or not +# Shouldn't have to enforce utilization of the common core, but eh +# Check and source some methods that are included as part of the common core +function checkAndSourceCommonCore() { + # If the installer was used, the common library should be symlinked/available + COMMON_CORE_INSTALL="$( cd $(dirname $(dirname "$(readlink -f "$0")")) && pwd)" + if [ ! -d $COMMON_CORE_INSTALL ]; then + echo "Could not locate the common core library at $COMMON_CORE_INSTALL. Please consider re-pulling the repository" + exit 1 + else + source $COMMON_CORE_INSTALL/utils/logging.sh + source $COMMON_CORE_INSTALL/utils/corefunc.sh + source $COMMON_CORE_INSTALL/utils/dockerinit.sh + fi +} + +# Main utility function +# Wrap it all into a function in case someone somehow partially downloads the file +function dutil() { + # Get current pwd + checkAndSourceCommonCore + CHECK_COMMANDS dockerinit warn ok error + + if [ -z $1 ]; then + usage + fi + + # TODO: Add v1 support, maybe. + if ! docker compose version &> /dev/null; then + echo "Docker compose v2 is not installed" + return 1 + fi + + case $1 in + init) + if ! command -v dockerinit &> /dev/null; then + echo "dockerinit is not present" + return 1 + else + dockerinit + fi + ;; + net|network|networks) + docker network ls + ;; + ps) + if [ -z $2 ]; then + docker ps -a + else + docker ps -a -f name=$2 + fi + ;; + psg) + if [ -z $2 ]; then + docker ps -a + else + docker ps -a | grep $2 + fi + ;; + rebuild) + #TODO: See if compose file has a build context? + docker compose down && docker compose build . && docker compose up -d + ;; + reload) + if [ ! -z $2 ]; then #docker compose up and down a given service + docker compose -f $2 down && docker compose -f $2 up -d + else + docker compose down && docker compose up -d + fi + ;; + shell) + if [ -z $2 ]; then + usage + error "Missing container name" + else + if docker exec $2 /bin/bash > /dev/null 2>&1; then + ok "Starting /bin/bash in $2" + docker exec -it $2 /bin/bash + elif docker exec $2 /bin/sh > /dev/null 2>&1; then + warn "$2 does not support /bin/bash, using /bin/sh" + docker exec -it $2 /bin/sh + else + error "Unable to spawn shell session for $2" + return 1 + fi + fi + ;; + upgrade) + docker compose down && docker compose pull && docker compose up -d && ok "Complete" + ;; + *) + usage + ;; + esac +} + +dutil "$@" \ No newline at end of file diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..df04d60 --- /dev/null +++ b/install.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# Author: Andrew Subowo +# @_subtype / subtype / 2024 +# Installation script to install dutil into /usr/local/bin + + +# Install dutil via a symlink to ./bin/dutil.sh is here. That way we can also sorta kinda maybe import the other helper utils +echo "Installing docker utilities" +echo "Creating 'dutil' symlink under /usr/local/bin" +## TODO: Check for updates? +# Get directory where this file is currently +SCRIPTPATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +ln -sf $SCRIPTPATH/bin/dutil.sh /usr/local/bin/dutil && echo "Installed as dutil under /usr/local/bin/dutil" || echo "There was an error creating the symlink" && exit 1 + +# Not going to fix this right now +# echo "Running aliases" + +# if [[ -x $SCRIPTPATH/setupshell.sh ]]; then +# . $SCRIPTPATH/setupshell.sh +# else +# echo "Couldn't find the setupshell script, or the file isn't executable" +# fi \ No newline at end of file diff --git a/setupshell.sh b/setupshell.sh new file mode 100755 index 0000000..7e8a1fb --- /dev/null +++ b/setupshell.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# Script to init a shell with some handy aliases, if they haven't already + +alias please='sudo' +alias mkdir='mkdir -pv' +alias mount='mount | column -t' +alias showports='netstat -nulpta' diff --git a/sourcecore.sh b/sourcecore.sh new file mode 100755 index 0000000..bfa61db --- /dev/null +++ b/sourcecore.sh @@ -0,0 +1,91 @@ +#!/bin/bash +# Author: Andrew Subowo +# Some common core scripts I use on a day to day basis that I find helpful +# @_subtype / subtype / 2023 +# This script sources the _subtype common core bash library + +# If this script is interrupted, just return the user to the pwd they were in +INITDIR=$(pwd) +trap "cleanup; cd $INITDIR" EXIT + +# "Verbose" mode +VERBOSE=false + +# Get directory where this file is currently +SCRIPTPATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +cd $SCRIPTPATH + +function CCQUIT() { + if [ $# -eq 0 ]; then + RC=1 #Default fail state + else + RC=$1 + fi + + if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then + # Sourced + return $RC + else + # Not sourced, possibly running in sub-shell + exit $RC + fi +} + +# Check for updates, don't do any auto update, let the user perform it. +function checkUpdate() { + if $VERBOSE; then + echo "Checking for updates..." + fi + UPSTREAM=${1:-'@{u}'} + LOCAL=$(git rev-parse @) + REMOTE=$(git rev-parse "$UPSTREAM") + BASE=$(git merge-base @ "$UPSTREAM") + if [ $LOCAL = $REMOTE ]; then + : # Do nothing no-op + elif [ $LOCAL = $BASE ]; then + echo "An update is available! Please update the repository via git fetch, then git pull." + fi +} + +# main function +function main() { + if [ ! -d $SCRIPTPATH/bash ]; then + echo "_subtype Common Core bash library not found or malformed." + fi + + if [ -d $SCRIPTPATH/utils ]; then + for FILE in $SCRIPTPATH/utils/*; do + # Source files in the current shell, not a sub-shell + . "$FILE" + if $VERBOSE; then + echo "Sourced $FILE" + fi + done + else + echo "Bash utils directory not found for sourcing." + fi +} + +function cleanup() { + unset VERBOSE + unset SCRIPTPATH + unset UPSTREAM + unset LOCAL + unset REMOTE + unset BASE +} + +while getopts "v" OPTS; do + case $OPTS in + v) + VERBOSE=true + ;; + *) + echo "Unsupported option" + exit 0 + ;; + esac +done + +checkUpdate +main \ No newline at end of file diff --git a/tests/utils/compmd_test.sh b/tests/utils/compmd_test.sh index 757aecf..2bac5f3 100755 --- a/tests/utils/compmd_test.sh +++ b/tests/utils/compmd_test.sh @@ -19,14 +19,20 @@ source ../../utils/logging.sh echo "Testing file comparison, shell, empty" if comphash ./test1.txt ./test2.txt; then ok +else + fail fi echo "Testing file comparison, shell, differing" if ! comphash ./test1.txt ./test3.txt; then ok +else + fail fi echo "Testing two files with different extensions" if comphash ./test1.txt ./test4.sh; then ok +else + fail fi \ No newline at end of file diff --git a/utils/background.sh b/utils/background.sh new file mode 100644 index 0000000..8b66ca7 --- /dev/null +++ b/utils/background.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# Author: Andrew Subowo +# Some common core scripts I use on a day to day basis that I find helpful +# @_subtype / subtype / 2023 +# To use the functions in this script, merely source it (e.g. source