From 8418563970aa04fd516e196efc50e6cf58007002 Mon Sep 17 00:00:00 2001 From: Bart Ribbers Date: Fri, 15 Jan 2021 12:59:25 +0100 Subject: [PATCH] Replace mycroft-{start,stop} with scripts that can launch properly installed setups These scripts will work with system-wide setups (/usr/bin/mycroft-start), user setups (~/.local/bin/mycroft-start) and virtual environments (.venv/bin/mycroft-start). A big benefit is that they don't care where they're installed. They'll launch from a virtual environment, user setups or system-wide setup, in that order. Also make sure these scripts are actually installed by setup.py. These changes should make it possible to create a PyPi package installable with pip. --- bin/mycroft-start | 153 +++++++++++++++++++++++++++++++++++++++-- bin/mycroft-stop | 109 +++++++++++++++++++++++++++-- dev_setup.sh | 54 +++------------ requirements/tests.txt | 2 +- setup.py | 7 +- 5 files changed, 270 insertions(+), 55 deletions(-) diff --git a/bin/mycroft-start b/bin/mycroft-start index 65723c2c3734..30cd1ecfdbbb 100755 --- a/bin/mycroft-start +++ b/bin/mycroft-start @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh # Copyright 2019 Mycroft AI Inc. # @@ -14,8 +14,151 @@ # See the License for the specific language governing permissions and # limitations under the License. -SOURCE="${BASH_SOURCE[0]}" -cd -P "$( dirname "$SOURCE" )"/.. -DIR="$( pwd )" +script=${0} +script=${script##*/} -. "$DIR/start-mycroft.sh" $@ +help() { + echo "${script}: Mycroft command/service launcher" + echo "usage: ${script} [COMMAND] [restart] [params]" + echo + echo "Services COMMANDs:" + echo " all runs core services: bus, audio, skills, voice" + echo " debug runs core services, then starts the CLI" + echo " audio the audio playback service" + echo " bus the messagebus service" + echo " skills the skill service" + echo " voice voice capture service" + echo " enclosure mark_1 enclosure service" + echo + echo "Tool COMMANDs:" + echo " cli the Command Line Interface" + echo + echo "Options:" + echo " restart (optional) Force the service to restart if running" + echo + echo "Examples:" + echo " ${script} all" + echo " ${script} all restart" + echo " ${script} bus" + echo " ${script} voice" + + exit 1 +} + +name_to_script_path() { + case ${1} in + "bus") _module="mycroft.messagebus.service" ;; + "skills") _module="mycroft.skills" ;; + "audio") _module="mycroft.audio" ;; + "voice") _module="mycroft.client.speech" ;; + "cli") _module="mycroft.client.text" ;; + "enclosure") _module="mycroft.client.enclosure" ;; + + *) + echo "Error: Unknown name '${1}'" + exit 1 + esac +} + +require_process() { + name_to_script_path "${1}" + if ! pgrep -f "python3 (.*)-m ${_module}" > /dev/null; then + launch_background "${1}" + fi +} + +launch_process() { + name_to_script_path "${1}" + + # Luanch process in foreground + echo "Starting $1" + python3 -m ${_module} "$_params" +} + +launch_background() { + name_to_script_path "${1}" + + if pgrep -f "python3 (.*)-m ${_module}" > /dev/null; then + if ($_force_restart); then + echo "Restarting: ${1}" + mycroft-stop "${1}" + else + # Already running, no need to restart + return + fi + else + echo "Starting background service $1" + fi + + # Security warning/reminder for the user + if [ "${1}" = "bus" ] ; then + echo "CAUTION: The Mycroft bus is an open websocket with no built-in security" + echo " measures. You are responsible for protecting the local port" + echo " 8181 with a firewall as appropriate." + fi + + # Launch process in background + # Send logs to XDG Base Directories cache location + logdir="${XDG_STATE_HOME:-$HOME/.local/state}/mycroft" + + if [ ! -d "$logdir" ]; then + mkdir -p "$logdir" + fi + + python3 -m ${_module} "$_params" >> "$logdir/${1}.log" 2>&1 & +} + +launch_all() { + echo "Starting all mycroft-core services" + launch_background bus + launch_background skills + launch_background audio + launch_background voice + launch_background enclosure +} + +_opt=$1 +_force_restart=false +shift +if [ "${1}" = "restart" ] || [ "${_opt}" = "restart" ]; then + _force_restart=true + if [ "${_opt}" = "restart" ]; then + # Support "start-mycroft restart all" as well as "start-mycroft all restart" + _opt=$1 + fi + shift +fi +_params=$* + +case ${_opt} in + "all") + launch_all + ;; + "bus") + launch_background "${_opt}" + ;; + "audio") + launch_background "${_opt}" + ;; + "skills") + launch_background "${_opt}" + ;; + "voice") + launch_background "${_opt}" + ;; + "debug") + launch_all + launch_process cli + ;; + "cli") + require_process bus + require_process skills + launch_process "${_opt}" + ;; + "enclosure") + launch-background "${_opt}" + ;; + *) + help + ;; +esac diff --git a/bin/mycroft-stop b/bin/mycroft-stop index b86b0ea65577..289c736959ae 100755 --- a/bin/mycroft-stop +++ b/bin/mycroft-stop @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh # Copyright 2019 Mycroft AI Inc. # @@ -14,8 +14,107 @@ # See the License for the specific language governing permissions and # limitations under the License. -SOURCE="${BASH_SOURCE[0]}" -cd -P "$( dirname "$SOURCE" )"/.. -DIR="$( pwd )" +script=${0} +script=${script##*/} -. "$DIR/stop-mycroft.sh" $@ +help() { + echo "${script}: Mycroft service stopper" + echo "usage: ${script} [service]" + echo + echo "Service:" + echo " all ends core services: bus, audio, skills, voice" + echo " (none) same as \"all\"" + echo " bus stop the Mycroft messagebus service" + echo " audio stop the audio playback service" + echo " skills stop the skill service" + echo " voice stop voice capture service" + echo " enclosure stop enclosure (hardware/gui interface) service" + echo + echo "Examples:" + echo " ${script}" + echo " ${script} audio" + + exit 0 +} + +process_running() { + if [ "$( pgrep -f "python3 (.*)-m mycroft.*${1}" )" ]; then + return 0 + else + return 1 + fi +} + +end_process() { + if process_running "$1"; then + # Find the process by name, only returning the oldest if it has children + pid=$( pgrep -o -f "python3 (.*)-m mycroft.*${1}" ) + echo "Stopping $1 (${pid})..." + kill -SIGINT "${pid}" + + # Wait up to 5 seconds (50 * 0.1) for process to stop + c=1 + while [ $c -le 50 ]; do + if process_running "$1"; then + sleep 0.1 + c=$((c + 1)) + else + c=999 # end loop + fi + done + + if process_running "$1"; then + echo "Failed to stop." + pid=$( pgrep -o -f "python3 (.*)-m mycroft.*${1}" ) + echo " Killing $1 (${pid})..." + kill -9 "${pid}" + echo "Killed." + result=120 + else + echo "Stopped." + if [ $result -eq 0 ] ; then + result=100 + fi + fi + fi +} + +result=0 # default, no change + +OPT=$1 +shift + +case ${OPT} in + "all"|"") + echo "Stopping all mycroft-core services" + end_process "skills" + end_process "audio" + end_process "speech" + end_process "enclosure" + end_process "messagebus.service" + ;; + "bus") + end_process "messagebus.service" + ;; + "audio") + end_process "audio" + ;; + "skills") + end_process "skills" + ;; + "voice") + end_process "speech" + ;; + "enclosure") + end_process "enclosure" + ;; + *) + help + ;; +esac + +# Exit codes: +# 0 if nothing changed (e.g. --help or no process was running) +# 100 at least one process was stopped +# 120 if any process had to be killed +exit $result diff --git a/dev_setup.sh b/dev_setup.sh index 4290850bda03..7181c82f2ba1 100755 --- a/dev_setup.sh +++ b/dev_setup.sh @@ -25,6 +25,12 @@ set -Ee cd $(dirname $0) TOP=$(pwd -L) +if [ -n "${XDG_CACHE_HOME+x}" ]; then + logdir="$XDG_CACHE_HOME/mycroft" +else + logdir="$HOME/.cache/mycroft" +fi + function clean_mycroft_files() { echo ' This will completely remove any files installed by mycroft (including pairing @@ -34,11 +40,12 @@ Do you wish to continue? (y/n)' read -N1 -s key case $key in [Yy]) - sudo rm -rf /var/log/mycroft + rm -rf $logdir rm -f /var/tmp/mycroft_web_cache.json rm -rf "${TMPDIR:-/tmp}/mycroft" rm -rf "$HOME/.mycroft" sudo rm -rf "/opt/mycroft" + ${VIRTUALENV}/bin/pip uninstall . exit 0 ;; [Nn]) @@ -227,33 +234,6 @@ locally?' fi echo - # Add mycroft-core/bin to the .bashrc PATH? - sleep 0.5 - echo ' -There are several Mycroft helper commands in the bin folder. These -can be added to your system PATH, making it simpler to use Mycroft. -Would you like this to be added to your PATH in the .profile?' - if get_YN ; then - echo -e "$HIGHLIGHT Y - Adding Mycroft commands to your PATH $RESET" - - if [[ ! -f ~/.profile_mycroft ]] ; then - # Only add the following to the .profile if .profile_mycroft - # doesn't exist, indicating this script has not been run before - echo '' >> ~/.profile - echo '# include Mycroft commands' >> ~/.profile - echo 'source ~/.profile_mycroft' >> ~/.profile - fi - - echo " -# WARNING: This file may be replaced in future, do not customize. -# set path so it includes Mycroft utilities -if [ -d \"${TOP}/bin\" ] ; then - PATH=\"\$PATH:${TOP}/bin\" -fi" > ~/.profile_mycroft - echo -e "Type ${CYAN}mycroft-help$RESET to see available commands." - else - echo -e "$HIGHLIGHT N - PATH left unchanged $RESET" - fi # Create a link to the 'skills' folder. sleep 0.5 @@ -332,18 +312,15 @@ Please review the following package changes carefully." fi } - function open_suse_install() { $SUDO zypper install -y git python3 python3-devel libtool libffi-devel libopenssl-devel autoconf automake bison swig portaudio-devel mpg123 flac curl libicu-devel pkg-config libjpeg-devel libfann-devel python3-curses pulseaudio $SUDO zypper install -y -t pattern devel_C_C++ } - function fedora_install() { $SUDO dnf install -y git python3 python3-devel python3-pip python3-setuptools python3-virtualenv pygobject3-devel libtool libffi-devel openssl-devel autoconf bison swig glib2-devel portaudio-devel mpg123 mpg123-plugins-pulseaudio screen curl pkgconfig libicu-devel automake libjpeg-turbo-devel fann-devel gcc-c++ redhat-rpm-config jq make } - function arch_install() { $SUDO pacman -S --needed --noconfirm git python python-pip python-setuptools python-virtualenv python-gobject libffi swig portaudio mpg123 screen flac curl icu libjpeg-turbo base-devel jq pulseaudio pulseaudio-alsa @@ -356,7 +333,6 @@ function arch_install() { ) } - function centos_install() { $SUDO yum install epel-release redhat_common_install @@ -513,7 +489,7 @@ if ! grep -q "$TOP" $VENV_PATH_FILE ; then ' "$VENV_PATH_FILE" fi -# install required python modules +# Install required python modules if ! pip install -r requirements/requirements.txt ; then echo 'Warning: Failed to install required dependencies. Continue? y/N' read -n1 continue @@ -537,6 +513,8 @@ fi if ! pip install -r requirements/tests.txt ; then echo "Warning: Test requirements failed to install. Note: normal operation should still work fine..." fi +# Actually installing Mycroft and it's deps +pip install . SYSMEM=$(free | awk '/^Mem:/ { print $2 }') MAXCORES=$(($SYSMEM / 2202010)) @@ -582,15 +560,5 @@ chmod +x bin/mycroft-say-to chmod +x bin/mycroft-skill-testrunner chmod +x bin/mycroft-speak -# create and set permissions for logging -if [[ ! -w /var/log/mycroft/ ]] ; then - # Creating and setting permissions - echo 'Creating /var/log/mycroft/ directory' - if [[ ! -d /var/log/mycroft/ ]] ; then - $SUDO mkdir /var/log/mycroft/ - fi - $SUDO chmod 777 /var/log/mycroft/ -fi - #Store a fingerprint of setup md5sum requirements/requirements.txt requirements/extra-audiobackend.txt requirements/extra-stt.txt requirements/extra-mark1.txt requirements/tests.txt dev_setup.sh > .installed diff --git a/requirements/tests.txt b/requirements/tests.txt index 9e038123e51a..306320db3e8b 100644 --- a/requirements/tests.txt +++ b/requirements/tests.txt @@ -5,7 +5,7 @@ pytest-cov==2.8.1 cov-core==1.15.0 sphinx==2.2.1 sphinx-rtd-theme==0.4.3 -git+https://github.com/behave/behave@v1.2.7.dev1 +behave @ git+https://github.com/behave/behave@v1.2.7.dev1 allure-behave==2.8.10 python-vlc==1.1.2 diff --git a/setup.py b/setup.py index 963c503e4d2f..815fceaa4c65 100644 --- a/setup.py +++ b/setup.py @@ -66,6 +66,7 @@ def required(requirements_file): 'mark1': required('requirements/extra-mark1.txt'), 'stt': required('requirements/extra-stt.txt') }, + tests_require=required('requirements/tests.txt'), packages=find_packages(include=['mycroft*']), include_package_data=True, @@ -80,5 +81,9 @@ def required(requirements_file): 'mycroft-enclosure-client=mycroft.client.enclosure.__main__:main', 'mycroft-cli-client=mycroft.client.text.__main__:main' ] - } + }, + scripts=[ + 'bin/mycroft-start', + 'bin/mycroft-stop' + ] )