bramw_baserow/deploy/plugins/install_plugin.sh

286 lines
9.6 KiB
Bash
Executable File

#!/bin/bash
# Bash strict mode: http://redsymbol.net/articles/unofficial-bash-strict-mode/
set -euo pipefail
show_help(){
echo """
Usage: install_plugin.sh [-d] [-f <plugin folder>]
-f, --folder <plugin folder> The folder where the plugin to install is located.
-g, --git <https git repo url> An url to a git repo containing the plugin to install.
-u, --url <plugin url> An url to a .tar.gz file containing the plugin to install.
--hash <plugin hash> If provided the plugin's contents will be hashed and checked against this hash, if they do not match the install will fail.
-d, --dev Install the plugin for development.
-r, --runtime If provided any runtime plugin setup scripts will be run if found. Should never be set if being called from a Dockerfile.
-o, --overwrite If provided any existing plugin of the same name will be overwritten and force re-installed, built and/or setup.
-h, --help Show this help message and exit.
A Baserow plugin is a folder named after the plugin, containing one or both of the
following sub-folders:
- a 'backend' folder, containing the plugin's backend code. This must be a valid Python
package.
- If a 'backend/build.sh' script exists this will be executed to perform any
additional plugin installation tasks.
- a 'web-frontend' folder, containing the plugin's backend code. This must be a valid
node package.
- If a 'web-frontend/build.sh' script exists this will be executed to perform any
additional plugin installation tasks.
"""
}
source /baserow/plugins/utils.sh
# First parse the args using getopt
VALID_ARGS=$(getopt -o u:dhf:rg:o --long hash:,url:,git:,help,dev,folder:,runtime,overwrite -- "$@")
if [[ $? -ne 0 ]]; then
error "Incorrect options provided."
show_help
exit 1;
fi
eval set -- "$VALID_ARGS"
if [[ "$*" == "--" ]]; then
error "No arguments provided."
show_help
exit 1;
fi
# Next loop over the user provided args and set flags accordingly.
dev=false
url=
folder=
hash=
git=
exclusive_flag_count=0
runtime=
overwrite=
# shellcheck disable=SC2078
while [ : ]; do
case "$1" in
-d | --dev)
log "Installing plugin in dev mode."
dev=true
shift
;;
-f | --folder)
folder="$2"
shift 2
exclusive_flag_count=$((exclusive_flag_count+1))
;;
--hash)
hash="$2"
shift 2
;;
-u | --url)
url="$2"
shift 2
exclusive_flag_count=$((exclusive_flag_count+1))
;;
-g | --git)
git="$2"
shift 2
exclusive_flag_count=$((exclusive_flag_count+1))
;;
-r | --runtime)
runtime="true"
shift
;;
-o | --overwrite)
overwrite="true"
shift
;;
-h | --help)
show_help
exit 0;
;;
--)
shift
break
;;
esac
done
if [[ "$exclusive_flag_count" -eq "0" ]]; then
error "You must provide one of the following flags: --folder, --url or --git"
show_help
exit 1;
fi
if [[ "$exclusive_flag_count" -gt "1" ]]; then
echo "You must provide only one of the following flags: --folder, --url or --git"
show_help
exit 1;
fi
# --git was provided, download the plugin using git..
if [[ -n "$git" ]]; then
log "Downloading plugin from git repo at $git."
temp_work_dir=$(mktemp -d)
cd "$temp_work_dir"
git clone "$git" .
dirs=("$temp_work_dir"/plugins/*/)
num_dirs=${#dirs[@]}
if [[ "$num_dirs" -ne 1 ]]; then
error "$git does not look like a Baserow plugin. The plugins/ subdirectory in the repo must contain exactly one sub-directory."
exit 1;
fi
folder=${dirs[0]}
fi
# --url was set, download the url, untar it to a temp dir, and verify it only has one
# sub dir.
if [[ -n "$url" ]]; then
log "Downloading and extracting plugin from $url."
temp_work_dir=$(mktemp -d)
curl -Ls "$url" | tar xz -C "$temp_work_dir"
dirs=("$temp_work_dir"/*/plugins/*/)
num_dirs=${#dirs[@]}
if [[ "$num_dirs" -ne 1 ]]; then
error "$url does not look like a Baserow plugin. The plugin archive must contain a plugins/ sub-directory itself containing exactly one sub-directory for the plugin."
exit 1;
fi
folder=${dirs[0]}
fi
# copy the plugin at the folder location into the plugin dir if it has not been already.
plugin_name="$(basename -- "$folder")"
plugin_install_dir="$BASEROW_PLUGIN_DIR/$plugin_name"
if [[ ! "$folder" -ef "$plugin_install_dir" ]]; then
if [[ ! -d "$plugin_install_dir" || "$overwrite" == "true" ]]; then
log "Copying plugin $plugin_name into plugins folder at $plugin_install_dir."
mkdir -p "$BASEROW_PLUGIN_DIR"
rm -rf "$plugin_install_dir"
cp -Tr "$folder" "$plugin_install_dir"
else
log "Found an existing plugin installed at $plugin_install_dir, not overwriting it
as the --overwrite flag was not provided to this script."
fi
folder="$BASEROW_PLUGIN_DIR/$plugin_name"
fi
chown -R "$DOCKER_USER": "$folder"
# Now we've copied the plugin into the plugin dir we can delete the tmp download dir
# if we used it.
if [[ -n "${temp_work_dir:-}" ]]; then
rm -rf "$temp_work_dir"
fi
# --hash was set, hash the plugin folder and check it matches.
if [[ -n "$hash" ]]; then
plugin_hash=$(find "$folder" -type f -print0 | sort -z | xargs -0 sha1sum | sha1sum | cut -d " " -f 1 )
if [[ "$plugin_hash" != "$hash" ]]; then
error "Plugin $plugin_name does not match the provided hash. This could mean it has been maliciously modified and it is not safe to install."
error "The plugins hash was: $plugin_hash"
error "Instead we expected : $hash"
exit 1;
else
log "Plugin ${plugin_name}'s hash matches provided hash."
fi
fi
check_and_run_script(){
if [[ -f "$1/$2" ]]; then
log "Running ${plugin_name}'s custom $2 script"
bash "$1/$2"
fi
}
PLUGIN_BACKEND_FOLDER="$folder/backend"
run_as_docker_user(){
CURRENT_USER=$(whoami)
if [[ "$CURRENT_USER" != "$DOCKER_USER" ]]; then
gosu "$DOCKER_USER" "$@"
else
"$@"
fi
}
# Make sure we create the container markers folder which we will use to check if a
# plugin has been installed or not already inside this container.
mkdir -p /baserow/container_markers
# Install the backend plugin
if [[ -d "/baserow/backend" && -d "$PLUGIN_BACKEND_FOLDER" ]]; then
log "Found a backend app for ${plugin_name}."
BACKEND_BUILT_MARKER=/baserow/container_markers/$plugin_name.backend-built
if [[ ! -f "$BACKEND_BUILT_MARKER" || "$overwrite" == "true" ]]; then
log "Building ${plugin_name}'s backend app."
. /baserow/venv/bin/activate
cd /baserow/backend
if [[ "$dev" == true ]]; then
run_as_docker_user pip3 install -e "$PLUGIN_BACKEND_FOLDER"
else
run_as_docker_user pip3 install "$PLUGIN_BACKEND_FOLDER"
fi
check_and_run_script "$PLUGIN_BACKEND_FOLDER" build.sh
touch "$BACKEND_BUILT_MARKER"
else
log "Skipping install of ${plugin_name}'s backend app as it is already installed."
fi
BACKEND_RUNTIME_SETUP_MARKER=/baserow/container_markers/$plugin_name.backend-runtime-setup
if [[ ( ! -f "$BACKEND_RUNTIME_SETUP_MARKER" || "$overwrite" == "true" ) && $runtime == "true" ]]; then
check_and_run_script "$PLUGIN_BACKEND_FOLDER" runtime_setup.sh
touch "$BACKEND_RUNTIME_SETUP_MARKER"
else
log "Skipping runtime setup of ${plugin_name}'s backend app."
fi
fi
# Install the web-frontend plugin
PLUGIN_WEBFRONTEND_FOLDER="$folder/web-frontend"
if [[ -d "/baserow/web-frontend" && -d "$PLUGIN_WEBFRONTEND_FOLDER" ]]; then
log "Found a web-frontend module for ${plugin_name}."
WEBFRONTEND_BUILT_MARKER=/baserow/container_markers/$plugin_name.web-frontend-built
if [[ ! -f "$WEBFRONTEND_BUILT_MARKER" || "$overwrite" == "true" ]]; then
log "Building ${plugin_name}'s web-frontend module."
cd /baserow/web-frontend
run_as_docker_user yarn add "$PLUGIN_WEBFRONTEND_FOLDER" && yarn cache clean
# We only load web-frontend modules into nuxt which have a built marker. Touch
# it now so the build-local picks up the newly installed module and builds it.
touch "$WEBFRONTEND_BUILT_MARKER"
function finish {
rm -f "$WEBFRONTEND_BUILT_MARKER"
}
trap finish EXIT
if [[ "$dev" != true ]]; then
run_as_docker_user /baserow/web-frontend/docker/docker-entrypoint.sh build-local
else
log "Installing plugins dev dependencies..."
# In dev mode yarn install the plugins own dependencies so they are available
# for linting the plugin etc.
cd "$PLUGIN_WEBFRONTEND_FOLDER"
run_as_docker_user yarn install
cd /baserow/web-frontend
fi
check_and_run_script "$PLUGIN_WEBFRONTEND_FOLDER" build.sh
trap - EXIT
else
log "Skipping build of $plugin_name web-frontend module as it has already been built."
fi
WEBFRONTEND_RUNTIME_SETUP_MARKER=/baserow/container_markers/$plugin_name.web-frontend-runtime-setup
if [[ ( -f "$WEBFRONTEND_RUNTIME_SETUP_MARKER" || "$overwrite" == "true" ) && $runtime == "true" ]]; then
check_and_run_script "$PLUGIN_WEBFRONTEND_FOLDER" runtime_setup.sh
touch "$WEBFRONTEND_RUNTIME_SETUP_MARKER"
else
log "Skipping runtime setup of ${plugin_name}'s web-frontend module."
fi
fi
log "Fixing ownership of plugins from $(id -u) to $DOCKER_USER in $BASEROW_PLUGIN_DIR"
chown -R "$DOCKER_USER": "$BASEROW_PLUGIN_DIR"
chown -R "$DOCKER_USER": /baserow/container_markers/
log_success "Finished setting up ${plugin_name} successfully."