From 8a8b26c7b8f86cca958abda07c7b098ab8f458f3 Mon Sep 17 00:00:00 2001 From: yalh76 Date: Mon, 22 Mar 2021 21:11:24 +0100 Subject: [PATCH] Switch from n to nodenv --- data/helpers.d/nodejs | 410 +++++++++++++++++++++++------------------- 1 file changed, 222 insertions(+), 188 deletions(-) diff --git a/data/helpers.d/nodejs b/data/helpers.d/nodejs index c7903ddcc..eadd36351 100644 --- a/data/helpers.d/nodejs +++ b/data/helpers.d/nodejs @@ -1,267 +1,301 @@ #!/bin/bash -n_version=7.0.2 -n_install_dir="/opt/node_n" -node_version_path="$n_install_dir/n/versions/node" -# N_PREFIX is the directory of n, it needs to be loaded as a environment variable. -export N_PREFIX="$n_install_dir" - -# Install Node version management -# -# [internal] -# -# usage: ynh_install_n -# -# Requires YunoHost version 2.7.12 or higher. -ynh_install_n () { - ynh_print_info --message="Installation of N - Node.js version management" - # Build an app.src for n - echo "SOURCE_URL=https://github.com/tj/n/archive/v${n_version}.tar.gz -SOURCE_SUM=fa80a8685f0fb1b4187fc0a1228b44f0ea2f244e063fe8f443b8913ea595af89" > "$YNH_APP_BASEDIR/conf/n.src" - # Download and extract n - ynh_setup_source --dest_dir="$n_install_dir/git" --source_id=n - # Install n - (cd "$n_install_dir/git" - PREFIX=$N_PREFIX make install 2>&1) +ynh_nodejs_try_bash_extension() { + if [ -x src/configure ]; then + src/configure && make -C src || { + ynh_print_info --message="Optional bash extension failed to build, but things will still work normally." + } + fi } -# Load the version of node for an app, and set variables. +nodenv_install_dir="/opt/nodenv" +nodejs_version_path="$nodenv_install_dir/versions" +# NODENV_ROOT is the directory of nodenv, it needs to be loaded as a environment variable. +export NODENV_ROOT="$nodenv_install_dir" + +# Load the version of Node.js for an app, and set variables. # -# usage: ynh_use_nodejs -# -# `ynh_use_nodejs` has to be used in any app scripts before using node for the first time. +# ynh_use_nodejs has to be used in any app scripts before using Node.js for the first time. # This helper will provide alias and variables to use in your scripts. # -# To use npm or node, use the alias `ynh_npm` and `ynh_node`. -# -# Those alias will use the correct version installed for the app. +# To use npm or node, use the alias `ynh_npm` and `ynh_node` +# Those alias will use the correct version installed for the app # For example: use `ynh_npm install` instead of `npm install` # # With `sudo` or `ynh_exec_as`, use instead the fallback variables `$ynh_npm` and `$ynh_node` -# And propagate $PATH to sudo with $ynh_node_load_PATH -# Exemple: `ynh_exec_as $app $ynh_node_load_PATH $ynh_npm install` +# And propagate $PATH to sudo with $ynh_node_load_path +# Exemple: `ynh_exec_as $app $ynh_node_load_path $ynh_npm install` # -# $PATH contains the path of the requested version of node. -# However, $PATH is duplicated into $node_PATH to outlast any manipulation of `$PATH` -# You can use the variable `$ynh_node_load_PATH` to quickly load your node version -# in $PATH for an usage into a separate script. -# Exemple: $ynh_node_load_PATH $final_path/script_that_use_npm.sh` +# $PATH contains the path of the requested version of Node.js. +# However, $PATH is duplicated into $nodejs_path to outlast any manipulation of $PATH +# You can use the variable `$ynh_node_load_path` to quickly load your Node.js version +# in $PATH for an usage into a separate script. +# Exemple: $ynh_node_load_path $final_path/script_that_use_npm.sh` # # -# Finally, to start a nodejs service with the correct version, 2 solutions -# Either the app is dependent of node or npm, but does not called it directly. -# In such situation, you need to load PATH : -# ``` -# Environment="__NODE_ENV_PATH__" -# ExecStart=__FINALPATH__/my_app -# ``` -# You will replace __NODE_ENV_PATH__ with $ynh_node_load_PATH. +# Finally, to start a Node.js service with the correct version, 2 solutions +# Either the app is dependent of Node.js or npm, but does not called it directly. +# In such situation, you need to load PATH +# `Environment="__YNH_NODE_LOAD_PATH__"` +# `ExecStart=__FINALPATH__/my_app` +# You will replace __YNH_NODE_LOAD_PATH__ with $ynh_node_load_path # -# Or node start the app directly, then you don't need to load the PATH variable -# ``` -# ExecStart=__YNH_NODE__ my_app run -# ``` -# You will replace __YNH_NODE__ with $ynh_node +# Or Node.js start the app directly, then you don't need to load the PATH variable +# `ExecStart=__YNH_NODE__ my_app run` +# You will replace __YNH_NODE__ with $ynh_node # # -# 2 other variables are also available -# - $nodejs_path: The absolute path to node binaries for the chosen version. -# - $nodejs_version: Just the version number of node for this app. Stored as 'nodejs_version' in settings.yml. +# one other variable is also available +# - $nodejs_path: The absolute path to Node.js binaries for the chosen version. # -# Requires YunoHost version 2.7.12 or higher. +# usage: ynh_use_nodejs +# +# Requires YunoHost version 3.2.2 or higher. ynh_use_nodejs () { nodejs_version=$(ynh_app_setting_get --app=$app --key=nodejs_version) - # Get the absolute path of this version of node - nodejs_path="$node_version_path/$nodejs_version/bin" + # Get the absolute path of this version of Node.js + nodejs_path="$nodejs_version_path/$YNH_APP_INSTANCE_NAME/bin" # Allow alias to be used into bash script shopt -s expand_aliases - # Create an alias for the specific version of node and a variable as fallback + # Create an alias for the specific version of Node.js and a variable as fallback ynh_node="$nodejs_path/node" alias ynh_node="$ynh_node" # And npm ynh_npm="$nodejs_path/npm" alias ynh_npm="$ynh_npm" - # Load the path of this version of node in $PATH + # Load the path of this version of Node.js in $PATH if [[ :$PATH: != *":$nodejs_path"* ]]; then PATH="$nodejs_path:$PATH" fi - node_PATH="$PATH" # Create an alias to easily load the PATH - ynh_node_load_PATH="PATH=$node_PATH" + ynh_node_load_PATH="PATH=$PATH" + ynh_nodejs_load_path="PATH=$PATH" + + # Sets the local application-specific Node.js version + pushd $final_path + $nodenv_install_dir/bin/nodenv local $nodejs_version + popd } -# Install a specific version of nodejs +# Install a specific version of Node.js # -# ynh_install_nodejs will install the version of node provided as argument by using n. +# ynh_install_nodejs will install the version of Node.js provided as argument by using nodenv. +# +# This helper creates a /etc/profile.d/nodenv.sh that configures PATH environment for nodenv +# for every LOGIN user, hence your user must have a defined shell (as opposed to /usr/sbin/nologin) +# +# Don't forget to execute Node.js-dependent command in a login environment +# (e.g. sudo --login option) +# When not possible (e.g. in systemd service definition), please use direct path +# to nodenv shims (e.g. $NODENV_ROOT/shims/bundle) # # usage: ynh_install_nodejs --nodejs_version=nodejs_version -# | arg: -n, --nodejs_version= - Version of node to install. When possible, your should prefer to use major version number (e.g. 8 instead of 8.10.0). The crontab will then handle the update of minor versions when needed. +# | arg: -v, --nodejs_version= - Version of Node.js to install. # -# `n` (Node version management) uses the `PATH` variable to store the path of the version of node it is going to use. -# That's how it changes the version -# -# Refer to `ynh_use_nodejs` for more information about available commands and variables -# -# Requires YunoHost version 2.7.12 or higher. +# Requires YunoHost version 3.2.2 or higher. ynh_install_nodejs () { - # Use n, https://github.com/tj/n to manage the nodejs versions - # Declare an array to define the options of this helper. - local legacy_args=n - local -A args_array=( [n]=nodejs_version= ) + local legacy_args=v + local -A args_array=( [v]=nodejs_version= ) local nodejs_version # Manage arguments with getopts ynh_handle_getopts_args "$@" - # Create $n_install_dir - mkdir --parents "$n_install_dir" + # Load nodenv path in PATH + local CLEAR_PATH="$nodenv_install_dir/bin:$PATH" - # Load n path in PATH - CLEAR_PATH="$n_install_dir/bin:$PATH" - # Remove /usr/local/bin in PATH in case of node prior installation + # Remove /usr/local/bin in PATH in case of Node.js prior installation PATH=$(echo $CLEAR_PATH | sed 's@/usr/local/bin:@@') - # Move an existing node binary, to avoid to block n. + # Move an existing Node.js binary, to avoid to block nodenv test -x /usr/bin/node && mv /usr/bin/node /usr/bin/node_n test -x /usr/bin/npm && mv /usr/bin/npm /usr/bin/npm_n - # If n is not previously setup, install it - if ! $n_install_dir/bin/n --version > /dev/null 2>&1 - then - ynh_install_n - elif dpkg --compare-versions "$($n_install_dir/bin/n --version)" lt $n_version - then - ynh_install_n + # Install or update nodenv + nodenv="$(command -v nodenv $nodenv_install_dir/bin/nodenv | head -1)" + if [ -n "$nodenv" ]; then + ynh_print_info --message="nodenv already seems installed in \`$nodenv'." + pushd "${nodenv%/*/*}" + if git remote -v 2>/dev/null | grep "https://github.com/nodenv/nodenv.git"; then + echo "Trying to update with git..." + git pull -q --tags origin master + cd .. + ynh_nodejs_try_bash_extension + fi + popd + else + ynh_print_info --message="Installing nodenv with git..." + mkdir -p $nodenv_install_dir + pushd $nodenv_install_dir + git init -q + git remote add -f -t master origin https://github.com/nodenv/nodenv.git > /dev/null 2>&1 + git checkout -q -b master origin/master + ynh_nodejs_try_bash_extension + nodenv=$nodenv_install_dir/bin/nodenv + popd fi - # Modify the default N_PREFIX in n script - ynh_replace_string --match_string="^N_PREFIX=\${N_PREFIX-.*}$" --replace_string="N_PREFIX=\${N_PREFIX-$N_PREFIX}" --target_file="$n_install_dir/bin/n" + node_build="$(command -v "$nodenv_install_dir"/plugins/*/bin/nodenv-install nodenv-install | head -1)" + if [ -n "$node_build" ]; then + ynh_print_info --message="\`nodenv install' command already available in \`$node_build'." + pushd "${node_build%/*/*}" + if git remote -v 2>/dev/null | grep "https://github.com/nodenv/node-build.git"; then + ynh_print_info --message="Trying to update nodenv with git..." + git pull -q origin master + fi + popd + else + ynh_print_info --message="Installing node-build with git..." + mkdir -p "${nodenv_install_dir}/plugins" + git clone -q https://github.com/nodenv/node-build.git "${nodenv_install_dir}/plugins/node-build" + fi + + nodenv_alias="$(command -v "$nodenv_install_dir"/plugins/*/bin/nodenv-alias nodenv-alias | head -1)" + if [ -n "$nodenv_alias" ]; then + ynh_print_info --message="\`nodenv alias' command already available in \`$nodenv_alias'." + pushd "${nodenv_alias%/*/*}" + if git remote -v 2>/dev/null | grep "https://github.com/nodenv/nodenv-aliases.git"; then + ynh_print_info --message="Trying to update nodenv-aliases with git..." + git pull -q origin master + fi + popd + else + ynh_print_info --message="Installing nodenv-aliases with git..." + mkdir -p "${nodenv_install_dir}/plugins" + git clone -q https://github.com/nodenv/nodenv-aliases.git "${nodenv_install_dir}/plugins/nodenv-aliase" + fi + + nodenv_latest="$(command -v "$nodenv_install_dir"/plugins/*/bin/nodenv-latest nodenv-latest | head -1)" + if [ -n "$nodenv_latest" ]; then + ynh_print_info --message="\`nodenv latest' command already available in \`$nodenv_latest'." + pushd "${nodenv_latest%/*/*}" + if git remote -v 2>/dev/null | grep "https://github.com/momo-lab/xxenv-latest.git"; then + ynh_print_info --message="Trying to update xxenv-latest with git..." + git pull -q origin master + fi + popd + else + ynh_print_info --message="Installing xxenv-latest with git..." + mkdir -p "${nodenv_install_dir}/plugins" + git clone -q https://github.com/momo-lab/xxenv-latest.git "${nodenv_install_dir}/plugins/xxenv-latest" + fi + + # Enable caching + mkdir -p "${nodenv_install_dir}/cache" + + # Create shims directory if needed + mkdir -p "${nodenv_install_dir}/shims" # Restore /usr/local/bin in PATH PATH=$CLEAR_PATH - # And replace the old node binary. + # And replace the old Node.js binary test -x /usr/bin/node_n && mv /usr/bin/node_n /usr/bin/node test -x /usr/bin/npm_n && mv /usr/bin/npm_n /usr/bin/npm - # Install the requested version of nodejs - uname=$(uname --machine) - if [[ $uname =~ aarch64 || $uname =~ arm64 ]] - then - n $nodejs_version --arch=arm64 - else - n $nodejs_version - fi - - # Find the last "real" version for this major version of node. - real_nodejs_version=$(find $node_version_path/$nodejs_version* -maxdepth 0 | sort --version-sort | tail --lines=1) - real_nodejs_version=$(basename $real_nodejs_version) - - # Create a symbolic link for this major version if the file doesn't already exist - if [ ! -e "$node_version_path/$nodejs_version" ] - then - ln --symbolic --force --no-target-directory $node_version_path/$real_nodejs_version $node_version_path/$nodejs_version - fi - - # Store the ID of this app and the version of node requested for it - echo "$YNH_APP_INSTANCE_NAME:$nodejs_version" | tee --append "$n_install_dir/ynh_app_version" + # Install the requested version of Node.js + local final_nodejs_version=$(nodenv latest --print $nodejs_version) + ynh_print_info --message="Installation of Node.js-$nodejs_version" + nodenv install --skip-existing $final_nodejs_version > /dev/null 2>&1 # Store nodejs_version into the config of this app - ynh_app_setting_set --app=$app --key=nodejs_version --value=$nodejs_version + ynh_app_setting_set --app=$YNH_APP_INSTANCE_NAME --key=nodejs_version --value=$final_nodejs_version - # Build the update script and set the cronjob - ynh_cron_upgrade_node + # Remove app virtualenv + if `nodenv alias --list | grep --quiet "$YNH_APP_INSTANCE_NAME " 1>/dev/null 2>&1` + then + nodenv alias $YNH_APP_INSTANCE_NAME --remove + fi - ynh_use_nodejs + # Create app virtualenv + nodenv alias $YNH_APP_INSTANCE_NAME $nodejs_version + + # Cleanup Node.js versions + ynh_cleanup_nodejs + + # Set environment for Node.js users + echo "#nodenv +export NODENV_ROOT=$nodenv_install_dir +export PATH=\"$nodenv_install_dir/bin:$PATH\" +eval \"\$(nodenv init -)\" +#nodenv" > /etc/profile.d/nodenv.sh + + # Load the environment + eval "$(nodenv init -)" } -# Remove the version of node used by the app. +# Remove the version of Node.js used by the app. +# +# This helper will also cleanup Node.js versions # # usage: ynh_remove_nodejs -# -# This helper will check if another app uses the same version of node. -# - If not, this version of node will be removed. -# - If no other app uses node, n will be also removed. -# -# Requires YunoHost version 2.7.12 or higher. ynh_remove_nodejs () { - nodejs_version=$(ynh_app_setting_get --app=$app --key=nodejs_version) + local nodejs_version=$(ynh_app_setting_get --app=$YNH_APP_INSTANCE_NAME --key=nodejs_version) + + # Load nodenv path in PATH + local CLEAR_PATH="$nodenv_install_dir/bin:$PATH" + + # Remove /usr/local/bin in PATH in case of Node.js prior installation + PATH=$(echo $CLEAR_PATH | sed 's@/usr/local/bin:@@') + + nodenv alias $YNH_APP_INSTANCE_NAME --remove # Remove the line for this app - sed --in-place "/$YNH_APP_INSTANCE_NAME:$nodejs_version/d" "$n_install_dir/ynh_app_version" + ynh_app_setting_delete --app=$YNH_APP_INSTANCE_NAME --key=nodejs_version - # If no other app uses this version of nodejs, remove it. - if ! grep --quiet "$nodejs_version" "$n_install_dir/ynh_app_version" - then - $n_install_dir/bin/n rm $nodejs_version - fi + # Cleanup Node.js versions + ynh_cleanup_nodejs +} - # If no other app uses n, remove n - if [ ! -s "$n_install_dir/ynh_app_version" ] +# Remove no more needed versions of Node.js used by the app. +# +# This helper will check what Node.js version are no more required, +# and uninstall them +# If no app uses Node.js, nodenv will be also removed. +# +# usage: ynh_cleanup_nodejs +ynh_cleanup_nodejs () { + + # List required Node.js versions + local installed_apps=$(yunohost app list | grep -oP 'id: \K.*$') + local required_nodejs_versions="" + for installed_app in $installed_apps + do + local installed_app_nodejs_version=$(ynh_app_setting_get --app=$installed_app --key="nodejs_version") + if [[ $installed_app_nodejs_version ]] + then + required_nodejs_versions="${installed_app_nodejs_version}\n${required_nodejs_versions}" + fi + done + + # Remove no more needed Node.js versions + local installed_nodejs_versions=$(nodenv versions --bare --skip-aliases | grep -Ev '/') + for installed_nodejs_version in $installed_nodejs_versions + do + if ! `echo ${required_nodejs_versions} | grep "${installed_nodejs_version}" 1>/dev/null 2>&1` + then + ynh_print_info --message="Removing of Node.js-$installed_nodejs_version" + $nodenv_install_dir/bin/nodenv uninstall --force $installed_nodejs_version + fi + done + + # If none Node.js version is required + if [[ ! $required_nodejs_versions ]] then + # Remove nodenv environment configuration + ynh_print_info --message="Removing of nodenv-"$nodenv_version + ynh_secure_remove --file="$nodenv_install_dir" + ynh_secure_remove --file="/etc/profile.d/nodenv.sh" + + # Remove previous n Version Management + n_install_dir="/opt/node_n" ynh_secure_remove --file="$n_install_dir" ynh_secure_remove --file="/usr/local/n" - sed --in-place "/N_PREFIX/d" /root/.bashrc - rm --force /etc/cron.daily/node_update + sed --in-place "/$n_install_dir/d" /root/.bashrc + ynh_secure_remove --file="/etc/cron.daily/node_update" fi } - -# Set a cron design to update your node versions -# -# [internal] -# -# This cron will check and update all minor node versions used by your apps. -# -# usage: ynh_cron_upgrade_node -# -# Requires YunoHost version 2.7.12 or higher. -ynh_cron_upgrade_node () { - # Build the update script - cat > "$n_install_dir/node_update.sh" << EOF -#!/bin/bash - -version_path="$node_version_path" -n_install_dir="$n_install_dir" - -# Log the date -date - -# List all real installed version of node -all_real_version="\$(find \$version_path/* -maxdepth 0 -type d | sed "s@\$version_path/@@g")" - -# Keep only the major version number of each line -all_real_version=\$(echo "\$all_real_version" | sed 's/\..*\$//') - -# Remove double entries -all_real_version=\$(echo "\$all_real_version" | sort --unique) - -# Read each major version -while read version -do - echo "Update of the version \$version" - sudo \$n_install_dir/bin/n \$version - - # Find the last "real" version for this major version of node. - real_nodejs_version=\$(find \$version_path/\$version* -maxdepth 0 | sort --version-sort | tail --lines=1) - real_nodejs_version=\$(basename \$real_nodejs_version) - - # Update the symbolic link for this version - sudo ln --symbolic --force --no-target-directory \$version_path/\$real_nodejs_version \$version_path/\$version -done <<< "\$(echo "\$all_real_version")" -EOF - - chmod +x "$n_install_dir/node_update.sh" - - # Build the cronjob - cat > "/etc/cron.daily/node_update" << EOF -#!/bin/bash - -$n_install_dir/node_update.sh >> $n_install_dir/node_update.log -EOF - - chmod +x "/etc/cron.daily/node_update" -}