mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-14 20:48:17 +08:00
259 lines
8.7 KiB
Bash
Executable File
259 lines
8.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# deploy.sh - Deploy luci-app-device-manager directly to an OpenWrt router for development/testing.
|
|
# SSH authentication is performed once; all deployment commands reuse that connection.
|
|
#
|
|
# Usage:
|
|
# ./tools/deploy.sh [options] <target>
|
|
#
|
|
# Examples:
|
|
# ./tools/deploy.sh 192.168.1.1
|
|
# ./tools/deploy.sh root@192.168.1.1
|
|
# ./tools/deploy.sh -p 2222 root@192.168.1.1
|
|
#
|
|
|
|
set -euo pipefail
|
|
|
|
# Color formatting
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
CYAN='\033[0;36m'
|
|
NC='\033[0m' # No Color
|
|
|
|
print_info() {
|
|
printf "${BLUE}[INFO]${NC} %s\n" "$1"
|
|
}
|
|
|
|
print_success() {
|
|
printf "${GREEN}[SUCCESS]${NC} %s\n" "$1"
|
|
}
|
|
|
|
print_error() {
|
|
printf "${RED}[ERROR]${NC} %s\n" "$1" >&2
|
|
}
|
|
|
|
show_usage() {
|
|
cat <<EOF
|
|
Usage: $0 [options] <target>
|
|
|
|
Arguments:
|
|
<target> Router IP address or SSH user@host (e.g., 192.168.1.1 or root@192.168.1.1)
|
|
|
|
Options:
|
|
-p, --port <port> SSH port number (default: 22)
|
|
-h, --help Show this help message
|
|
|
|
Examples:
|
|
$0 192.168.1.1
|
|
$0 root@192.168.1.1
|
|
$0 -p 2222 root@192.168.31.1
|
|
EOF
|
|
}
|
|
|
|
# Parse options
|
|
SSH_PORT="22"
|
|
TARGET=""
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
-p|--port)
|
|
if [[ $# -lt 2 || ! "$2" =~ ^[0-9]+$ || ${#2} -gt 5 ]] || (( 10#$2 < 1 || 10#$2 > 65535 )); then
|
|
print_error "SSH port must be an integer between 1 and 65535."
|
|
exit 1
|
|
fi
|
|
SSH_PORT="$2"
|
|
shift 2
|
|
;;
|
|
-h|--help)
|
|
show_usage
|
|
exit 0
|
|
;;
|
|
-*)
|
|
print_error "Unknown option: $1"
|
|
show_usage
|
|
exit 1
|
|
;;
|
|
*)
|
|
if [[ -z "$TARGET" ]]; then
|
|
TARGET="$1"
|
|
else
|
|
print_error "Unexpected argument: $1"
|
|
show_usage
|
|
exit 1
|
|
fi
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ -z "$TARGET" ]]; then
|
|
print_error "Target router address is required."
|
|
show_usage
|
|
exit 1
|
|
fi
|
|
|
|
# Normalize SSH target
|
|
if [[ "$TARGET" != *"@"* ]]; then
|
|
SSH_TARGET="root@${TARGET}"
|
|
ROUTER_IP="${TARGET}"
|
|
else
|
|
SSH_TARGET="${TARGET}"
|
|
ROUTER_IP="${TARGET#*@}"
|
|
fi
|
|
|
|
# Check prerequisites
|
|
for cmd in ssh scp mktemp; do
|
|
if ! command -v "$cmd" &>/dev/null; then
|
|
print_error "Required local command '$cmd' is not found in PATH."
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
# Resolve workspace directory
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
|
|
# Verify essential project files exist
|
|
if [[ ! -f "${PROJECT_ROOT}/htdocs/luci-static/resources/view/device-manager/devices.js" ]]; then
|
|
print_error "Missing frontend view file: htdocs/.../devices.js"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -f "${PROJECT_ROOT}/root/usr/share/luci/menu.d/luci-app-device-manager.json" ]]; then
|
|
print_error "Missing menu definition file: root/.../menu.d/luci-app-device-manager.json"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -f "${PROJECT_ROOT}/root/usr/share/rpcd/acl.d/luci-app-device-manager.json" ]]; then
|
|
print_error "Missing ACL definition file: root/.../acl.d/luci-app-device-manager.json"
|
|
exit 1
|
|
fi
|
|
|
|
for resource in model.js service.js snapshot-cache.js feedback.js online-check.js preferences.js table.js device-dialog.js group-dialog.js styles.css; do
|
|
if [[ ! -f "${PROJECT_ROOT}/htdocs/luci-static/resources/device-manager/${resource}" ]]; then
|
|
print_error "Missing frontend resource: device-manager/${resource}"
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
if [[ ! -f "${PROJECT_ROOT}/root/usr/libexec/rpcd/luci.device-manager" || ! -f "${PROJECT_ROOT}/tools/initialize-config.sh" ]]; then
|
|
print_error "Missing rpcd helper or configuration initializer."
|
|
exit 1
|
|
fi
|
|
|
|
# Keep the control socket private and its path short enough for Unix socket limits.
|
|
SSH_CONTROL_DIR="$(mktemp -d /tmp/luci-deploy.XXXXXX)"
|
|
SSH_CONTROL_PATH="${SSH_CONTROL_DIR}/ssh"
|
|
SSH_COMMON_OPTS=(-o ConnectTimeout=5 -o StrictHostKeyChecking=accept-new -o "ControlPath=${SSH_CONTROL_PATH}")
|
|
SSH_OPTS=(-p "${SSH_PORT}" "${SSH_COMMON_OPTS[@]}" -o ControlMaster=no -o BatchMode=yes)
|
|
SCP_OPTS=(-P "${SSH_PORT}" "${SSH_COMMON_OPTS[@]}" -o ControlMaster=no -o BatchMode=yes)
|
|
|
|
cleanup() {
|
|
ssh "${SSH_OPTS[@]}" -O exit "${SSH_TARGET}" >/dev/null 2>&1 || true
|
|
rm -rf -- "${SSH_CONTROL_DIR}"
|
|
}
|
|
trap cleanup EXIT
|
|
trap 'exit 130' INT
|
|
trap 'exit 143' TERM
|
|
|
|
print_info "Connecting to OpenWrt router at ${SSH_TARGET} (port: ${SSH_PORT})..."
|
|
|
|
# Authenticate once and keep the master connection open until cleanup.
|
|
if ! ssh -p "${SSH_PORT}" "${SSH_COMMON_OPTS[@]}" -o BatchMode=no -o ControlPersist=no -M -N -f "${SSH_TARGET}"; then
|
|
print_error "Failed to connect to ${SSH_TARGET} via SSH. Please check network, IP, port, and SSH key/password."
|
|
exit 1
|
|
fi
|
|
|
|
if ! ssh "${SSH_OPTS[@]}" "${SSH_TARGET}" "command -v ip >/dev/null && command -v flock >/dev/null && test -r /usr/share/libubox/jshn.sh"; then
|
|
print_error "Router is missing ip, flock or jshn. Standard OpenWrt BusyBox provides ip and flock; custom builds may need ip-tiny and flock. Install jshn if missing."
|
|
exit 1
|
|
fi
|
|
|
|
print_info "Connection verified. Preparing remote directories..."
|
|
|
|
ssh "${SSH_OPTS[@]}" "${SSH_TARGET}" "
|
|
mkdir -p /www/luci-static/resources/view/device-manager \
|
|
/usr/share/luci/menu.d \
|
|
/usr/share/rpcd/acl.d \
|
|
/usr/lib/lua/luci/i18n \
|
|
/usr/libexec/rpcd \
|
|
/etc/uci-defaults \
|
|
/etc/config
|
|
"
|
|
|
|
print_info "Deploying shared frontend modules and stylesheet..."
|
|
scp "${SCP_OPTS[@]}" -r \
|
|
"${PROJECT_ROOT}/htdocs/luci-static/resources/device-manager" \
|
|
"${SSH_TARGET}:/www/luci-static/resources/"
|
|
|
|
print_info "Deploying frontend view (devices.js)..."
|
|
scp "${SCP_OPTS[@]}" \
|
|
"${PROJECT_ROOT}/htdocs/luci-static/resources/view/device-manager/devices.js" \
|
|
"${SSH_TARGET}:/www/luci-static/resources/view/device-manager/devices.js"
|
|
|
|
print_info "Deploying LuCI menu and rpcd ACL definitions..."
|
|
scp "${SCP_OPTS[@]}" \
|
|
"${PROJECT_ROOT}/root/usr/share/luci/menu.d/luci-app-device-manager.json" \
|
|
"${SSH_TARGET}:/usr/share/luci/menu.d/luci-app-device-manager.json"
|
|
|
|
scp "${SCP_OPTS[@]}" \
|
|
"${PROJECT_ROOT}/root/usr/share/rpcd/acl.d/luci-app-device-manager.json" \
|
|
"${SSH_TARGET}:/usr/share/rpcd/acl.d/luci-app-device-manager.json"
|
|
|
|
# Native translations are installed as the matching luci-i18n package.
|
|
# Remove files left by older development deployments without changing LuCI language.
|
|
ssh "${SSH_OPTS[@]}" "${SSH_TARGET}" "
|
|
rm -f /www/luci-static/resources/device-manager/i18n.js /www/luci-static/resources/device-manager/translations.js
|
|
rm -f /usr/lib/lua/luci/i18n/device-manager-builtin.zh-cn.lmo /etc/uci-defaults/81_device_manager_i18n
|
|
"
|
|
|
|
if [[ -f "${PROJECT_ROOT}/root/usr/libexec/rpcd/luci.device-manager" ]]; then
|
|
print_info "Deploying rpcd helper backend script..."
|
|
scp "${SCP_OPTS[@]}" \
|
|
"${PROJECT_ROOT}/root/usr/libexec/rpcd/luci.device-manager" \
|
|
"${SSH_TARGET}:/usr/libexec/rpcd/luci.device-manager"
|
|
ssh "${SSH_OPTS[@]}" "${SSH_TARGET}" "chmod +x /usr/libexec/rpcd/luci.device-manager"
|
|
fi
|
|
|
|
if [[ -f "${PROJECT_ROOT}/tools/initialize-config.sh" ]]; then
|
|
print_info "Deploying development configuration initializer..."
|
|
scp "${SCP_OPTS[@]}" \
|
|
"${PROJECT_ROOT}/tools/initialize-config.sh" \
|
|
"${SSH_TARGET}:/etc/uci-defaults/80_device_manager"
|
|
fi
|
|
|
|
print_info "Ensuring configuration persistence (protecting existing user remarks)..."
|
|
ssh "${SSH_OPTS[@]}" "${SSH_TARGET}" "
|
|
set -e
|
|
if [ ! -f /etc/config/device_manager ]; then
|
|
echo 'Initializing default /etc/config/device_manager'
|
|
else
|
|
echo 'Existing /etc/config/device_manager preserved.'
|
|
fi
|
|
if [ -f /etc/uci-defaults/80_device_manager ]; then
|
|
chmod +x /etc/uci-defaults/80_device_manager
|
|
/etc/uci-defaults/80_device_manager
|
|
rm -f /etc/uci-defaults/80_device_manager
|
|
fi
|
|
"
|
|
|
|
print_info "Clearing LuCI cache and restarting rpcd..."
|
|
ssh "${SSH_OPTS[@]}" "${SSH_TARGET}" "
|
|
rm -f /tmp/luci-indexcache* /tmp/luci-modulecache*
|
|
/etc/init.d/rpcd restart
|
|
"
|
|
|
|
print_success "Deployment completed successfully!"
|
|
printf "\n"
|
|
printf "${CYAN}================================================================${NC}\n"
|
|
printf " ${GREEN}luci-app-device-manager${NC} is now active on your router.\n"
|
|
printf "\n"
|
|
printf " Access URL: ${YELLOW}http://%s/cgi-bin/luci/admin/network/device-manager${NC}\n" "${ROUTER_IP}"
|
|
printf " Menu path: ${YELLOW}网络 -> 设备管理${NC}\n"
|
|
printf "\n"
|
|
printf " ${YELLOW}Note:${NC} If you are already logged in, please refresh the page\n"
|
|
printf " with ${CYAN}Ctrl + Shift + R${NC} to clear browser JavaScript cache.\n"
|
|
printf "${CYAN}================================================================${NC}\n"
|