Files
JWeBot/deploy/scripts/bootstrap-jwebot.sh

181 lines
5.0 KiB
Bash
Executable File

#!/usr/bin/env bash
set -Eeuo pipefail
action=${1:-}
source_dir=${2:-}
target_user=${3:-jwebot}
profile_id=${4:-default}
runtime_root=${JWEBOT_RUNTIME_ROOT:-/opt/jwebot}
runtime_python="${runtime_root}/venv/bin/python"
usage() {
cat >&2 <<'EOF'
usage:
bootstrap-jwebot.sh prepare SOURCE_DIR [TARGET_USER] [PROFILE]
bootstrap-jwebot.sh verify SOURCE_DIR [TARGET_USER] [PROFILE]
bootstrap-jwebot.sh deploy SOURCE_DIR [TARGET_USER] [PROFILE]
EOF
exit 64
}
[[ -n "${action}" && -n "${source_dir}" ]] || usage
[[ "${target_user}" =~ ^[a-z_][a-z0-9_-]{0,31}$ ]] || {
echo "[JWeBot] error: invalid target user" >&2
exit 1
}
[[ "${profile_id}" =~ ^[a-z][a-z0-9-]{0,47}$ ]] || {
echo "[JWeBot] error: invalid profile id" >&2
exit 1
}
[[ "${EUID}" -eq 0 ]] || {
echo "[JWeBot] error: bootstrap must run as root" >&2
exit 1
}
[[ -d "${source_dir}" ]] || {
echo "[JWeBot] error: source directory is unavailable" >&2
exit 1
}
progress() {
local step=$1
local total=$2
local label=$3
local width=20
local filled=$((step * width / total))
local empty=$((width - filled))
local bar padding
printf -v bar '%*s' "${filled}" ''
printf -v padding '%*s' "${empty}" ''
printf '[JWeBot] [%s%s] %3d%% %s\n' \
"${bar// /#}" "${padding// /-}" "$((step * 100 / total))" "${label}"
}
account_home() {
getent passwd "${target_user}" | cut -d: -f6
}
as_runtime_user() {
local home
home=$(account_home)
/usr/sbin/runuser -u "${target_user}" -- /usr/bin/env \
HOME="${home}" \
JWEBOT_CONFIG_ROOT="${home}/.config/jwebot" \
JWEBOT_CONFIG_STATE_ROOT="${home}/.local/state/jwebot" \
"$@"
}
prepare() {
progress 1 6 "operating-system packages"
command -v apt-get >/dev/null 2>&1 || {
echo "[JWeBot] error: automatic prepare currently supports apt-based systems" >&2
exit 1
}
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y \
python3 \
python3-pip \
python3-venv \
libx11-6 \
libxdamage1 \
scrot \
tesseract-ocr \
tesseract-ocr-chi-sim \
wmctrl \
xclip \
xdotool
progress 2 6 "runtime account"
if ! id "${target_user}" >/dev/null 2>&1; then
useradd --create-home --shell /bin/bash "${target_user}"
fi
target_home=$(account_home)
[[ -n "${target_home}" && -d "${target_home}" ]]
progress 3 6 "python environment"
install -d -o root -g root -m 0755 "${runtime_root}"
if [[ ! -x "${runtime_python}" ]]; then
python3 -m venv "${runtime_root}/venv"
fi
"${runtime_python}" -m pip install --upgrade pip
"${runtime_python}" -m pip install -r "${source_dir}/requirements-runtime.txt"
"${runtime_python}" -m pip install \
"${source_dir}/packages/jwebot-config" \
"${source_dir}/packages/jwebot-sdk" \
"${source_dir}/packages/jwebot-official-extensions" \
"${source_dir}/packages/jwebot-kernel" \
"${source_dir}/packages/jwebot-extension-host"
progress 4 6 "configuration profile"
if [[ ! -L "${target_home}/.config/jwebot/current" ]]; then
as_runtime_user "${runtime_python}" -m jwebot_config \
init "${profile_id}" --activate
else
as_runtime_user "${runtime_python}" -m jwebot_config \
validate "${profile_id}"
fi
progress 5 6 "private state permissions"
install -d -o "${target_user}" -g "${target_user}" -m 0700 \
"${target_home}/.config/jwebot" \
"${target_home}/.local/state/jwebot" \
"${target_home}/.local/state/jwebot-edge" \
"${target_home}/.local/state/wechat-adaptive-ocr"
progress 6 6 "prepare ready"
cat <<EOF
Edit and activate:
${target_home}/.config/jwebot/profiles/${profile_id}/
Then install and sign in to desktop WeChat on display :1 before running:
$0 verify ${source_dir} ${target_user} ${profile_id}
EOF
}
verify() {
progress 1 5 "runtime account"
id "${target_user}" >/dev/null
target_home=$(account_home)
[[ -d "${target_home}" ]]
progress 2 5 "runtime dependencies"
for command in xdotool scrot xclip wmctrl tesseract; do
command -v "${command}" >/dev/null
done
"${runtime_python}" -c \
'import cv2,numpy,PIL,rapidocr,jwebot_config,jwebot_kernel'
progress 3 5 "configuration profile"
as_runtime_user "${runtime_python}" -m jwebot_config validate "${profile_id}"
as_runtime_user "${runtime_python}" -m jwebot_config activate "${profile_id}"
progress 4 5 "repository gate"
PATH="${runtime_root}/venv/bin:${PATH}" \
"${source_dir}/tools/check-config.sh"
PATH="${runtime_root}/venv/bin:${PATH}" \
"${source_dir}/tools/check-secrets.py"
progress 5 5 "desktop prerequisites"
/usr/sbin/runuser -u "${target_user}" -- /usr/bin/env \
HOME="${target_home}" \
DISPLAY=:1 \
XAUTHORITY="${target_home}/.Xauthority" \
xdotool search --onlyvisible --class wechat >/dev/null
echo "[JWeBot] verify ready"
}
deploy() {
verify
progress 1 2 "coordinated deployment"
JWEBOT_RUNTIME_PYTHON="${runtime_python}" \
"${source_dir}/deploy/scripts/deploy-jwebot-production.sh" \
"${source_dir}" "${target_user}"
progress 2 2 "running"
}
case "${action}" in
prepare) prepare ;;
verify) verify ;;
deploy) deploy ;;
*) usage ;;
esac