Files
ProxmoxVE/tools/addon/arcane.sh
T
CanbiZ (MickLesk) 3e7fcf1350 Arcane: write both env names, stop shipping the unused .env (#18466)
The compose file from main now uses ARCANE_ENCRYPTION_KEY and
ARCANE_PROJECTS_DIRECTORY, which the latest image (2.17.3) does not read,
so it panicked on a missing ENCRYPTION_KEY. Arcane 3 in turn refuses to
start when only the old names are set. Install and update now keep both
names in compose.yaml, which also carries existing installs over to 3.

The .env was never referenced by the compose file, but compose parses it,
and the unanchored ENCRYPTION_KEY sed turned the renamed line into
ARCANE_#ENCRYPTION_KEY and broke every install. New installs no longer
download it; update repairs the broken line.
2026-11-05 11:56:39 +01:00

214 lines
6.9 KiB
Bash

#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: summoningpixels
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/getarcaneapp/arcane
if ! command -v curl &>/dev/null; then
printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2
if [[ -f /etc/alpine-release ]]; then
apk update >/dev/null 2>&1
apk add --no-cache curl >/dev/null 2>&1
else
apt-get update >/dev/null 2>&1
apt-get install -y curl >/dev/null 2>&1
fi
fi
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/core/core.func")
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/lib/tools.func")
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/core/error_handler.func")
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/api/api.func") 2>/dev/null || true
declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "arcane" "addon"
# Enable error handling
set -Eeuo pipefail
trap 'error_handler' ERR
# ==============================================================================
# CONFIGURATION
# ==============================================================================
APP="Arcane"
APP_TYPE="addon"
INSTALL_PATH="/opt/arcane"
COMPOSE_FILE="${INSTALL_PATH}/compose.yaml"
ENV_FILE="${INSTALL_PATH}/.env"
DEFAULT_PORT=3552
# Initialize all core functions (colors, formatting, icons, STD mode)
load_functions
# ==============================================================================
# UNINSTALL
# ==============================================================================
function uninstall() {
msg_info "Uninstalling ${APP}"
if [[ -f "$COMPOSE_FILE" ]]; then
msg_info "Stopping and removing Docker containers"
cd "$INSTALL_PATH"
$STD docker compose down --volumes --remove-orphans
msg_ok "Stopped and removed Docker containers"
fi
rm -rf "$INSTALL_PATH"
rm -f "/usr/local/bin/update_arcane"
msg_ok "${APP} has been uninstalled"
}
# ==============================================================================
# UPDATE
# ==============================================================================
# Arcane 3 needs the ARCANE_* names, the current latest image reads only the old ones.
function sync_env_names() {
local key
for key in ENCRYPTION_KEY PROJECTS_DIRECTORY; do
grep -q "^[[:space:]]*- ARCANE_${key}=" "$COMPOSE_FILE" || sed -i "/^[[:space:]]*- ${key}=/{p;s/- ${key}=/- ARCANE_${key}=/;}" "$COMPOSE_FILE"
grep -q "^[[:space:]]*- ${key}=" "$COMPOSE_FILE" || sed -i "/^[[:space:]]*- ARCANE_${key}=/{p;s/- ARCANE_${key}=/- ${key}=/;}" "$COMPOSE_FILE"
done
if [[ -f "$ENV_FILE" ]]; then
sed -i 's/^ARCANE_#/#ARCANE_/' "$ENV_FILE"
fi
}
function update() {
chown -R 65532:65532 /etc/arcane/projects /etc/arcane/builds 2>/dev/null || true
sync_env_names
msg_info "Pulling latest ${APP} image"
cd "$INSTALL_PATH"
$STD docker compose pull
msg_ok "Pulled latest image"
msg_info "Restarting ${APP}"
$STD docker compose up -d --remove-orphans
msg_ok "Restarted ${APP}"
msg_ok "Updated successfully"
exit
}
# ==============================================================================
# INSTALL
# ==============================================================================
function install() {
ensure_docker
msg_info "Creating install directory"
mkdir -p "$INSTALL_PATH"
msg_ok "Created ${INSTALL_PATH}"
# Generate secrets and config values
local ENCRYPTION_KEY PROJ_DIR BUILDS_DIR
ENCRYPTION_KEY=$(random_password 32)
PROJ_DIR="/etc/arcane/projects"
BUILDS_DIR="/etc/arcane/builds"
# Arcane drops root and runs as UID/GID 65532 by default (Dockerfile default,
# see backend/pkg/libarcane/startup/runtime_identity.go), so bind-mounted
# host directories must be owned by that UID or the container can't write to them.
msg_info "Creating stacks directory"
mkdir -p "$PROJ_DIR"
chown -R 65532:65532 "$PROJ_DIR"
msg_ok "Created ${PROJ_DIR}"
msg_info "Creating builds directory"
mkdir -p "$BUILDS_DIR"
chown -R 65532:65532 "$BUILDS_DIR"
msg_ok "Created ${BUILDS_DIR}"
msg_info "Downloading Docker Compose file"
curl -fsSL "https://raw.githubusercontent.com/getarcaneapp/arcane/refs/heads/main/docker/examples/compose.basic.yaml" -o "$COMPOSE_FILE"
msg_ok "Downloaded Docker Compose file"
msg_info "Configuring compose file"
sed -i '/^[[:space:]]*#/!s|/host/path/to/projects|'"$PROJ_DIR"'|g' "$COMPOSE_FILE"
sed -i '/^[[:space:]]*#/!s|/host/path/to/builds|'"$BUILDS_DIR"'|g' "$COMPOSE_FILE"
sed -i '/^[[:space:]]*#/!s|ENCRYPTION_KEY=.*|ENCRYPTION_KEY='"$ENCRYPTION_KEY"'|g' "$COMPOSE_FILE"
sync_env_names
msg_ok "Configured compose file"
msg_info "Starting ${APP}"
cd "$INSTALL_PATH"
$STD docker compose up -d
msg_ok "Started ${APP}"
# Create update script
msg_info "Creating update script"
cat <<'UPDATEEOF' >/usr/local/bin/update_arcane
#!/usr/bin/env bash
# Arcane Update Script
type=update bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/arcane.sh)"
UPDATEEOF
chmod +x /usr/local/bin/update_arcane
msg_ok "Created update script (/usr/local/bin/update_arcane)"
echo ""
msg_ok "${APP} is reachable at: ${BL}http://${LOCAL_IP}:${DEFAULT_PORT}${CL}"
echo ""
echo -e "Arcane Credentials"
echo -e "=================="
echo -e "User: arcane"
echo -e "Password: arcane-admin"
echo ""
msg_warn "On first access, you'll be prompted to change your password."
}
# ==============================================================================
# MAIN
# ==============================================================================
# Handle type=update (called from update script)
if [[ "${type:-}" == "update" ]]; then
header_info
if [[ -f "$COMPOSE_FILE" ]]; then
update
else
msg_error "${APP} is not installed. Nothing to update."
exit 233
fi
exit 0
fi
header_info
confirm_not_pve_host
get_lxc_ip
# Check if already installed
if [[ -f "$COMPOSE_FILE" ]]; then
msg_warn "${APP} is already installed."
echo ""
echo -n "${TAB}Uninstall ${APP}? (y/N): "
read -r uninstall_prompt
if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then
uninstall
exit 0
fi
echo -n "${TAB}Update ${APP}? (y/N): "
read -r update_prompt
if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then
update
exit 0
fi
msg_warn "No action selected. Exiting."
exit 0
fi
# Fresh installation
msg_warn "${APP} is not installed."
echo ""
echo -e "${TAB}${INFO} This will install:"
echo -e "${TAB} - Arcane (via Docker Compose)"
echo ""
echo -n "${TAB}Install ${APP}? (y/N): "
read -r install_prompt
if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then
install
else
msg_warn "Installation cancelled. Exiting."
exit 0
fi