You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

283 lines
8.1 KiB

#!/usr/bin/env bash
## CONSTANTS
REPO_BRANCH=main
YAMAL_DIR=/home/yamal
YAMAL_CONFIG=$YAMAL_DIR/config
RAILS_BASE_PORT=900
FORCE=false
DEBUG=''
PACMAN_CMD='pacman'
SESSION_RESTART=false
OS_RELEASE=/etc/os-release
DISTRO=$(test -f "$OS_RELEASE" && (grep -s ^ID "$OS_RELEASE" | cut -d= -f2) || echo unknown_distro)
# TODO: project introspection...
declare -A DISTRO_PACKAGES
DISTRO_PACKAGES[_]="postgresql shfmt"
DISTRO_PACKAGES[debian]="build-essential libssl-dev libyaml-dev zlib1g-dev libgmp-dev libpq-dev libvips42 poppler-utils redis-server"
DISTRO_PACKAGES[arch]="base-devel openssl libyaml zlib gmp libvips poppler valkey"
## FUNCTIONS
function usage {
echo "$(dirname $0)/$(basename $0) --repo-url|-r <URL> [--repo-name|-n <NAME>] [--project|-p <NAME>] [--branch|-b <BRANCH>] [--force|-f] [--debug|-d]"
}
function assert_requirements {
[[ ! $SHELL =~ /bash$ ]] && (echo 'bash is mandatory!' && exit 1)
command -v curl >/dev/null || (echo 'curl is mandatory!' && exit 2)
true
}
function parse_options {
while [[ $# -gt 0 ]]; do
case "$1" in
--repo-url | -r)
shift 1
[[ -z ${1:-} || $1 =~ ^- ]] && usage && exit 1
REPO_URL=$1
;;
--repo-name | -n)
shift 1
[[ -z ${1:-} || $1 =~ ^- ]] && usage && exit 1
REPO_NAME=$1
;;
--project | -p)
shift 1
[[ -z ${1:-} || $1 =~ ^- ]] && usage && exit 1
PROJECT_NAME=$1
;;
--branch | -b)
shift 1
[[ -z ${1:-} || $1 =~ ^- ]] && usage && exit 1
REPO_BRANCH=$1
;;
--origin | -o)
shift 1
[[ -z ${1:-} || $1 =~ ^- ]] && usage && exit 1
ORIGIN=$1
;;
--force | -f)
FORCE=true
;;
--debug | -d)
DEBUG='bash -xl'
;;
--help | -h)
usage
exit 0
;;
*)
echo "Unknown option: $1"
usage
exit 2
;;
esac
shift 1 # Move to the next argument
done
}
function assert_repo_url {
if [[ -z ${REPO_URL:-} ]]; then
>&2 echo -e "ERROR3: REPO_URL undefined!\n\tplease either use --repo-url or -r\n"
usage
exit 3
fi
}
function create_user {
if ! id -u $PROJECT_NAME >/dev/null 2>&1; then
[[ -d /opt/$PROJECT_NAME ]] &&
echo -e "ERROR\n special opt folder not empty: /opt/$PROJECT_NAME\nplease specify unique project name with argument --project|-p" &&
exit 20
useradd -G ssh,yamal -rm --home-dir $YAMAL_DIR/projects/$PROJECT_NAME --shell /bin/bash $PROJECT_NAME
echo "user $PROJECT_NAME successfully created!"
fi
if [[ ! -d /opt/$PROJECT_NAME ]]; then
mkdir -p /opt/$PROJECT_NAME
chown $PROJECT_NAME:$PROJECT_NAME /opt/$PROJECT_NAME
fi
if [[ ! -d /etc/$PROJECT_NAME ]]; then
mkdir -p /etc/$PROJECT_NAME
echo "special folder: /etc/$PROJECT_NAME successfully created!"
fi
chown -R $PROJECT_NAME:$PROJECT_NAME /etc/$PROJECT_NAME
}
function set_project_name {
[[ -z ${REPO_NAME:-} ]] && REPO_NAME=$(echo $REPO_URL | rev | cut -d/ -f1 | rev |cut -d. -f1) # get last url part minus '.git'
DB_USER=$REPO_NAME
[[ -z ${PROJECT_NAME:-} ]] && PROJECT_NAME=$REPO_NAME
true
}
function define_rails_app {
if [[ -f $YAMAL_CONFIG ]]; then
local index=$(grep "^rails_app_.*=$PROJECT_NAME\$" $YAMAL_CONFIG | cut -d '=' -f1 | cut -d '_' -f3)
[[ -z $index ]] && index=$(grep "rails_app_count=" $YAMAL_CONFIG | cut -d '=' -f2)
fi
RAILS_APP=${index:-0}
}
function inc_rails_app_count {
if [[ -f $YAMAL_CONFIG ]]; then
if ! grep -q "^rails_app_.*=$PROJECT_NAME\$" $YAMAL_CONFIG; then
echo "rails_app_$RAILS_APP=$PROJECT_NAME" >>$YAMAL_CONFIG
RAILS_APP=$(($RAILS_APP + 1))
sed -i "s/^rails_app_count.*/rails_app_count=$RAILS_APP/" $YAMAL_CONFIG
echo "new rails app succesfully registered!"
fi
else
mkdir -p $(dirname $YAMAL_CONFIG)
echo "rails_app_count=1" >$YAMAL_CONFIG
echo "rails_app_$RAILS_APP=$PROJECT_NAME" >>$YAMAL_CONFIG
fi
}
function install_systemd_service {
local service="/etc/systemd/system/$PROJECT_NAME.service"
local rails_port=$(($RAILS_BASE_PORT + $RAILS_APP))
if [[ ! -f $service ]]; then
cat <<EOF >$service
[Unit]
Description=$PROJECT_NAME
After=network.target
[Service]
Type=simple
User=$PROJECT_NAME
SyslogIdentifier=$PROJECT_NAME
AmbientCapabilities=CAP_NET_BIND_SERVICE
PermissionsStartOnly=true
WorkingDirectory=/opt/$PROJECT_NAME
ExecStart=$YAMAL_DIR/projects/$PROJECT_NAME/.local/bin/mise exec -- rails server --port $rails_port
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable $PROJECT_NAME
fi
systemctl restart $PROJECT_NAME
systemctl is-active $PROJECT_NAME >/dev/null && echo "service $PROJECT_NAME running on port: $rails_port"
}
function launch_normal_user_setup {
# clean temporary files in case mise install ruby fails!
rm /tmp/mise-ruby-build -rf
echo "launch setup as normal user: $PROJECT_NAME"
local cmd="${DEBUG:-bash -l} -- $(realpath $(dirname "$0"))/lib/setup-prod-normal-user.bash $REPO_URL $REPO_BRANCH $FORCE"
if sudo -iu $PROJECT_NAME $cmd; then
: #ok
else
local exit_code=$?
[[ $exit_code == 100 ]] && echo 'up-to-date: no change!' && exit 0
echo "an error $exit_code has occured during 'setup-prod-normal-user'" && exit $exit_code
fi
}
function enhance_mise_user_from_origin {
[[ -z ${ORIGIN:-} ]] && return
if [[ -d $ORIGIN/.local/share/mise/installs/ruby ]] && [[ ! -f /home/$PROJECT_NAME/.local/bin/mise ]]; then
echo -n "copy mise ruby from origin=$ORIGIN..."
mkdir -p /home/$PROJECT_NAME/.local/share/mise/installs/ruby
cp -r $ORIGIN/.local/share/mise/installs/ruby /home/$PROJECT_NAME/.local/share/mise/installs/
chown -R $PROJECT_NAME:$PROJECT_NAME /home/$PROJECT_NAME/.local
echo OK
fi
}
function install_pacapt {
if [[ ! -f $HOME/.local/bin/pacman ]]; then
mkdir -p $HOME/.local/bin
curl -sLo $HOME/.local/bin/pacman https://github.com/icy/pacapt/raw/ng/pacapt
chmod 755 $HOME/.local/bin/pacman
else
if [[ $DISTRO == 'arch' ]]; then
echo 'pacman natively detected!'
else
PACMAN_CMD="$HOME/.local/bin/pacman"
# echo "pacapt/pacman ($PACMAN_CMD) already installed!"
fi
fi
}
function install_idem_packages {
if ! pacman -Qi $1 &>/dev/null; then
sudo $HOME/.local/bin/pacman -S --noconfirm $1 >/dev/null
echo "$2 installed successfully"
else
echo "$2 already installed!"
fi
}
function install_packages {
install_idem_packages "${DISTRO_PACKAGES[_]}" "generic packages"
[ -v "DISTRO_PACKAGES[${DISTRO}]" ] && install_idem_packages "${DISTRO_PACKAGES[${DISTRO}]}" "distro specific packages: [$DISTRO]"
true
}
function assert_etc_config_for_user {
local config_dir="/etc/$PROJECT_NAME"
if [[ -d $config_dir ]]; then
chown $PROJECT_NAME $config_dir
chmod -R 750 $config_dir
else
echo -e "-----\nERROR\n-----\nplease provide configuration file from location:\n $config_dir"
false
fi
}
function postgres_newdb {
if ! (sudo -u postgres -- psql --csv -tc "SELECT 1 as found FROM pg_roles WHERE rolname = '$DB_USER'" | grep -q ^1$); then
echo "creating postgresql user: $DB_USER"
sudo -iu postgres psql -c "CREATE USER $DB_USER WITH PASSWORD '$DB_USER'"
sudo -iu postgres psql -c "ALTER USER $DB_USER WITH SUPERUSER"
fi
}
function postgres_global_encoding_utf8 {
if ! sudo -iu postgres -- psql -c '\l template1' | grep -q UTF8; then
echo 'defining postgresql template1 with default UTF8 encoding'
sudo -iu postgres -- psql <<EOF
UPDATE pg_database SET datistemplate = FALSE WHERE datname = 'template1';
DROP DATABASE template1;
CREATE DATABASE template1 WITH TEMPLATE = template0 ENCODING = 'UTF8';
UPDATE pg_database SET datistemplate = TRUE WHERE datname = 'template1';
VACUUM FREEZE;
EOF
fi
}
## MAIN
assert_requirements
parse_options $*
[[ $(id -u) != 0 ]] && exec sudo -- $DEBUG $(dirname "$0")/$(basename "$0") --origin $HOME $*
set -Eue
assert_repo_url
set_project_name
install_pacapt
install_packages
define_rails_app
# echo "$PROJECT_NAME:$REPO_BRANCH:$RAILS_APP:$ORIGIN"
create_user
assert_etc_config_for_user
# enhance_mise_user_from_origin # FIXME: not working yet!
postgres_global_encoding_utf8
postgres_newdb
launch_normal_user_setup
install_systemd_service
inc_rails_app_count