diff --git a/.config.example b/.config.example new file mode 100644 index 0000000..ae8b803 --- /dev/null +++ b/.config.example @@ -0,0 +1,5 @@ +SSH_ADDR=example.com +SSH_USER=zpkg +HTTP_ADDR=example.com +HTTP_PATH=zpkg +PKG_PATH='$HOME/pkg' diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..df1d12e --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.config +pkg +Zmakefile +autodeploy.sh +.deploytime diff --git a/server_deploy.sh b/server_deploy.sh new file mode 100755 index 0000000..bb88263 --- /dev/null +++ b/server_deploy.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +. "$(pwd)/.config" + +ssh="$SSH_USER@$SSH_ADDR" + +# add sources to server +ssh "$ssh" mkdir -p "$PKG_PATH" || exit $? +scp .config scripts/* "$ssh":~/ || exit $? + +# make zpkg package +DIR=tmp +PKG=zpkg +DEST=/usr/local/bin +BASHDEST=/etc/bash_completion.d +mkdir -p "$DIR/$PKG$DEST" || exit $? +mkdir -p "$DIR/$PKG$BASHDEST" || exit $? +cp src/zpkg.bash "$ZPKG_PKG_PATH/$PKG$BASHDEST" || exit $? +cp src/zpkg "$ZPKG_PKG_PATH/$PKG$DEST" || exit $? +cd pkg/zpkg || exit $? +tar -cvJf zpkg.tar.xz * || exit $? +# send package +scp zpkg.tar.xz "$ssh":~/"$PKG_PATH" || exit $? +rm -rd "$DIR" +# update database +ssh "$ssh" sh database_update.sh || exit $? +# generate install script +ssh "$ssh" sh gen_install.sh || exit $? diff --git a/src/zpkg b/src/zpkg new file mode 100755 index 0000000..db48bef --- /dev/null +++ b/src/zpkg @@ -0,0 +1,371 @@ +#!/bin/sh + +config_path=/etc/zpkg + +getname () +{ + readlink -f "$1" | rev | cut -d'/' -f1 | rev +} + +usage () +{ + echo "zpkg [options] " + echo "" + echo "Operations:" + echo " update Update packages" + echo " update-database Update only database" + echo " install Install packages" + echo " remove Remove packages" + echo " fetch Fetch packages into current directory" + echo " show Show package files" + echo " list List currently installed packages" + echo " list-all List all packages in repository" + echo " list-outdated List outdated packages" + echo " list-removed List outdated packages" + echo "Admin operations:" + echo " deploy Deploy target to package server" + echo "" + echo "Options:" + echo " -h Display this help" + echo " -c Custom config path" +} + +error () { + printf "\033[1;31m%s\033[0m\n" "$1" >&2 +} +root_check () { + if [ "$(id | cut -d'=' -f2 | cut -d'(' -f1)" -ne 0 ] + then + echo "Root privileges required" > /dev/stderr + exit 10 + fi +} + +package () +{ + echo "Packaging $(getname "$1"): $(du -sh "$1" | awk '{print $1}')iB" + ( + cd "$1" + if [ -n "$(command -v pv)" ] + then + tar cf - * -P | pv -s "$(du -sb . | awk '{print $1}')" | xz > "$2" + else + tar -cvJf "$2" * + fi + ) + mv "$1/$2" ./ +} + +update_remote_database () +{ + ssh $SSH_ADDRESS "~/database_update.sh" +} + +deploy_folder () +{ + archive="$(getname "$1").tar.xz" + if [ -n "$(echo "$1" | grep '\.tar\.xz$' )" ] + then + deploy_package "$1" || return 1 + else + package "$1" "$archive" || return 1 + deploy_package "$archive" || return 1 + rm "$archive" 2> /dev/null + fi +} + +deploy_package () +{ + echo "Deploying $1: $(du -sh "$1" | awk '{print $1}')iB" + scp "$1" $SSH_ADDRESS:~/"$(grep 'PKG_PATH=' .config | cut -d'=' -f2-)" +} + +fetch_package () +{ + if ! wget "$HTTP_ADDRESS/zpkg/$1.tar.xz" -q --show-progress -O "$1.tar.xz" 2>&1 + then + echo "Package '$1' not found" > /dev/stderr + return 1 + else + return 0 + fi +} +add_package_entry () +{ + cd "$config_path" + if grep -q -w "$1" installed 2>/dev/null + then + sed "s|$1 .*|$1 $(date +%s)|g" -i installed + else + echo "$1 $(date +%s)" >> installed + fi +} + +fetch_pkglist () +{ + cd "$config_path" + mv pkglist pkglist_bak 2>/dev/null + if ! wget "$HTTP_ADDRESS/zpkg/pkglist" -q --show-progress -O pkglist 2>&1 + then + echo "Couldn't fetch server data" > /dev/stderr + mv pkglist_bak pkglist 2>/dev/null + return 1 + else + rm pkglist_bak 2>/dev/null + return 0 + fi +} + +view_package () +{ + cd "$PKG_PATH" + tar tf "$1.tar.xz" +} + +unpack () +{ + echo "Unpacking $1" + tar xf "$1" +} + +install_package () +{ + cd / + if fetch_package "$1" + then + unpack "$1.tar.xz" + mv "$1.tar.xz" "$PKG_PATH" + add_package_entry "$1" + fi +} + +removed_packages () +{ + cd "$config_path" + cat installed | while read -r in + do + name=$(echo "$in" | awk '{print $1}') + rem=$(grep -w "$name" pkglist | awk '{print $2}') + if [ -z "$rem" ] ; then + echo $name + fi + done +} + +outdated_packages () +{ + cd "$config_path" + cat installed | while read -r in + do + name=$(echo "$in" | awk '{print $1}') + loc=$(echo "$in" | awk '{print $2}') + rem=$(grep -w "$name" pkglist | awk '{print $2}') + if [ -n "$rem" ] && [ "$loc" -lt "$rem" ] + then + echo $name + fi + done +} + +remove_package () +{ + cd "$PKG_PATH" + if [ ! -f "$1.tar.xz" ] || ! grep -q -w "$1" "$config_path/installed" + then + echo "Package '$1' not installed" > /dev/stderr + return 1 + fi + echo "Removing $1" + tar tf "$1.tar.xz" | tac | while read -r in + do + rm -d "/$in" 2>/dev/null + done + rm "$1.tar.xz" 2>/dev/null + sed -i "/$1 /d" "$config_path/installed" +} + +while getopts ":hc:" opt; +do + case $opt in + h) + usage + exit 0 + ;; + c) + config_path="$OPTARG" + ;; + \?) + echo "Uknown option: $OPTARG" + usage + exit 1 + ;; + esac +done + +config_file="$config_path/zpkg.conf" + +if [ ! -d "$config_path" ] +then + mkdir -p "$config_path" 2>/dev/null +fi +if [ ! -f "$config_file" ] +then + echo "Error: no config file '$config_file'" > /dev/stderr + exit 1 +fi + +. "$config_file" + +#ssh_address=$(grep "SSH_ADDRESS=" "$config_file" | cut -d '=' -f2-) +#http_address=$(grep "HTTP_ADDRESS=" "$config_file" | cut -d '=' -f2-) +#pkg_path=$(grep "PKG_PATH=" "$config_file" | cut -d '=' -f2-) +if [ "$(echo $PKG_PATH | cut -c1)" != "/" ] +then + PKG_PATH="$config_path/$PKG_PATH" +fi + +if [ ! -d "$PKG_PATH" ] +then + mkdir -p "$PKG_PATH" 2>/dev/null +fi + +if [ -n "$1" ] +then + + if [ "$1" = "list" ] + then + awk '{print $1}' "$config_path/installed" 2>/dev/null + + elif [ "$1" = "list-all" ] + then + awk '{print $1}' "$config_path/pkglist" 2>/dev/null + + + elif [ "$1" = "fetch" ] + then + + shift $((OPTIND)) + if [ -z "$2" ] + then + echo "No package specified" > /dev/stderr + else + for I in $* + do + fetch_package "$I" + done + fi + + + elif [ "$1" = "show" ] + then + + if [ -z "$2" ] + then + echo "No package specified" > /dev/stderr + else + shift $((OPTIND)) + for I in $* + do + view_package "$I" + done + fi + + elif [ "$1" = "install" ] + then + + if [ -z "$2" ] + then + echo "No package specified" > /dev/stderr + else + root_check + shift $((OPTIND)) + for I in $* + do + install_package "$I" + done + fi + + elif [ "$1" = "remove" ] + then + + if [ -z "$2" ] + then + echo "No package specified" > /dev/stderr + else + root_check + shift $((OPTIND)) + for I in $* + do + remove_package "$I" + done + fi + + elif [ "$1" = "update-database" ] + then + + root_check + fetch_pkglist + + elif [ "$1" = "update" ] + then + + root_check + fetch_pkglist || exit 1 + r_pkg=$(removed_packages) + o_pkg=$(outdated_packages) + if [ -n "$r_pkg" ] + then + echo "Removing packages: "$r_pkg + for I in $r_pkg + do + remove_package $I + done + fi + if [ -n "$o_pkg" ] + then + echo "Updating packages: "$o_pkg + for I in $o_pkg + do + remove_package $I + install_package $I + done + fi + + elif [ "$1" = "list-outdated" ] + then + + old_cfg_path=$config_path + config_path=/tmp/zpkg + mkdir -p $config_path + ln -sf $old_cfg_path/installed $config_path/installed + fetch_pkglist > /dev/null || exit 1 + outdated_packages + + elif [ "$1" = "list-removed" ] + then + + old_cfg_path=$config_path + config_path=/tmp/zpkg + mkdir -p $config_path + ln -sf $old_cfg_path/installed $config_path/installed + fetch_pkglist > /dev/null || exit 1 + removed_packages + + + elif [ "$1" = "deploy" ] + then + + shift $((OPTIND)) + for I in $* + do + deploy_folder "$I" || exit 1 + done + update_remote_database + + else + usage + fi + +else + usage +fi diff --git a/src/zpkg.bash b/src/zpkg.bash new file mode 100644 index 0000000..21a5176 --- /dev/null +++ b/src/zpkg.bash @@ -0,0 +1,16 @@ +#/usr/bin/env bash + +_zpkg_completion() +{ + _cw1="deploy update update-database install remove fetch show list list-all list-outdated list-removed" + _cw1_pkgw="install remove fetch show" + _cw1_file="deploy" + if [ "$COMP_CWORD" = "1" ] ; then + _compwords=$_cw1 + elif [ "$COMP_CWORD" -gt "1" ] && [ -n "$(echo "$_cw1_pkgw" | grep -w "${COMP_WORDS[1]}")" ] ; then + _compwords=$(zpkg list-all) + fi + COMPREPLY=($(compgen -W "$_compwords" "${COMP_WORDS[$COMP_CWORD]}")) +} + +complete -F _zpkg_completion -o dirnames zpkg