ynh-dev/ynh-dev
2023-04-04 14:50:58 +02:00

503 lines
21 KiB
Bash
Executable file
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
function show_usage() {
cat <<EOF
${BLUE}On the host, to manage the LXC${NORMAL}
${BLUE}==============================${NORMAL}
start [DIST] [NAME] [YNH_BRANCH] (Create and) starts a LXC (DIST=bullseye, NAME=ynh-dev and YNH_BRANCH=unstable by default)
attach [DIST] [NAME] [YNH_BRANCH] Attach an already started LXC (DIST=bullseye, NAME=ynh-dev and YNH_BRANCH=unstable by default)
destroy [DIST] [NAME] [YNH_BRANCH] Destroy the ynh-dev LXC (DIST=bullseye, NAME=ynh-dev and YNH_BRANCH=unstable by default)
rebuild [DIST] [NAME] [YNH_BRANCH] Rebuild a fresh, up-to-date box (DIST=bullseye, NAME=ynh-dev and YNH_BRANCH=unstable by default)
${BLUE}Inside the dev instance${NORMAL}
${BLUE}=======================${NORMAL}
ip Give the ip of the guest container
use-git [PKG] Use Git repositories from dev environment path
test [PKG] Deploy, update and run tests for some packages
Tests for single modules and functions can ran with
e.g. ./ynh-dev test yunohost/appurl:urlavailable
catalog
build Rebuild the custom catalog
add Add the custom catalog in Yunohost catalog list
override Override default catalog with the custom catalog
reset Reset the catalog list to Yunohost's default
rebuild-api-doc Rebuild the API swagger doc
EOF
}
function main()
{
local ACTION="$1"
local ARGUMENTS="${@:2}"
[ -z "$ACTION" ] && show_usage && exit 0
case "${ACTION}" in
help|-h|--help) show_usage $ARGUMENTS ;;
start|--start) start_ynhdev $ARGUMENTS ;;
attach|--attach) attach_ynhdev $ARGUMENTS ;;
destroy|--destroy) destroy_ynhdev $ARGUMENTS ;;
rebuild|--rebuild) rebuild_ynhdev $ARGUMENTS ;;
ip|--ip) show_vm_ip $ARGUMENTS ;;
use-git|--use-git) use_git $ARGUMENTS ;;
test|--test) run_tests $ARGUMENTS ;;
catalog|--catalog) catalog $ARGUMENTS ;;
rebuild-api-doc|--rebuild-api-doc) rebuild_api_doc $ARGUMENTS ;;
*) critical "Unknown action ${ACTION}." ;;
esac
}
##################################################################
# Misc helpers #
##################################################################
readonly NORMAL=$(printf '\033[0m')
readonly BOLD=$(printf '\033[1m')
readonly faint=$(printf '\033[2m')
readonly UNDERLINE=$(printf '\033[4m')
readonly NEGATIVE=$(printf '\033[7m')
readonly RED=$(printf '\033[31m')
readonly GREEN=$(printf '\033[32m')
readonly ORANGE=$(printf '\033[33m')
readonly BLUE=$(printf '\033[34m')
readonly YELLOW=$(printf '\033[93m')
readonly WHITE=$(printf '\033[39m')
function success()
{
local msg=${1}
echo "[${BOLD}${GREEN} OK ${NORMAL}] ${msg}"
}
function info()
{
local msg=${1}
echo "[${BOLD}${BLUE}INFO${NORMAL}] ${msg}"
}
function warn()
{
local msg=${1}
echo "[${BOLD}${ORANGE}WARN${NORMAL}] ${msg}" 2>&1
}
function error()
{
local msg=${1}
echo "[${BOLD}${RED}FAIL${NORMAL}] ${msg}" 2>&1
}
function critical()
{
local msg=${1}
echo "[${BOLD}${RED}CRIT${NORMAL}] ${msg}" 2>&1
exit 1
}
function assert_inside_vm() {
[ -d /etc/yunohost ] || critical "There's no YunoHost in there. Are you sure that you are inside the container ?"
}
function create_sym_link() {
local DEST=$1
local LINK=$2
# Remove current sources if not a symlink
[ -L "$LINK" ] || rm -rf $LINK
# Symlink from Git repository
ln -sfn $DEST $LINK
}
##################################################################
# Actions #
##################################################################
function check_lxd_setup()
{
# Check lxd is installed somehow
[[ -e /snap/bin/lxd ]] || which lxd &>/dev/null \
|| critical "You need to have LXD installed for ynh-dev to be usable from the host machine. Refer to the README to know how to install it."
# Check that we'll be able to use lxc/lxd using sudo (for which the PATH is defined in /etc/sudoers and probably doesn't include /snap/bin)
if [[ ! -e /usr/bin/lxc ]] && [[ ! -e /usr/bin/lxd ]] && [[ -e /snap ]]
then
[[ -e /usr/local/bin/lxc ]] && [[ -e /usr/local/bin/lxd ]] \
|| critical "You might want to add lxc and lxd inside /usr/local/bin so that there's no tricky PATH issue with sudo. If you installed lxd/lxc with snapd, this should do the trick: sudo ln -s /snap/bin/lxc /usr/local/bin/lxc && sudo ln -s /snap/bin/lxd /usr/local/bin/lxd"
fi
ip a | grep -q lx[cd]br0 \
|| critical "There is no 'lxcbr0' or 'lxdbr0' interface... Did you ran 'lxd init' ?"
}
function start_ynhdev()
{
check_lxd_setup
local DIST=${1:-bullseye}
local YNH_BRANCH=${3:-unstable}
local BOX=${2:-ynh-dev}-${DIST}-${YNH_BRANCH}
if ! sudo lxc info $BOX &>/dev/null
then
if ! sudo lxc image info $BOX-base &>/dev/null
then
LXC_BASE="ynh-dev-$DIST-amd64-$YNH_BRANCH-base"
sudo lxc launch yunohost:$LXC_BASE $BOX -c security.nesting=true -c security.privileged=true \
|| critical "Failed to launch the container ?"
else
sudo lxc launch $BOX-base $BOX -c security.nesting=true -c security.privileged=true
fi
sudo lxc config device add $BOX ynhdev-shared-folder disk path=/ynh-dev source="$(readlink -f $(pwd))"
info "Now attaching to the container"
else
info "Attaching to existing container"
fi
attach_ynhdev "$@"
}
function attach_ynhdev()
{
check_lxd_setup
local DIST=${1:-bullseye}
local YNH_BRANCH=${3:-unstable}
local BOX=${2:-ynh-dev}-${DIST}-${YNH_BRANCH}
sudo lxc start $BOX 2>/dev/null || true
sudo lxc exec $BOX --cwd /ynh-dev -- /bin/bash
}
function destroy_ynhdev()
{
check_lxd_setup
local DIST=${1:-bullseye}
local YNH_BRANCH=${3:-unstable}
local BOX=${2:-ynh-dev}-${DIST}-${YNH_BRANCH}
sudo lxc stop $BOX
sudo lxc delete $BOX
}
function rebuild_ynhdev()
{
check_lxd_setup
local DIST=${1:-bullseye}
local YNH_BRANCH=${3:-unstable}
local BOX=${2:-ynh-dev}-${DIST}-${YNH_BRANCH}
set -x
sudo lxc info $BOX-rebuild >/dev/null && sudo lxc delete $BOX-rebuild --force
sudo lxc launch images:debian/$DIST/amd64 $BOX-rebuild -c security.nesting=true -c security.privileged=true
sleep 5
sudo lxc exec $BOX-rebuild -- apt install curl -y
INSTALL_SCRIPT="https://install.yunohost.org/$DIST"
sudo lxc exec $BOX-rebuild -- /bin/bash -c "curl $INSTALL_SCRIPT | bash -s -- -a -d $YNH_BRANCH"
sudo lxc stop $BOX-rebuild
sudo lxc publish $BOX-rebuild --alias $BOX-base
set +x
}
function show_vm_ip()
{
assert_inside_vm
hostname --all-ip-addresses | tr ' ' '\n'
}
function use_git()
{
assert_inside_vm
local PACKAGES=("$@")
if [ "$PACKAGES" = "" ]
then
PACKAGES=('moulinette' 'ssowat' 'yunohost' 'yunohost-admin')
fi
for i in "${!PACKAGES[@]}";
do
case ${PACKAGES[i]} in
ssowat)
create_sym_link "/ynh-dev/ssowat" "/usr/share/ssowat"
local ssowat_conf_file="/etc/nginx/conf.d/ssowat.conf"
if ! grep -q lua_package_path $ssowat_conf_file; then
local current_ssowatconf=$(cat $ssowat_conf_file)
echo "lua_package_path '/ynh-dev/ZeroBraneStudio/lualibs/?/?.lua;/ynh-dev/ZeroBraneStudio/lualibs/?.lua;;';" > $ssowat_conf_file
echo "lua_package_cpath '/ynh-dev/ZeroBraneStudio/bin/linux/x64/clibs/?.so;;';" >> $ssowat_conf_file
echo "$current_ssowatconf" >> $ssowat_conf_file
fi
if [ ! -d "/ynh-dev/ZeroBraneStudio" ]; then
info "If you want to debug ssowat, you can clone https://github.com/pkulchenko/ZeroBraneStudio into the ynh-dev directory of your host,"
info "open it, and open a file at the root of the ssowat project in ynh-dev directory, click on \"Project -> Project Directory -> Set From Current File\"."
info "You can start the remote debugger with \"Project -> Start Debugger Server\"."
info "Add the line \"require(\"mobdebug\").start('THE_IP_OF_YOUR_HOST_IN_THE_CONTAINER_NETWORK')\" at the top of the file access.lua and reload nginx in your container with \"systemctl reload nginx\"."
info "After that you should be able to debug ssowat \o/. The debugger should pause the first time it reaches the line \"require(\"mobdebug\").start('...')\", but you can add breakpoints where needed."
info "More info here: http://notebook.kulchenko.com/zerobrane/debugging-openresty-nginx-lua-scripts-with-zerobrane-studio and here: https://github.com/pkulchenko/MobDebug."
fi
success "Now using Git repository for SSOwat"
;;
moulinette)
create_sym_link "/ynh-dev/moulinette/locales" "/usr/share/moulinette/locale"
create_sym_link "/ynh-dev/moulinette/moulinette" "/usr/lib/python3/dist-packages/moulinette"
success "Now using Git repository for Moulinette"
;;
yunohost)
for FILE in $(ls /ynh-dev/yunohost/bin/)
do
create_sym_link "/ynh-dev/yunohost/bin/$FILE" "/usr/bin/$FILE"
done
if [[ $(lsb_release -sc) == "bullseye" ]]
then
for FILE in $(ls /ynh-dev/yunohost/conf/metronome/modules/)
do
create_sym_link "/ynh-dev/yunohost/conf/metronome/modules/$FILE" "/usr/lib/metronome/modules/$FILE"
done
for FILE in $(ls /ynh-dev/yunohost/share/)
do
create_sym_link "/ynh-dev/yunohost/share/$FILE" "/usr/share/yunohost/$FILE"
done
create_sym_link "/ynh-dev/yunohost/hooks" "/usr/share/yunohost/hooks"
create_sym_link "/ynh-dev/yunohost/helpers" "/usr/share/yunohost/helpers.d"
create_sym_link "/ynh-dev/yunohost/conf" "/usr/share/yunohost/conf"
create_sym_link "/ynh-dev/yunohost/locales" "/usr/share/yunohost/locales"
create_sym_link "/ynh-dev/yunohost/src" "/usr/lib/python3/dist-packages/yunohost"
python3 "/ynh-dev/yunohost/doc/generate_bash_completion.py"
create_sym_link "/ynh-dev/yunohost/doc/bash-completion.sh" "/etc/bash_completion.d/yunohost"
else
# data
python3 "/ynh-dev/yunohost/data/actionsmap/yunohost_completion.py"
create_sym_link "/ynh-dev/yunohost/data/bash-completion.d/yunohost" "/etc/bash_completion.d/yunohost"
create_sym_link "/ynh-dev/yunohost/data/actionsmap/yunohost.yml" "/usr/share/moulinette/actionsmap/yunohost.yml"
create_sym_link "/ynh-dev/yunohost/data/hooks" "/usr/share/yunohost/hooks"
create_sym_link "/ynh-dev/yunohost/data/templates" "/usr/share/yunohost/templates"
create_sym_link "/ynh-dev/yunohost/data/helpers" "/usr/share/yunohost/helpers"
create_sym_link "/ynh-dev/yunohost/data/helpers.d" "/usr/share/yunohost/helpers.d"
create_sym_link "/ynh-dev/yunohost/data/other" "/usr/share/yunohost/yunohost-config/moulinette"
# lib
create_sym_link "/ynh-dev/yunohost/lib/metronome/modules/ldap.lib.lua" "/usr/lib/metronome/modules/ldap.lib.lua"
create_sym_link "/ynh-dev/yunohost/lib/metronome/modules/mod_auth_ldap2.lua" "/usr/lib/metronome/modules/mod_auth_ldap2.lua"
create_sym_link "/ynh-dev/yunohost/lib/metronome/modules/mod_legacyauth.lua" "/usr/lib/metronome/modules/mod_legacyauth.lua"
create_sym_link "/ynh-dev/yunohost/lib/metronome/modules/mod_storage_ldap.lua" "/usr/lib/metronome/modules/mod_storage_ldap.lua"
create_sym_link "/ynh-dev/yunohost/lib/metronome/modules/vcard.lib.lua" "/usr/lib/metronome/modules/vcard.lib.lua"
# src
create_sym_link "/ynh-dev/yunohost/src/yunohost" "/usr/lib/moulinette/yunohost"
# locales
create_sym_link "/ynh-dev/yunohost/locales" "/usr/lib/moulinette/yunohost/locales"
fi
success "Now using Git repository for YunoHost"
;;
yunohost-admin)
mkdir -p /var/cache/ynh-dev/yunohost-admin/
create_sym_link "/ynh-dev/yunohost-admin/app/.env" "/var/cache/ynh-dev/yunohost-admin/.env"
create_sym_link "/var/cache/ynh-dev/yunohost-admin/node_modules" "/ynh-dev/yunohost-admin/app/node_modules"
create_sym_link "/ynh-dev/yunohost-admin/app/package.json" "/var/cache/ynh-dev/yunohost-admin/package.json"
create_sym_link "/ynh-dev/yunohost-admin/app/package-lock.json" "/var/cache/ynh-dev/yunohost-admin/package-lock.json"
cd /var/cache/ynh-dev/yunohost-admin/
# Inject container ip in .env file
# Used by vite to expose itself on network and proxy api requests.
IP=$(hostname -I | tr ' ' '\n' | grep "\.")
echo "VITE_IP=$IP" > .env
# Allow port 8080 in config file or else the dev server will stop working after postinstall
if [[ ! -e /etc/yunohost/installed ]]
then
python3 - <<EOF
import os, yaml
setting_file = "/etc/yunohost/firewall.yml"
assert os.path.exists(setting_file), "Firewall yaml file %s does not exists ?" % setting_file
with open(setting_file) as f:
settings = yaml.load(f)
if 8080 not in settings["ipv4"]["TCP"]:
settings["ipv4"]["TCP"].append(8080)
with open(setting_file, "w") as f:
yaml.safe_dump(settings, f, default_flow_style=False)
EOF
fi
# Vite require node v14 to parse modern syntax
if [[ ! $(node -v) == v14* ]]
then
DISTRO="$(lsb_release -s -c)"
if [ "$DISTRO" == "buster" ]; then
info "Installing node v14..."
KEYRING=/usr/share/keyrings/nodesource.gpg
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource.gpg.key | gpg --dearmor | sudo tee "$KEYRING" >/dev/null
gpg --no-default-keyring --keyring "$KEYRING" --list-keys
VERSION=node_14.x
echo "deb [signed-by=$KEYRING] https://deb.nodesource.com/$VERSION $DISTRO main" | sudo tee /etc/apt/sources.list.d/nodesource.list
fi
apt-get update
apt install nodejs -y
# to return to nodejs debian version
# apt purge nodejs && rm -r /etc/apt/sources.list.d/nodesource.list && apt install nodejs
fi
# Install dependencies with npm install (or rather npm ci)
if [[ -e "/var/cache/ynh-dev/yunohost-admin/node_modules/vue" ]];
then
info "NB: skipping npm ci because vue is already installed. If you want to upgrade/refresh npm dependencies, you should run 'npm ci' manually, or delete /var/cache/ynh-dev/yunohot-admin/node_modules."
else
info "Installing npm dependencies ... (this may take a while)"
npm ci --no-bin-links
fi
cd /ynh-dev/yunohost-admin/app/
info "Now running 'npm run dev'"
npm run dev
;;
yunohost-admin-build)
if [[ ! -e "/usr/share/yunohost/admin-bkp" ]]
then
info "Backuping base yunohost-admin sources"
mv /usr/share/yunohost/admin /usr/share/yunohost/admin-bkp
fi
cd /ynh-dev/yunohost-admin/app
npm run build
create_sym_link "/ynh-dev/yunohost-admin/app/dist" "/usr/share/yunohost/admin"
IP=$(hostname -I | tr ' ' '\n' | grep "\.")
success "App builded and available at https://$IP/yunohost/admin"
;;
*)
error "Invalid package '${PACKAGES[i]}': correct arguments are 'yunohost', 'ssowat', 'moulinette', 'yunohost-admin' or nothing for all"
;;
esac
done
}
function run_tests()
{
assert_inside_vm
local PACKAGES="$@"
for PACKAGE in $PACKAGES;
do
TEST_FUNCTION=$(echo "$PACKAGE" | tr '/:' ' ' | awk '{print $3}')
TEST_MODULE=$(echo "$PACKAGE" | tr '/:' ' ' | awk '{print $2}')
PACKAGE=$(echo "$PACKAGE" | tr '/:' ' ' | awk '{print $1}')
case $PACKAGE in
yunohost)
# Pytest and tests dependencies
if ! type "pytest" > /dev/null 2>&1; then
info "> Installing pytest ..."
apt-get install python3-pip -y
pip3 install pytest pytest-sugar
fi
for DEP in "pytest-mock requests-mock mock"
do
if [ -z "$(pip3 show $DEP)" ]; then
info "Installing $DEP with pip3"
pip3 install $DEP
fi
done
# Apps for test
cd /ynh-dev/yunohost/src/tests
[ -d "apps" ] || git clone https://github.com/YunoHost/test_apps ./apps
cd apps
git pull > /dev/null 2>&1
# Run tests
info "Running tests for YunoHost"
[ -e "/etc/yunohost/installed" ] || critical "You should run postinstallation before running tests :s."
if [[ -z "$TEST_MODULE" ]]
then
cd /ynh-dev/yunohost/
py.test tests
cd /ynh-dev/yunohost/src
py.test tests
else
cd /ynh-dev/yunohost/src
if [[ -z "$TEST_FUNCTION" ]]
then
py.test tests/test_"$TEST_MODULE".py
else
py.test tests/test_"$TEST_MODULE".py::test_"$TEST_FUNCTION"
fi
fi
;;
esac
done
}
function catalog()
{
assert_inside_vm
local ACTION="$1"
local CUSTOM_APPS_FOLDER=${2:-"/ynh-dev/custom-catalog"}
local CUSTOM_CAT_PATH="${CUSTOM_APPS_FOLDER}/catalog.json"
local CACHE_FOLDER="/var/cache/yunohost/repo"
cd /ynh-dev/custom-catalog/
case "${ACTION}" in
build)
info "Rebuilding custom app catalog"
python3 -c "from catalog_manager import build; build(folder='${CUSTOM_APPS_FOLDER}')" && success "Successfully build custom catalog list in '${CUSTOM_CAT_PATH}'"
;;
add)
info "Injecting custom catalog in YunoHost catalog list"
create_sym_link "${CUSTOM_CAT_PATH}" "${CACHE_FOLDER}/custom.json"
python3 -c "from catalog_manager import add; add()" && success "Custom catalog '${CUSTOM_CAT_PATH}' added to catalog list"
;;
override)
info "Overriding default catalog by custom one"
create_sym_link "${CUSTOM_CAT_PATH}" "${CACHE_FOLDER}/custom.json"
python3 -c "from catalog_manager import override; override()" && success "Default catalog is now overrided by '$CUSTOM_CAT_PATH'"
;;
reset)
info "Reseting to YunoHost default catalog list"
[ -e "$CACHE_FOLDER/custom.json" ] && rm "$CACHE_FOLDER/custom.json"
python3 -c "from catalog_manager import reset; reset()" || exit 1
success "Returned to default"
;;
*)
critical "Unknown catalog action '${ACTION}'."
;;
esac
}
function rebuild_api_doc()
{
test -d yunohost || critical "Expected to find a 'yunohost' folder ?"
cd yunohost/doc
if ! test -d swagger
then
# Download swagger ui
info "Downloading swagger UI"
curl -L https://github.com/swagger-api/swagger-ui/archive/refs/tags/v4.15.5.tar.gz | tar -xvz swagger-ui-4.15.5/dist/;
mv swagger-ui-4.15.5/dist/ swagger
rmdir swagger-ui-4.15.5
fi
info "Rebuild swagger json/js according to actionsmap"
python3 generate_api_doc.py
success "You should now open yunohost/doc/api.html using your favorite browser"
}
main $@