2022-03-29 20:40:04 +00:00
|
|
|
#!/usr/bin/env bash
|
2022-09-14 01:37:32 +00:00
|
|
|
# Required Notice: Copyright
|
|
|
|
# Umbrel (https://umbrel.com)
|
|
|
|
|
2022-03-29 20:40:04 +00:00
|
|
|
set -euo pipefail
|
|
|
|
|
2022-03-30 19:26:01 +00:00
|
|
|
# use greadlink instead of readlink on osx
|
|
|
|
if [[ "$(uname)" == "Darwin" ]]; then
|
|
|
|
rdlk=greadlink
|
|
|
|
else
|
|
|
|
rdlk=readlink
|
|
|
|
fi
|
|
|
|
|
|
|
|
ROOT_FOLDER="$($rdlk -f $(dirname "${BASH_SOURCE[0]}")/..)"
|
2022-08-04 19:17:21 +00:00
|
|
|
REPO_ID="$(echo -n "https://github.com/meienberger/runtipi-appstore" | sha256sum | awk '{print $1}')"
|
2022-03-29 20:40:04 +00:00
|
|
|
STATE_FOLDER="${ROOT_FOLDER}/state"
|
|
|
|
|
|
|
|
show_help() {
|
2022-05-18 18:13:58 +00:00
|
|
|
cat <<EOF
|
2022-03-29 20:40:04 +00:00
|
|
|
app 0.0.1
|
|
|
|
|
|
|
|
CLI for managing Tipi apps
|
|
|
|
|
|
|
|
Usage: app <command> <app> [<arguments>]
|
|
|
|
|
|
|
|
Commands:
|
|
|
|
install Pulls down images for an app and starts it
|
|
|
|
uninstall Removes images and destroys all data for an app
|
|
|
|
stop Stops an installed app
|
|
|
|
start Starts an installed app
|
2022-09-19 13:15:44 +00:00
|
|
|
compose Passes all arguments to Docker Compose
|
2022-03-29 20:40:04 +00:00
|
|
|
ls-installed Lists installed apps
|
|
|
|
EOF
|
|
|
|
}
|
|
|
|
|
|
|
|
# Get field from json file
|
|
|
|
function get_json_field() {
|
2022-05-18 18:13:58 +00:00
|
|
|
local json_file="$1"
|
|
|
|
local field="$2"
|
2022-03-29 20:40:04 +00:00
|
|
|
|
2022-05-18 18:13:58 +00:00
|
|
|
echo $(jq -r ".${field}" "${json_file}")
|
2022-03-29 20:40:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
list_installed_apps() {
|
|
|
|
str=$(get_json_field ${STATE_FOLDER}/apps.json installed)
|
|
|
|
echo $str
|
|
|
|
}
|
|
|
|
|
|
|
|
if [ -z ${1+x} ]; then
|
|
|
|
command=""
|
|
|
|
else
|
|
|
|
command="$1"
|
|
|
|
fi
|
|
|
|
|
|
|
|
# Lists installed apps
|
|
|
|
if [[ "$command" = "ls-installed" ]]; then
|
|
|
|
list_installed_apps
|
|
|
|
|
|
|
|
exit
|
|
|
|
fi
|
|
|
|
|
|
|
|
if [ -z ${2+x} ]; then
|
|
|
|
show_help
|
|
|
|
exit 1
|
|
|
|
else
|
2022-08-04 19:17:21 +00:00
|
|
|
|
2022-03-29 20:40:04 +00:00
|
|
|
app="$2"
|
2022-05-19 21:05:51 +00:00
|
|
|
root_folder_host="${3:-$ROOT_FOLDER}"
|
2022-08-04 19:17:21 +00:00
|
|
|
repo_id="${4:-$REPO_ID}"
|
2022-05-19 21:05:51 +00:00
|
|
|
|
2022-08-04 19:17:21 +00:00
|
|
|
if [[ -z "${repo_id}" ]]; then
|
2022-08-03 20:36:27 +00:00
|
|
|
echo "Error: Repo id not provided"
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
|
2022-08-09 18:44:07 +00:00
|
|
|
if [[ -z "${root_folder_host}" ]]; then
|
|
|
|
echo "Error: Root folder not provided"
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
app_dir="${ROOT_FOLDER}/apps/${app}"
|
|
|
|
|
|
|
|
if [[ ! -d "${app_dir}" ]]; then
|
|
|
|
# copy from repo
|
|
|
|
echo "Copying app from repo"
|
|
|
|
mkdir -p "${app_dir}"
|
|
|
|
cp -r "${ROOT_FOLDER}/repos/${repo_id}/apps/${app}"/* "${app_dir}"
|
|
|
|
fi
|
|
|
|
|
2022-03-29 20:40:04 +00:00
|
|
|
app_data_dir="${ROOT_FOLDER}/app-data/${app}"
|
|
|
|
|
|
|
|
if [[ -z "${app}" ]] || [[ ! -d "${app_dir}" ]]; then
|
|
|
|
echo "Error: \"${app}\" is not a valid app"
|
|
|
|
exit 1
|
|
|
|
fi
|
2022-05-17 16:54:04 +00:00
|
|
|
|
2022-03-29 20:40:04 +00:00
|
|
|
fi
|
|
|
|
|
|
|
|
if [ -z ${3+x} ]; then
|
|
|
|
args=""
|
|
|
|
else
|
|
|
|
args="${@:3}"
|
|
|
|
fi
|
|
|
|
|
|
|
|
compose() {
|
|
|
|
local app="${1}"
|
|
|
|
shift
|
2022-05-13 06:47:18 +00:00
|
|
|
|
|
|
|
local architecture="$(uname -m)"
|
|
|
|
|
|
|
|
if [[ "$architecture" == "aarch64" ]]; then
|
|
|
|
architecture="arm64"
|
|
|
|
fi
|
|
|
|
|
2022-03-29 20:40:04 +00:00
|
|
|
# App data folder
|
|
|
|
local env_file="${ROOT_FOLDER}/.env"
|
|
|
|
local app_compose_file="${app_dir}/docker-compose.yml"
|
2022-05-13 06:47:18 +00:00
|
|
|
|
2022-05-13 06:53:35 +00:00
|
|
|
# Pick arm architecture if running on arm and if the app has a docker-compose.arm.yml file
|
|
|
|
if [[ "$architecture" == "arm"* ]] && [[ -f "${app_dir}/docker-compose.arm.yml" ]]; then
|
2022-05-13 06:47:18 +00:00
|
|
|
app_compose_file="${app_dir}/docker-compose.arm.yml"
|
2022-05-18 18:13:58 +00:00
|
|
|
fi
|
2022-05-13 06:47:18 +00:00
|
|
|
|
2022-08-04 19:17:21 +00:00
|
|
|
local common_compose_file="${ROOT_FOLDER}/repos/${repo_id}/apps/docker-compose.common.yml"
|
2022-03-29 20:40:04 +00:00
|
|
|
|
|
|
|
# Vars to use in compose file
|
2022-05-17 16:54:04 +00:00
|
|
|
export APP_DATA_DIR="${root_folder_host}/app-data/${app}"
|
2022-04-07 08:34:18 +00:00
|
|
|
export APP_DIR="${app_dir}"
|
2022-05-17 17:55:44 +00:00
|
|
|
export ROOT_FOLDER_HOST="${root_folder_host}"
|
2022-04-12 20:07:39 +00:00
|
|
|
export ROOT_FOLDER="${ROOT_FOLDER}"
|
|
|
|
|
2022-09-19 13:15:44 +00:00
|
|
|
# Docker Compose does not support multiple env files
|
2022-04-12 20:07:39 +00:00
|
|
|
# --env-file "${env_file}" \
|
2022-03-29 20:40:04 +00:00
|
|
|
|
|
|
|
docker-compose \
|
2022-04-12 20:07:39 +00:00
|
|
|
--env-file "${ROOT_FOLDER}/app-data/${app}/app.env" \
|
2022-03-29 20:40:04 +00:00
|
|
|
--project-name "${app}" \
|
|
|
|
--file "${app_compose_file}" \
|
2022-04-07 08:34:18 +00:00
|
|
|
--file "${common_compose_file}" \
|
2022-03-29 20:40:04 +00:00
|
|
|
"${@}"
|
|
|
|
}
|
|
|
|
|
2022-04-07 08:34:18 +00:00
|
|
|
# Install new app
|
|
|
|
if [[ "$command" = "install" ]]; then
|
|
|
|
compose "${app}" pull
|
|
|
|
|
2022-04-13 21:47:07 +00:00
|
|
|
# Copy default data dir to app data dir if it exists
|
2022-08-09 18:44:07 +00:00
|
|
|
if [[ -d "${app_dir}/data" ]]; then
|
|
|
|
cp -r "${app_dir}/data" "${app_data_dir}/data"
|
2022-04-13 21:47:07 +00:00
|
|
|
fi
|
2022-04-13 18:34:24 +00:00
|
|
|
|
2022-05-17 18:18:56 +00:00
|
|
|
# Remove all .gitkeep files from app data dir
|
|
|
|
find "${app_data_dir}" -name ".gitkeep" -exec rm -f {} \;
|
|
|
|
|
2022-05-17 16:54:04 +00:00
|
|
|
chown -R "1000:1000" "${app_data_dir}"
|
|
|
|
|
2022-04-07 08:34:18 +00:00
|
|
|
compose "${app}" up -d
|
|
|
|
exit
|
|
|
|
fi
|
|
|
|
|
2022-03-29 20:40:04 +00:00
|
|
|
# Removes images and destroys all data for an app
|
|
|
|
if [[ "$command" = "uninstall" ]]; then
|
|
|
|
echo "Removing images for app ${app}..."
|
2022-05-17 16:54:04 +00:00
|
|
|
|
2022-05-23 19:56:18 +00:00
|
|
|
compose "${app}" up --detach
|
|
|
|
compose "${app}" down --rmi all --remove-orphans
|
2022-03-29 20:40:04 +00:00
|
|
|
|
|
|
|
echo "Deleting app data for app ${app}..."
|
|
|
|
if [[ -d "${app_data_dir}" ]]; then
|
2022-05-17 16:54:04 +00:00
|
|
|
rm -rf "${app_data_dir}"
|
2022-03-29 20:40:04 +00:00
|
|
|
fi
|
|
|
|
|
2022-08-09 18:44:07 +00:00
|
|
|
if [[ -d "${app_dir}" ]]; then
|
|
|
|
rm -rf "${app_dir}"
|
|
|
|
fi
|
|
|
|
|
2022-03-29 20:40:04 +00:00
|
|
|
echo "Successfully uninstalled app ${app}"
|
|
|
|
exit
|
|
|
|
fi
|
|
|
|
|
2022-08-04 19:17:21 +00:00
|
|
|
# Update an app
|
|
|
|
if [[ "$command" = "update" ]]; then
|
|
|
|
compose "${app}" up --detach
|
|
|
|
compose "${app}" down --rmi all --remove-orphans
|
2022-08-09 18:44:07 +00:00
|
|
|
|
|
|
|
# Remove app
|
|
|
|
if [[ -d "${app_dir}" ]]; then
|
|
|
|
rm -rf "${app_dir}"
|
|
|
|
fi
|
|
|
|
|
|
|
|
# Copy app from repo
|
|
|
|
cp -r "${ROOT_FOLDER}/repos/${repo_id}/apps/${app}" "${app_dir}"
|
|
|
|
|
2022-08-04 19:17:21 +00:00
|
|
|
compose "${app}" pull
|
|
|
|
exit
|
|
|
|
fi
|
|
|
|
|
2022-03-29 20:40:04 +00:00
|
|
|
# Stops an installed app
|
|
|
|
if [[ "$command" = "stop" ]]; then
|
|
|
|
echo "Stopping app ${app}..."
|
|
|
|
compose "${app}" rm --force --stop
|
|
|
|
exit
|
|
|
|
fi
|
|
|
|
|
|
|
|
# Starts an installed app
|
|
|
|
if [[ "$command" = "start" ]]; then
|
|
|
|
echo "Starting app ${app}..."
|
|
|
|
compose "${app}" up --detach
|
|
|
|
exit
|
|
|
|
fi
|
|
|
|
|
2022-09-19 13:15:44 +00:00
|
|
|
# Passes all arguments to Docker Compose
|
2022-03-29 20:40:04 +00:00
|
|
|
if [[ "$command" = "compose" ]]; then
|
|
|
|
compose "${app}" ${args}
|
|
|
|
exit
|
|
|
|
fi
|
|
|
|
|
|
|
|
# If we get here it means no valid command was supplied
|
|
|
|
# Show help and exit
|
|
|
|
show_help
|
2022-05-18 18:13:58 +00:00
|
|
|
exit 1
|