diff --git a/.example/localhost/_server.env b/.example/localhost/_server.env new file mode 100644 index 0000000..2f15e36 --- /dev/null +++ b/.example/localhost/_server.env @@ -0,0 +1,5 @@ +SSH_ADDRESS=localhost +SSH_PORT=22 +SSH_USER=$(whoami) + + diff --git a/.example/localhost/squashkiwi.env b/.example/localhost/squashkiwi.env new file mode 100644 index 0000000..17d4d32 --- /dev/null +++ b/.example/localhost/squashkiwi.env @@ -0,0 +1,16 @@ +# Application settings +CONTAINER_PORT=8181 +HOST_PORT=80 + +# Deployment settings +DEPLOY_FOLDER="${HOME}/sk/deploy" +DATA_FOLDER="${HOME}/sk/data" +BACKUP_FOLDER="${HOME}/sk/backups" +CONTAINER_NAME="squashkiwi" + +# Image settings +IMAGE_REGISTRY="gitea.jde.nz" +IMAGE_REPO="squashkiwi/squashkiwi" +IMAGE_TAG="latest" + + diff --git a/LICENSE b/LICENSE index cedb72e..062ed47 100644 --- a/LICENSE +++ b/LICENSE @@ -1,37 +1,2 @@ -Copyright (c) 2001 David Giffin. -All rights reserved. +MIT License -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in -the documentation and/or other materials provided with the -distribution. - -3. All advertising materials mentioning features or use of this -software must display the following acknowledgment: -"This product includes software developed by -David Giffin ." - -4. Redistributions of any form whatsoever must retain the following -acknowledgment: -"This product includes software developed by -David Giffin ." - -THIS SOFTWARE IS PROVIDED BY DAVID GIFFIN ``AS IS'' AND ANY -EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID GIFFIN OR -ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED -OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..11117de --- /dev/null +++ b/install.sh @@ -0,0 +1,105 @@ +#!/bin/bash + +# Colors for output +GREEN='\033[0;32m' +RED='\033[0;31m' +NC='\033[0m' # No Color + +# Check if USER_DEFINITIONS is provided +if [ -z "$1" ]; then + echo -e "${RED}Error: USER_DEFINITIONS path is required${NC}" + echo "Usage: $0 " + echo "Example: $0 ~/.config/dropshell" + exit 1 +fi + +USER_DEFINITIONS="$1" + +# Script locations +SCRIPT_SOURCE="$(pwd)/src/dropshell.sh" +SCRIPT_DEST="/usr/local/bin/dropshell" +COMPLETION_SOURCE="$(pwd)/src/dropshell-completion.bash" +COMPLETION_DEST="/etc/bash_completion.d/dropshell" + +# Check if running with sudo/root +if [ "$EUID" -ne 0 ]; then + echo -e "${RED}Please run as root or with sudo${NC}" + exit 1 +fi + +# Check if source script exists +if [ ! -f "$SCRIPT_SOURCE" ]; then + echo -e "${RED}Error: $SCRIPT_SOURCE not found${NC}" + echo "Please ensure the script exists in the src directory" + exit 1 +fi + +# Check if completion script exists +if [ ! -f "$COMPLETION_SOURCE" ]; then + echo -e "${RED}Error: $COMPLETION_SOURCE not found${NC}" + echo "Please ensure the completion script exists in the src directory" + exit 1 +fi + +if [ ! -d "$USER_DEFINITIONS" ]; then + # Create user definitions directory structure + echo "Creating user definitions directory structure..." + mkdir -p "$USER_DEFINITIONS/servers" + mkdir -p "$USER_DEFINITIONS/templates" + + # Create example files + mkdir -p "$USER_DEFINITIONS/servers/example.com" + + cat > "$USER_DEFINITIONS/services/example.com/server.json" << 'EOF' + { + "ssh_address": "localhost", + "ssh_port": 22, + "ssh_user": "$(whoami)" + } + EOF + + # cat > "$USER_DEFINITIONS/services/example.com/server.json" << 'EOF' + # { + # "ssh_address": "localhost", + # "ssh_port": 22, + # "ssh_user": "$(whoami)" + # } + # EOF +fi + +# Create symbolic link +echo "Creating symbolic link..." +if [ -L "$SCRIPT_DEST" ]; then + rm "$SCRIPT_DEST" +fi +ln -s "$SCRIPT_SOURCE" "$SCRIPT_DEST" + +# Make sure the scripts are executable +chmod +x "$SCRIPT_SOURCE" +chmod +x "$COMPLETION_SOURCE" + +# Install bash completion +echo "Installing bash completion..." +if [ ! -d "/etc/bash_completion.d" ]; then + mkdir -p "/etc/bash_completion.d" +fi + +# Install completion script +cp "$COMPLETION_SOURCE" "$COMPLETION_DEST" +chmod 644 "$COMPLETION_DEST" + +# Create environment file +cat > "/etc/dropshell.conf" << EOF +# Dropshell configuration +USER_DEFINITIONS="$USER_DEFINITIONS" +EOF + +# Source the completion script +echo "source $COMPLETION_DEST" >> ~/.bashrc + +echo -e "${GREEN}Installation completed successfully!${NC}" +echo "User definitions directory created at: $USER_DEFINITIONS" +echo +echo "Please run 'source ~/.bashrc' or start a new terminal to enable completion" + +exit 0 diff --git a/src/dropshell-completion.bash b/src/dropshell-completion.bash new file mode 100755 index 0000000..7feb809 --- /dev/null +++ b/src/dropshell-completion.bash @@ -0,0 +1,30 @@ +#!/bin/bash + +_dropshell_completions() { + local cur prev opts + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + + # List of main commands + opts="help version status" + + # If we're completing the first argument, show all commands + if [[ ${COMP_CWORD} -eq 1 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + fi + + # Command-specific completions + case "${prev}" in + status) + # No additional completions for status + COMPREPLY=() + ;; + *) + ;; + esac +} + +# Register the completion function +complete -F _dropshell_completions dropshell \ No newline at end of file diff --git a/src/dropshell.sh b/src/dropshell.sh new file mode 100755 index 0000000..9b43cbe --- /dev/null +++ b/src/dropshell.sh @@ -0,0 +1,142 @@ +#!/bin/bash + +# Script name: dropshell.sh +# Description: A basic shell script for common operations + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Source version information +SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" +source "$SCRIPT_DIR/version.sh" + +# Source configuration +if [ -f "/etc/dropshell.conf" ]; then + source "/etc/dropshell.conf" +else + echo -e "${RED}Error: Configuration file not found${NC}" + echo "Please run the installation script first" + exit 1 +fi + +# Check if USER_DEFINITIONS is set +if [ -z "$USER_DEFINITIONS" ]; then + echo -e "${RED}Error: USER_DEFINITIONS not set${NC}" + echo "Please run the installation script first" + exit 1 +fi + +# Function to print usage +print_usage() { + echo "Usage: $0 [options]" + echo + echo "Commands:" + echo " help - Show this help message" + echo " version - Show version information" + echo " status - Check system status" + echo " servers - List configured servers" + echo " services - List configured services" + echo " templates - List available templates" + echo + echo "Options:" + echo " -v, --verbose Enable verbose output" +} + +# Function to print version +print_version() { + get_version_info +} + +# Function to check status +check_status() { + echo -e "${GREEN}System Status:${NC}" + echo "Date: $(date)" + echo "Uptime: $(uptime)" + echo "Memory Usage:" + free -h + echo + echo "Disk Usage:" + df -h / +} + +# Function to list servers +list_servers() { + echo -e "${GREEN}Configured Servers:${NC}" + if [ -d "$USER_DEFINITIONS/servers" ]; then + for server in "$USER_DEFINITIONS/servers"/*.json; do + if [ -f "$server" ]; then + name=$(jq -r '.name' "$server") + host=$(jq -r '.host' "$server") + echo "- $name ($host)" + fi + done + else + echo "No servers configured" + fi +} + +# Function to list services +list_services() { + echo -e "${GREEN}Configured Services:${NC}" + if [ -d "$USER_DEFINITIONS/services" ]; then + for service in "$USER_DEFINITIONS/services"/*.json; do + if [ -f "$service" ]; then + name=$(jq -r '.name' "$service") + echo "- $name" + fi + done + else + echo "No services configured" + fi +} + +# Function to list templates +list_templates() { + echo -e "${GREEN}Available Templates:${NC}" + if [ -d "$USER_DEFINITIONS/templates" ]; then + for template in "$USER_DEFINITIONS/templates"/*.sh; do + if [ -f "$template" ]; then + echo "- $(basename "$template")" + fi + done + else + echo "No templates available" + fi +} + +# Main script logic +case "$1" in + "help"|"-h"|"--help") + print_usage + ;; + "version"|"-V"|"--version") + print_version + ;; + "status") + check_status + ;; + "servers") + list_servers + ;; + "services") + list_services + ;; + "templates") + list_templates + ;; + "") + echo -e "${RED}Error: No command provided${NC}" + print_usage + exit 1 + ;; + *) + echo -e "${RED}Error: Unknown command '$1'${NC}" + print_usage + exit 1 + ;; +esac + +exit 0 \ No newline at end of file diff --git a/src/version.sh b/src/version.sh new file mode 100644 index 0000000..59f76bd --- /dev/null +++ b/src/version.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# Version information +DROPSHELL_VERSION="1.0.0" +DROPSHELL_RELEASE_DATE="2025-04-21" +DROPSHELL_AUTHOR="j842" +DROPSHELL_LICENSE="MIT" + +# Function to get full version information +get_version_info() { + echo "dropshell version $DROPSHELL_VERSION" + echo "Release date: $DROPSHELL_RELEASE_DATE" + echo "Author: $DROPSHELL_AUTHOR" + echo "License: $DROPSHELL_LICENSE" +} \ No newline at end of file diff --git a/templates/squashkiwi/_common.sh b/templates/squashkiwi/_common.sh new file mode 100755 index 0000000..7c9150b --- /dev/null +++ b/templates/squashkiwi/_common.sh @@ -0,0 +1,127 @@ +#!/bin/bash + +# Print error message and exit with code 1 +# Usage: die "error message" +die() { + echo -e "\033[91mError: $1\033[0m" + exit 1 +} + +# Load environment variables from .env file +# Usage: load_env [path_to_env_file] +# If no path is provided, looks for .env in the same directory as the script +load_env() { + local script_dir="$(dirname "${BASH_SOURCE[0]}")" + local env_file + + if [ -z "$1" ]; then + echo "Usage: $0 [path_to_env_file]" + return 1 + else + # If path is relative, make it absolute using script directory as base + if [[ "$1" != /* ]]; then + env_file="$script_dir/$1" + else + env_file="$1" + fi + fi + + if [ -f "$env_file" ]; then + set -a + source "$env_file" + set +a + else + echo "Warning: .env file not found at $env_file" + return 1 + fi +} + +grey_start() { + echo -e -n "\033[90m" +} + +grey_end() { + echo -e -n "\033[0m" +} + +# Test if Docker is installed and working on the local machine +# Returns 0 if Docker is working, 1 if there are any issues +test_docker() { + echo "Testing Docker on local machine..." + + # Test Docker installation + if ! command -v docker >/dev/null 2>&1; then + die "Docker is not installed on this machine" + fi + + # Test Docker daemon is running + if ! docker info >/dev/null 2>&1; then + die "Docker daemon is not running" + fi + + # Test Docker can run containers + if ! docker run --rm hello-world >/dev/null 2>&1; then + die "Docker cannot run containers" + fi + + echo "Docker is working correctly on local machine" + return 0 +} + + +start_container() { + # Check if container exists and is stopped + echo "Checking container status..." + if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then + if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then + die "Container ${CONTAINER_NAME} is already running" + else + echo "Starting existing container ${CONTAINER_NAME}..." + grey_start + if ! docker start "${CONTAINER_NAME}"; then + die "Failed to start container ${CONTAINER_NAME}" + fi + grey_end + fi + else + echo "Creating and starting new container ${CONTAINER_NAME}..." + grey_start + docker run -d \ + --restart unless-stopped \ + --name ${CONTAINER_NAME} \ + -p ${HOST_PORT}:${CONTAINER_PORT} \ + -v ${DATA_FOLDER}:/skdata \ + ${IMAGE_REGISTRY}/${IMAGE_REPO}:${IMAGE_TAG} + grey_end + fi + + # check if the container is running + if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then + die "Container ${CONTAINER_NAME} is not running" + fi + + # print the container id + ID=$(docker ps --format '{{.ID}}' --filter "name=^${CONTAINER_NAME}$") + echo "Container ${CONTAINER_NAME} is running with ID ${ID}" + + return 0 +} + +stop_container() { + # Check if container is running + echo "Checking if container is running..." + if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then + echo "Stopping container ${CONTAINER_NAME}..." + grey_start + if ! docker stop "${CONTAINER_NAME}"; then + die "Failed to stop container ${CONTAINER_NAME}" + fi + grey_end + echo "Container ${CONTAINER_NAME} stopped successfully" + return 0 + else + echo "Container ${CONTAINER_NAME} is not running" + return 1 + fi +} + diff --git a/templates/squashkiwi/backup.sh b/templates/squashkiwi/backup.sh new file mode 100755 index 0000000..d668ff8 --- /dev/null +++ b/templates/squashkiwi/backup.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Source common functions +source "$(dirname "$0")/_common.sh" + +# Load environment variables +load_env "$1" || die "Failed to load environment variables" + +# set the backup file name to be the current date and time +BACKUP_FILE="$BACKUP_FOLDER/backup-squashkiwi-$(date +%Y-%m-%d_%H-%M-%S).tar.gz" + +if [ -f "$BACKUP_FILE" ]; then + echo "Backup file $BACKUP_FILE already exists" + exit 1 +fi + +stop_container + +# create the backup file, using relative paths. +tar zcvf "$BACKUP_FILE" -C "$DATA_FOLDER" . || die "Failed to create backup" + +start_container + +echo "Backup created in $BACKUP_FILE" diff --git a/templates/squashkiwi/example.env b/templates/squashkiwi/example.env new file mode 100644 index 0000000..9170268 --- /dev/null +++ b/templates/squashkiwi/example.env @@ -0,0 +1,20 @@ +# This file contains environment variables specific to the server where the application will be deployed. +# Copy this file to .env and modify the values according to your server's configuration. +# Do not commit the actual .env file to version control as it may contain sensitive information. + +# Application settings +CONTAINER_PORT=8181 +HOST_PORT=80 + +# Deployment settings +DEPLOY_FOLDER="${HOME}/sk/deploy" +DATA_FOLDER="${HOME}/sk/data" +BACKUP_FOLDER="${HOME}/sk/backups" +CONTAINER_NAME="squashkiwi" + +# Image settings +IMAGE_REGISTRY="gitea.jde.nz" +IMAGE_REPO="squashkiwi/squashkiwi" +IMAGE_TAG="latest" + + diff --git a/templates/squashkiwi/install.sh b/templates/squashkiwi/install.sh new file mode 100755 index 0000000..68524f2 --- /dev/null +++ b/templates/squashkiwi/install.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Source common functions +source "$(dirname "$0")/_common.sh" + +# Load environment variables +load_env "$1" || exit 1 + +# Test Docker +if ! test_docker; then + echo "Docker test failed, aborting installation..." + exit 1 +fi + +# Rest of your install script here + +function create_folder() { + local folder="$1" + if ! mkdir -p "$folder"; then + die "Failed to create folder: $folder" + fi +} + +# Create deploy and data folders +echo "Ensuring deployment folders exist..." +create_folder "$DEPLOY_FOLDER" +create_folder "$DATA_FOLDER" +create_folder "$BACKUP_FOLDER" + +# check can pull image on remote host and exit if fails +if ! docker pull "$IMAGE_REGISTRY/$IMAGE_REPO:$IMAGE_TAG"; then + echo "Failed to pull image $IMAGE_REGISTRY/$IMAGE_REPO:$IMAGE_TAG" + exit 1 +fi +echo "Successfully pulled the docker image from the registry" + diff --git a/templates/squashkiwi/start.sh b/templates/squashkiwi/start.sh new file mode 100755 index 0000000..151cf07 --- /dev/null +++ b/templates/squashkiwi/start.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Source common functions +source "$(dirname "$0")/_common.sh" + +# Load environment variables +load_env "$1" || die "Failed to load environment variables" + +start_container || die "Failed to start container ${CONTAINER_NAME}" + +grey_start +docker logs --tail 20 "${CONTAINER_NAME}" +grey_end \ No newline at end of file diff --git a/templates/squashkiwi/stop.sh b/templates/squashkiwi/stop.sh new file mode 100755 index 0000000..53d8164 --- /dev/null +++ b/templates/squashkiwi/stop.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Source common functions +source "$(dirname "$0")/_common.sh" + +# Load environment variables +load_env "$1" || die "Failed to load environment variables" + +stop_container || die "Failed to stop container ${CONTAINER_NAME}" + +grey_start +docker logs --tail 20 "${CONTAINER_NAME}" +grey_end \ No newline at end of file diff --git a/templates/squashkiwi/update.sh b/templates/squashkiwi/update.sh new file mode 100755 index 0000000..2903dfa --- /dev/null +++ b/templates/squashkiwi/update.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# Source common functions +source "$(dirname "$0")/_common.sh" + +# Load environment variables +load_env "$1" || exit 1 + +# check can pull image on remote host and exit if fails +if ! docker pull "$IMAGE_REGISTRY/$IMAGE_REPO:$IMAGE_TAG"; then + echo "Failed to pull image $IMAGE_REGISTRY/$IMAGE_REPO:$IMAGE_TAG" + exit 1 +fi +echo "Successfully pulled the docker image from the registry" + +# stop the old container +stop_container + +# remove the old container +grey_start +if ! docker rm "$CONTAINER_NAME"; then + echo "Failed to remove container $CONTAINER_NAME" + exit 1 +fi +grey_end +echo "Successfully removed the old container" + +# start the new container +start_container +