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
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
|