mirror of
https://github.com/alexhopeoconnor/dnsmasq-webui.git
synced 2026-10-22 07:52:08 +10:00
Install script, static assets, UI and release workflow updates
- install.sh: default dirs, --update/--service/--uninstall/--purge, in-dir dnsmasq-webui symlink, ContentRootPath note - UseStaticFiles instead of MapStaticAssets (fix 0-byte assets) - Program: ContentRootPath when not Development for symlink/CWD - App.razor: direct asset paths - Dnsmasq page: remove redundant status pre, margin above Reload button - Sidebar: logo 64px via app.css, hamburger vertically centered, toggler inside top-row - ApplicationTitle documented in README - publish/release: RIDs, Docker build for glibc
This commit is contained in:
+144
-23
@@ -16,6 +16,7 @@ SERVICE=false
|
||||
UNINSTALL=false
|
||||
PURGE=false
|
||||
UPDATE=false
|
||||
BUILD_FROM_SOURCE=false
|
||||
|
||||
# Default install dir (user-writable, no sudo). Overridden by --dir or --system.
|
||||
default_install_dir() {
|
||||
@@ -36,6 +37,7 @@ usage() {
|
||||
echo " --list List available releases (tag, name, published_at) and exit."
|
||||
echo " --version TAG Install from release TAG (e.g. v1.0.0). Default: latest."
|
||||
echo " --update Reinstall latest into the default user directory (~/.local/share/dnsmasq-webui)."
|
||||
echo " --build-from-source Build from source instead of downloading (requires .NET SDK and a git clone; not supported when run via curl | sh). Auto-detects RID for your machine. Use if the prebuilt binary fails to start (e.g. TypeLoadException)."
|
||||
echo " -h, -?, --help Show this help."
|
||||
echo ""
|
||||
echo "Install location:"
|
||||
@@ -67,8 +69,9 @@ usage() {
|
||||
echo " $0 --uninstall --purge # Remove services, symlinks, and default install dir"
|
||||
echo " sudo $0 --uninstall --purge --system # Also remove /opt/dnsmasq-webui"
|
||||
echo " $0 --list # List releases"
|
||||
echo " $0 --build-from-source # Build locally (if prebuilt binary fails on your system)"
|
||||
echo ""
|
||||
echo "After install, configure via appsettings.json or Dnsmasq__* environment variables, then run the binary (or dnsmasq-webui if symlink created). If you used --service, enable/start with systemctl."
|
||||
echo "After install, configure via appsettings.json or Dnsmasq__* environment variables, then run the binary (or dnsmasq-webui if symlink created). If you used --service, enable/start with systemctl. If the app fails to start with TypeLoadException or a glibc error, try --build-from-source (requires .NET SDK)."
|
||||
exit 0
|
||||
}
|
||||
|
||||
@@ -113,17 +116,15 @@ detect_arch() {
|
||||
esac
|
||||
}
|
||||
|
||||
# Map to portable RIDs only (linux-x64, linux-arm64, linux-musl-*). Ubuntu and other
|
||||
# glibc distros use linux-x64/linux-arm64; Alpine uses linux-musl-*. Used for downloading
|
||||
# release assets (CI builds portable RIDs only).
|
||||
detect_rid() {
|
||||
local arch
|
||||
arch="$(detect_arch)"
|
||||
if [ -f /etc/os-release ]; then
|
||||
. /etc/os-release
|
||||
case "${ID:-}" in
|
||||
ubuntu)
|
||||
case "${VERSION_ID:-}" in
|
||||
24.04) echo "ubuntu.24.04-$arch"; return ;;
|
||||
22.04) echo "ubuntu.22.04-$arch"; return ;;
|
||||
esac ;;
|
||||
alpine)
|
||||
case "$arch" in
|
||||
x64) echo "linux-musl-x64"; return ;;
|
||||
@@ -139,6 +140,18 @@ detect_rid() {
|
||||
esac
|
||||
}
|
||||
|
||||
# Print detected environment (OS, arch) for user feedback.
|
||||
print_env_detection() {
|
||||
local rid
|
||||
rid="$1"
|
||||
if [ -f /etc/os-release ]; then
|
||||
. /etc/os-release
|
||||
echo "Detected: ${ID:-unknown} ${VERSION_ID:-}, $(uname -m) -> RID $rid"
|
||||
else
|
||||
echo "Detected: $(uname -m) -> RID $rid"
|
||||
fi
|
||||
}
|
||||
|
||||
# GET release (latest or by tag). Output raw JSON to stdout.
|
||||
fetch_release() {
|
||||
local api_url
|
||||
@@ -270,8 +283,97 @@ do_uninstall() {
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Build from source and install to INSTALL_DIR. Requires .NET SDK and running from a git clone.
|
||||
do_install_from_source() {
|
||||
local script_dir repo_root publish_output rid publish_dir
|
||||
script_dir="$(cd "$(dirname "$0")" && pwd)"
|
||||
repo_root="$(cd "$script_dir/.." && pwd)"
|
||||
|
||||
if [ ! -f "$repo_root/scripts/publish-self-contained.sh" ]; then
|
||||
echo "Error: --build-from-source requires a git clone (run from the repo directory). Not found: $repo_root/scripts/publish-self-contained.sh" >&2
|
||||
echo "Do not use --build-from-source when installing via 'curl ... | sh'; clone the repo first, then run ./scripts/install.sh --build-from-source" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! command -v dotnet >/dev/null 2>&1; then
|
||||
echo "Error: --build-from-source requires the .NET SDK. Install from https://dotnet.microsoft.com/download or your distro (e.g. apt install dotnet-sdk-9.0)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$SYSTEM_INSTALL" = true ]; then
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "Error: --system installs to /opt and requires root. Run with sudo: sudo $0 --system --build-from-source" >&2
|
||||
exit 1
|
||||
fi
|
||||
INSTALL_DIR="/opt/dnsmasq-webui"
|
||||
elif [ -z "$INSTALL_DIR" ]; then
|
||||
INSTALL_DIR="$(default_install_dir)"
|
||||
fi
|
||||
|
||||
echo "Building from source (auto-detecting RID for your machine)..."
|
||||
publish_output="$(cd "$repo_root" && ./scripts/publish-self-contained.sh 2>&1)" || exit $?
|
||||
echo "$publish_output"
|
||||
rid="$(echo "$publish_output" | sed -n 's/^Detected RID: \([^ ]*\).*/\1/p')"
|
||||
if [ -z "$rid" ]; then
|
||||
rid="$(echo "$publish_output" | sed -n 's/^Publishing self-contained.* for \([^.]*\)\.\.\./\1/p')"
|
||||
fi
|
||||
if [ -z "$rid" ]; then
|
||||
echo "Error: Could not determine RID from build output." >&2
|
||||
exit 1
|
||||
fi
|
||||
publish_dir="$repo_root/src/DnsmasqWebUI/bin/Release/net9.0/$rid/publish"
|
||||
if [ ! -d "$publish_dir" ] || [ ! -f "$publish_dir/DnsmasqWebUI" ]; then
|
||||
echo "Error: Build output not found at $publish_dir" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Installing to $INSTALL_DIR ..."
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
cp -a "$publish_dir"/* "$INSTALL_DIR/"
|
||||
ln -sf DnsmasqWebUI "$INSTALL_DIR/dnsmasq-webui" 2>/dev/null || true
|
||||
|
||||
if [ "$SYSTEM_INSTALL" = true ]; then
|
||||
ln -sf "$INSTALL_DIR/dnsmasq-webui" /usr/local/bin/dnsmasq-webui 2>/dev/null || true
|
||||
echo ""
|
||||
echo "Installed to $INSTALL_DIR (system-wide)"
|
||||
echo "Run: dnsmasq-webui (or $INSTALL_DIR/dnsmasq-webui)"
|
||||
else
|
||||
LOCAL_BIN="${HOME:-}/.local/bin"
|
||||
if [ -n "${HOME:-}" ] && [ -d "$(dirname "$LOCAL_BIN")" ]; then
|
||||
mkdir -p "$LOCAL_BIN"
|
||||
if [ -w "$LOCAL_BIN" ]; then
|
||||
ln -sf "$INSTALL_DIR/dnsmasq-webui" "$LOCAL_BIN/dnsmasq-webui" 2>/dev/null && echo "Symlink: $LOCAL_BIN/dnsmasq-webui -> $INSTALL_DIR/dnsmasq-webui" || true
|
||||
fi
|
||||
fi
|
||||
echo ""
|
||||
echo "Installed to $INSTALL_DIR"
|
||||
echo "Run: $INSTALL_DIR/dnsmasq-webui"
|
||||
if [ -f "$LOCAL_BIN/dnsmasq-webui" ] 2>/dev/null; then
|
||||
echo " or: dnsmasq-webui (if ~/.local/bin is in your PATH)"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$SERVICE" = true ]; then
|
||||
if [ "$SYSTEM_INSTALL" = true ] && [ "$(id -u)" -ne 0 ]; then
|
||||
echo "Error: Installing a system service (--service with --system) requires root." >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! command -v systemctl >/dev/null 2>&1; then
|
||||
echo "Error: systemd not found. --service is only supported on systemd-based systems." >&2
|
||||
exit 1
|
||||
fi
|
||||
install_systemd_unit "$INSTALL_DIR"
|
||||
fi
|
||||
|
||||
echo "Configure via appsettings.json in that directory or Dnsmasq__* environment variables (e.g. Dnsmasq__MainConfigPath=/etc/dnsmasq.conf)."
|
||||
}
|
||||
|
||||
# Main install: fetch release, find asset for RID, download, extract, optionally symlink.
|
||||
do_install() {
|
||||
if [ "$BUILD_FROM_SOURCE" = true ]; then
|
||||
do_install_from_source
|
||||
return
|
||||
fi
|
||||
|
||||
check_jq
|
||||
detect_repo
|
||||
|
||||
@@ -297,7 +399,7 @@ do_install() {
|
||||
fi
|
||||
|
||||
rid="$(detect_rid)"
|
||||
echo "Detected RID: $rid"
|
||||
print_env_detection "$rid"
|
||||
echo "Fetching release..."
|
||||
release_json="$(fetch_release)"
|
||||
tag="$(echo "$release_json" | jq -r '.tag_name')"
|
||||
@@ -317,24 +419,25 @@ do_install() {
|
||||
echo "Extracting to $INSTALL_DIR ..."
|
||||
unzip -o -q "$tmpzip" -d "$INSTALL_DIR"
|
||||
rm -f "$tmpzip"
|
||||
ln -sf DnsmasqWebUI "$INSTALL_DIR/dnsmasq-webui" 2>/dev/null || true
|
||||
|
||||
if [ "$SYSTEM_INSTALL" = true ]; then
|
||||
ln -sf "$INSTALL_DIR/DnsmasqWebUI" /usr/local/bin/dnsmasq-webui 2>/dev/null || true
|
||||
ln -sf "$INSTALL_DIR/dnsmasq-webui" /usr/local/bin/dnsmasq-webui 2>/dev/null || true
|
||||
echo ""
|
||||
echo "Installed to $INSTALL_DIR (system-wide)"
|
||||
echo "Run: dnsmasq-webui (or $INSTALL_DIR/DnsmasqWebUI)"
|
||||
echo "Run: dnsmasq-webui (or $INSTALL_DIR/dnsmasq-webui)"
|
||||
else
|
||||
# User install: create ~/.local/bin symlink so `dnsmasq-webui` works if ~/.local/bin is in PATH
|
||||
LOCAL_BIN="${HOME:-}/.local/bin"
|
||||
if [ -n "${HOME:-}" ] && [ -d "$(dirname "$LOCAL_BIN")" ]; then
|
||||
mkdir -p "$LOCAL_BIN"
|
||||
if [ -w "$LOCAL_BIN" ]; then
|
||||
ln -sf "$INSTALL_DIR/DnsmasqWebUI" "$LOCAL_BIN/dnsmasq-webui" 2>/dev/null && echo "Symlink: $LOCAL_BIN/dnsmasq-webui -> $INSTALL_DIR/DnsmasqWebUI" || true
|
||||
ln -sf "$INSTALL_DIR/dnsmasq-webui" "$LOCAL_BIN/dnsmasq-webui" 2>/dev/null && echo "Symlink: $LOCAL_BIN/dnsmasq-webui -> $INSTALL_DIR/dnsmasq-webui" || true
|
||||
fi
|
||||
fi
|
||||
echo ""
|
||||
echo "Installed to $INSTALL_DIR"
|
||||
echo "Run: $INSTALL_DIR/DnsmasqWebUI"
|
||||
echo "Run: $INSTALL_DIR/dnsmasq-webui"
|
||||
if [ -f "$LOCAL_BIN/dnsmasq-webui" ] 2>/dev/null; then
|
||||
echo " or: dnsmasq-webui (if ~/.local/bin is in your PATH)"
|
||||
fi
|
||||
@@ -345,17 +448,21 @@ do_install() {
|
||||
fi
|
||||
|
||||
echo "Configure via appsettings.json in that directory or Dnsmasq__* environment variables (e.g. Dnsmasq__MainConfigPath=/etc/dnsmasq.conf)."
|
||||
echo "If the app fails to start with TypeLoadException or a glibc error, try: $0 --build-from-source (requires .NET SDK)."
|
||||
}
|
||||
|
||||
# Install systemd unit. Call only when SERVICE=true and systemctl exists.
|
||||
# Removes the other service type first (user vs system) so switching works cleanly.
|
||||
# $1 = INSTALL_DIR (where DnsmasqWebUI binary and appsettings.json live)
|
||||
# $1 = INSTALL_DIR (contains dnsmasq-webui runnable and appsettings.json)
|
||||
install_systemd_unit() {
|
||||
local dir bin
|
||||
dir="$1"
|
||||
bin="$dir/DnsmasqWebUI"
|
||||
if [ ! -f "$bin" ]; then
|
||||
echo "Warning: Binary $bin not found; skipping systemd unit install." >&2
|
||||
if [ -f "$dir/dnsmasq-webui" ]; then
|
||||
bin="$dir/dnsmasq-webui"
|
||||
elif [ -f "$dir/DnsmasqWebUI" ]; then
|
||||
bin="$dir/DnsmasqWebUI"
|
||||
else
|
||||
echo "Warning: No binary found in $dir; skipping systemd unit install." >&2
|
||||
return 0
|
||||
fi
|
||||
if [ "$SYSTEM_INSTALL" = true ]; then
|
||||
@@ -470,6 +577,10 @@ while [ $# -gt 0 ]; do
|
||||
PURGE=true
|
||||
shift
|
||||
;;
|
||||
--build-from-source)
|
||||
BUILD_FROM_SOURCE=true
|
||||
shift
|
||||
;;
|
||||
-*)
|
||||
echo "Error: unknown option $1" >&2
|
||||
usage >&2
|
||||
@@ -488,16 +599,26 @@ if [ "$PURGE" = true ] && [ "$UNINSTALL" != true ]; then
|
||||
echo "Error: --purge must be used with --uninstall. E.g. $0 --uninstall --purge" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "$UNINSTALL" = true ]; then
|
||||
if [ "$UPDATE" = true ] || [ -n "$VERSION" ] || [ "$SERVICE" = true ]; then
|
||||
echo "Error: --uninstall cannot be combined with install/update options (--version, --update, --service). Run uninstall alone, then install if needed." >&2
|
||||
exit 1
|
||||
if [ "$UNINSTALL" = true ]; then
|
||||
if [ "$UPDATE" = true ] || [ -n "$VERSION" ] || [ "$SERVICE" = true ] || [ "$BUILD_FROM_SOURCE" = true ]; then
|
||||
echo "Error: --uninstall cannot be combined with install/update options (--version, --update, --service, --build-from-source). Run uninstall alone, then install if needed." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "$LIST" = true ]; then
|
||||
echo "Error: --uninstall cannot be combined with --list." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
if [ "$LIST" = true ]; then
|
||||
echo "Error: --uninstall cannot be combined with --list." >&2
|
||||
exit 1
|
||||
if [ "$BUILD_FROM_SOURCE" = true ]; then
|
||||
if [ "$LIST" = true ]; then
|
||||
echo "Error: --build-from-source cannot be combined with --list." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ -n "$VERSION" ] || [ "$UPDATE" = true ]; then
|
||||
echo "Error: --build-from-source builds from current source; do not use --version or --update." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if [ "$LIST" = true ]; then
|
||||
if [ "$UNINSTALL" = true ] || [ "$UPDATE" = true ] || [ "$SERVICE" = true ] || [ -n "$VERSION" ]; then
|
||||
echo "Error: --list lists releases and exits; do not combine with install/uninstall options." >&2
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
# for ASP.NET Core). Copy the publish directory to the target host and run the binary.
|
||||
#
|
||||
# Platform auto-detection: if you do not pass a RID, the script picks one from /etc/os-release
|
||||
# and uname -m. On Ubuntu 24.04/22.04 we use ubuntu.<ver>-<arch> so the runtime matches the
|
||||
# host and avoids TypeLoadException seen with generic linux-x64 on those distros. Else we use
|
||||
# generic linux-* or linux-musl-* (Alpine). Trimming is off by default because PublishTrimmed
|
||||
# and uname -m. We use generic linux-* (glibc) or linux-musl-* (Alpine). Trimming is off by default because PublishTrimmed
|
||||
# can cause Blazor routing/404 issues. The script cleans the target RID before publish
|
||||
# so previous artefacts (e.g. from a different --trim or RID) cannot pollute the build.
|
||||
set -e
|
||||
@@ -14,6 +12,8 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
PROJECT="$REPO_ROOT/src/DnsmasqWebUI/DnsmasqWebUI.csproj"
|
||||
|
||||
# Portable RIDs (built in CI). Ubuntu RIDs for local builds only (CI does not build these;
|
||||
# use when .NET from distro repo so the binary runs on this machine without TypeLoadException).
|
||||
VALID_RIDS="linux-x64 linux-arm64 linux-arm linux-musl-x64 linux-musl-arm64 ubuntu.24.04-x64 ubuntu.24.04-arm64 ubuntu.22.04-x64 ubuntu.22.04-arm64"
|
||||
TRIM=false
|
||||
CLEAN=true
|
||||
@@ -30,7 +30,9 @@ detect_arch() {
|
||||
esac
|
||||
}
|
||||
|
||||
# Pick RID from OS and arch so runtime matches host (avoids TypeLoadException on Ubuntu).
|
||||
# Pick RID from OS and arch. We only add explicit distro RIDs where we've seen
|
||||
# TypeLoadException with portable linux-x64 (Ubuntu when .NET from distro). Alpine
|
||||
# uses musl. Other distros (Debian, Fedora, etc.) fall through to linux-x64/linux-arm64.
|
||||
default_rid() {
|
||||
local arch
|
||||
arch="$(detect_arch)"
|
||||
@@ -64,7 +66,7 @@ while [ $# -gt 0 ]; do
|
||||
echo ""
|
||||
echo "Build a self-contained folder publish for Linux. If RID is omitted, the script"
|
||||
echo "auto-detects from the current OS and architecture (recommended on Ubuntu and Alpine)."
|
||||
echo "Pass options first, then RID if desired (e.g. $0 --trim ubuntu.24.04-x64)."
|
||||
echo "Pass options first, then RID if desired (e.g. $0 --trim linux-x64)."
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --trim Enable trimming (smaller output; can cause 404/routing issues with Blazor)"
|
||||
@@ -79,7 +81,7 @@ while [ $# -gt 0 ]; do
|
||||
echo " Alpine (musl):"
|
||||
echo " linux-musl-x64 Alpine amd64"
|
||||
echo " linux-musl-arm64 Alpine aarch64"
|
||||
echo " Ubuntu (use on Ubuntu to avoid TypeLoadException with generic RID):"
|
||||
echo " Ubuntu (local builds only; not in CI releases; use if prebuilt linux-x64 fails):"
|
||||
echo " ubuntu.24.04-x64 ubuntu.24.04-arm64"
|
||||
echo " ubuntu.22.04-x64 ubuntu.22.04-arm64"
|
||||
echo ""
|
||||
@@ -87,11 +89,11 @@ while [ $# -gt 0 ]; do
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 # Publish for current machine (auto-detect RID)"
|
||||
echo " $0 ubuntu.24.04-x64 # Publish for Ubuntu 24.04 amd64"
|
||||
echo " $0 linux-x64 # Publish for glibc amd64 (Debian, Ubuntu, etc.)"
|
||||
echo " $0 linux-arm64 # Publish for Raspberry Pi 4/5 or other aarch64"
|
||||
echo " $0 linux-musl-x64 # Publish for Alpine (e.g. Docker)"
|
||||
echo " $0 --trim linux-x64 # Smaller build (not recommended for Blazor)"
|
||||
echo " $0 --no-clean ubuntu.24.04-x64 # Skip clean (faster; same RID/options as last run)"
|
||||
echo " $0 --no-clean linux-x64 # Skip clean (faster; same RID/options as last run)"
|
||||
exit 0
|
||||
;;
|
||||
--trim)
|
||||
|
||||
Reference in New Issue
Block a user