From 2997c6fc48be5a8b242553e30d2919d276a84200 Mon Sep 17 00:00:00 2001 From: david Date: Thu, 21 Dec 2023 02:13:33 +0100 Subject: [PATCH] added various functions to lib.sh and added ci.sh --- ci.sh | 204 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib.sh | 58 ++++++++++++++-- 2 files changed, 256 insertions(+), 6 deletions(-) create mode 100755 ci.sh diff --git a/ci.sh b/ci.sh new file mode 100755 index 0000000..2acb5ef --- /dev/null +++ b/ci.sh @@ -0,0 +1,204 @@ +#!/usr/bin/env bash + + + ## ## ## ## ## ## ## ## ## ## ## ## ## ## + ## ## + ## ci.sh ## + ## ## + ## A poor man's continuous ## + ## integration system ## + ## ## + ## ## ## ## ## ## ## ## ## ## ## ## ## ## + + +## +## Information +## (don't touch unless you are me) +## + +NAME="ci.sh" +DESCRIPTION="A poor man's continuous integration system" +VERSION="0.1.0" +AUTHOR="david@socialnerds.org" +LICENSE="MIT" +WEBSITE="https://git.socialnerds.org/david/scripts" +CHANGELOG=("[2023-12-20][v0.1.0] Initial version") + + +## +## Configuration +## + +EXECUTABLE="$(basename $0)" +LIBRARIES="lib.sh" +DEPENDENCIES="whoami tr head basename dirname readlink curl git cut nohup ps cat getent" +REQUIRE_ROOT=0 +[[ ! $HOME ]] && HOME=$(getent passwd "$USER" | cut -d: -f6) +SCRIPTS_DIR="$HOME/ci-scripts" +TEMP_DIR="/tmp/ci-repos" + +## +## Functions +## + +# Print help information +function print_help() { + local i=20 + printf "%s\n\n%s\n%b\n\n%s\n" \ + "$DESCRIPTION" "Usage:" "$LIB_BOLD$EXECUTABLE $LIB_CLEAR" "Options:" + printf " %-${i}s %b\n" \ + "-s, --scripts " "Specify the scripts directory (default: $SCRIPTS_DIR)" \ + "-c, --checks " "Enable ${LIB_UNDERLINE}healthchecks.io${LIB_CLEAR} integration" \ + "-n, --ntfy " "Enable ${LIB_UNDERLINE}ntfy.sh${LIB_CLEAR} integration" \ + "-h, --help" "Print help screen and exit" \ + "-i, --info" "Print script information and exit" \ + "-v, --verbose" "More verbose output" \ + "-q, --quiet" "No output except errors (overrides -v)" +} + + +## +## Preflight +## + +# Load BASH libraries +SCRIPT_PATH=$(readlink -f "$0") +SCRIPT_DIR=$(dirname "$SCRIPT_PATH") +for LIBRARY in $LIBRARIES; do + if [[ -r "$SCRIPT_DIR/$LIBRARY" ]]; then + #echo "Loading library file [$SCRIPT_DIR/$LIBRARY]" + source "$SCRIPT_DIR/$LIBRARY" + else + echo "Error: Cannot load library file [$SCRIPT_DIR/$LIBRARY]" + exit 1 + fi +done + +# Check for root privileges +if ! lib_amiroot && [[ $REQUIRE_ROOT -eq 1 ]]; then + lib_print "!You need to have root privileges" + exit 1 +fi + +# Check for dependencies +MISSING_COMMANDS=$(lib_missing_commands $DEPENDENCIES) +if [[ -n "$MISSING_COMMANDS" ]]; then + lib_print "!One or more commands missing [$MISSING_COMMANDS]" + lib_print "Try installing them with your package manager" + exit 1 +fi + + +## +## Liftoff +## + +while [[ "$1" =~ ^- && ! "$1" == "--" ]]; do + case "$1" in + -n|--ntfy) + CURRENT_OPTION="$1" + shift + if lib_is_url "$1"; then + NTFY_URL="$1" + else + lib_print "!$CURRENT_OPTION expects a valid ntfy.sh URL [$1]" + exit 1 + fi + ;; + -c|--checks) + CURRENT_OPTION="$1" + shift + if lib_is_url "$1"; then + CHECKS_URL="$1" + else + lib_print "!$CURRENT_OPTION expects a valid healthchecks.io URL [$1]" + exit 1 + fi + ;; + -s|--scripts) + shift + if lib_is_absolute "$1"; then + SCRIPTS_DIR="$1" + else + SCRIPTS_DIR="$(PWD)/$1" + fi + ;; + -h|--help) + H=1 + ;; + -i|--info) + I=1 + ;; + -v|--verbose) + V=1 + ;; + -q|--quiet) + Q=1 + ;; + *) + lib_print "!Unknown option [$1]" + lib_print "Try --help or -h for available options" + exit 1 + ;; + esac + shift +done + +if [[ "$1" == '--' ]]; then + shift +fi + +if [[ $I -eq 1 ]]; then + lib_print_info + exit 0 +elif [[ $H -eq 1 ]]; then + print_help + exit 0 +fi + +if [[ -z "$1" ]]; then + print_help + exit 0 +else + # Check if a valid Git URL is provided and extract parts + if [[ "$1" =~ ^(git@|https:\/\/)([^\/:]+)[:\/]([^\/]+)\/([^\/]+)(\.git)$ ]]; then + GIT_URL="$1" + GIT_HOST="${BASH_REMATCH[2]}" + GIT_USER="${BASH_REMATCH[3]}" + GIT_REPO="${BASH_REMATCH[4]}" + else + lib_print "!Input does not seem to be a valid Git URL [$1]" + exit 1 + fi + + if [[ ! -d "$TEMP_DIR/$GIT_HOST/$GIT_USER" ]]; then + mkdir -p "$TEMP_DIR/$GIT_HOST/$GIT_USER" + fi + + if [[ -d "$TEMP_DIR/$GIT_HOST/$GIT_USER/$GIT_REPO" ]]; then + # Check if the previous job is still running + if lib_is_running $(cat "$TEMP_DIR/$GIT_HOST/$GIT_USER/$GIT_REPO.pid" 2> /dev/null); then + lib_print "!CI Job is still running [$GIT_HOST/$GIT_USER/$GIT_REPO]" + lib_ntfy "$NTFY_URL" "!CI job is still running [$GIT_HOST/$GIT_USER/$GIT_REPO]" + lib_checks "$CHECKS_URL" "!CI job is still running [$GIT_HOST/$GIT_USER/$GIT_REPO]" + exit 1 # if we exit with 0 the output would show up in the gitea webinterface + else + rm -rf "$TEMP_DIR/$GIT_HOST/$GIT_USER/$GIT_REPO" + fi + fi + + git clone --quiet "$GIT_URL" "$TEMP_DIR/$GIT_HOST/$GIT_USER/$GIT_REPO" + cd "$TEMP_DIR/$GIT_HOST/$GIT_USER/$GIT_REPO" + nohup "$SCRIPTS_DIR/$GIT_HOST/$GIT_USER/$GIT_REPO.sh" > "$TEMP_DIR/$GIT_HOST/$GIT_USER/$GIT_REPO.log" 2>&1 & + echo $! > "$TEMP_DIR/$GIT_HOST/$GIT_USER/$GIT_REPO.pid" + + lib_ntfy "$NTFY_URL" "CI job has been triggered [$GIT_HOST/$GIT_USER/$GIT_REPO]" + lib_checks "$CHECKS_URL" "CI job has been triggered [$GIT_HOST/$GIT_USER/$GIT_REPO]" +fi + + +## +## Here be dragons +## + +exit 0 diff --git a/lib.sh b/lib.sh index b9ebd49..3b46a32 100644 --- a/lib.sh +++ b/lib.sh @@ -76,6 +76,10 @@ LIB_LIGHTGREY_BG_BOLD="\e[1;47m" ## Functions ## +# Function aliases +#function lib_message() { lib_print "$@"; } +function lib_checks() { lib_healthchecks $@; } + # Print text message function lib_print() { local LIB_Q=${Q:-0} @@ -135,19 +139,32 @@ function lib_gen_string() { } # Contact Healthchecks -# The first argument($1) must be the URL everything after that gets transmitted as log message +# The first argument($1) must be the URL everything else gets transmitted as message function lib_healthchecks() { - if [[ "$2" ]]; then + if [[ "$2" ]] && lib_is_url "$1"; then curl -fsS -m 10 --retry 5 --data-raw "${*:2}" -o /dev/null "$1" - elif [[ "$1" ]]; then + elif [[ "$1" ]] && lib_is_url "$1"; then curl -fsS -m 10 --retry 5 -o /dev/null "$1" fi } +# Send a notification to ntfy.sh +# The first argument($1) must be the URL everything else gets transmitted as message +function lib_ntfy() { + MESSAGE=${*:2} + if [[ "$2" ]] && lib_is_url "$1"; then + if [[ "$2" =~ ^\! ]]; then + curl -fsS -m 10 --retry 5 -H "Title: ${NAME:-lib_ntfy}" -H "Tags: red_circle" -d "${MESSAGE#\!}" -o /dev/null "$1" + else + curl -fsS -m 10 --retry 5 -H "Title: ${NAME:-lib_ntfy}" -H "Tags: green_circle" -d "$MESSAGE" -o /dev/null "$1" + fi + fi +} + # Verify if input is an intager function lib_is_int { local INT_EXPR='^[0-9]+$' - if ! [[ $1 =~ $INT_EXPR ]]; then + if ! [[ "$1" =~ $INT_EXPR ]]; then return 1 fi } @@ -155,7 +172,7 @@ function lib_is_int { # Verify if input is alphanumeric function lib_is_alnum { local ALNUM_EXPR='^[a-zA-Z0-9]+$' - if ! [[ $1 =~ $ALNUM_EXPR ]]; then + if ! [[ "$1" =~ $ALNUM_EXPR ]]; then return 1 fi } @@ -163,7 +180,36 @@ function lib_is_alnum { # Verify if input looks like an URL function lib_is_url { local URL_EXPR='^https?://.+$' - if ! [[ $1 =~ $URL_EXPR ]]; then + if ! [[ "$1" =~ $URL_EXPR ]]; then + return 1 + fi +} + +# Verify if input is an existing directory +function lib_is_dir { + if ! [[ -d "$1" ]]; then + return 1 + fi +} + +# Verify if input is an absolute path +function lib_is_absolute { + if ! [[ "$1" == /* ]]; then + return 1 + fi +} + +# Verify if input is a relative path +function lib_is_relative { + if [[ "$1" == /* ]]; then + return 1 + fi +} + +# Verify if PID (still) exists +function lib_is_running { + ps -p $1 > /dev/null + if [[ $? -ne 0 ]]; then return 1 fi }