diff --git a/README.md b/README.md index 6cb78ce..d0a46c6 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ The recommended way to get a prebuilt binary is the **install script**. It picks curl -sSL https://raw.githubusercontent.com/alexhopeoconnor/dnsmasq-webui/master/scripts/install.sh | sh ``` -**From a git clone** (repo is detected from `git remote origin`): +**From a git clone:** ```bash ./scripts/install.sh @@ -102,8 +102,6 @@ sudo ./scripts/install.sh --uninstall --purge --system # Also remove /opt/dnsm ./scripts/install.sh --version v1.0.0 ``` -**Installing from a fork:** Pass the repo explicitly: `./scripts/install.sh --repo owner/dnsmasq-webui` or set `GITHUB_REPO=owner/dnsmasq-webui`. The default repo is `alexhopeoconnor/dnsmasq-webui`. - **If the binary fails to run** (e.g. `TypeLoadException` or glibc errors): use the install script with `--build-from-source` so the app is built for your machine. This requires the .NET SDK and a **git clone** (it does not work when installing via `curl ... | sh`). Clone the repo, then run `./scripts/install.sh --build-from-source`. The script checks that .NET is installed, detects your OS/arch, and builds (on Ubuntu it may use a distro-specific RID for a better match). Other distros use the portable RID and may still hit runtime issues; building from source on the target machine is the most reliable. **Configuration:** Set at least `Dnsmasq__MainConfigPath` to your main dnsmasq config. See [Configuration](#configuration) for all options. @@ -238,7 +236,6 @@ All scripts live under `scripts/` and are intended for Linux (WSL may work but i | Option | Description | |--------|-------------| -| `--repo OWNER/REPO` | GitHub owner/repo (or set `GITHUB_REPO`). When run from a clone, repo is detected from `git remote origin`. Default repo: `alexhopeoconnor/dnsmasq-webui`. | | `--list` | List available releases (tag, name, published_at) and exit. | | `--version TAG` | Install from release TAG (e.g. v1.0.0). Default: latest. | | `--update` | Reinstall latest into the default user directory (~/.local/share/dnsmasq-webui). | @@ -291,18 +288,14 @@ Invalid combinations (script errors with a clear message): `--purge` without `-- Script lists release tags and dates; list-only mode cannot be combined with install/uninstall so you do not accidentally change the system. **Command:** `./scripts/install.sh --list` -10. **Install from a fork or a specific GitHub repo** instead of the default. - You pass the repo; script uses it for the API. Without a clone or `--repo`/GITHUB_REPO it errors so you fix the command instead of getting a confusing API failure. - **Command:** `./scripts/install.sh --repo owner/dnsmasq-webui` or `GITHUB_REPO=owner/dnsmasq-webui ./scripts/install.sh` - -11. **Install a specific release tag** (e.g. an older or pinned version) instead of latest. +10. **Install a specific release tag** (e.g. an older or pinned version) instead of latest. Script fetches that tag's release and the asset for your RID. If no asset matches your OS/arch it errors and prints the asset names so you can pick another tag or report a missing build. **Command:** `./scripts/install.sh --version v1.0.0` **Examples:** ```bash -# Install latest (from clone: repo auto-detected; from curl: uses alexhopeoconnor/dnsmasq-webui by default, or use --repo) +# Install latest ./scripts/install.sh # Update to latest release @@ -329,8 +322,8 @@ sudo ./scripts/install.sh --uninstall --purge --system # Switch to another release (overwrites that dir) ./scripts/install.sh --dir ~/.local/share/dnsmasq-webui --version v0.9.0 -# List releases (from fork) -./scripts/install.sh --repo alexhopeoconnor/dnsmasq-webui --list +# List releases +./scripts/install.sh --list ``` **How the script works and avoids mistakes** @@ -351,7 +344,7 @@ sudo ./scripts/install.sh --uninstall --purge --system `--uninstall` removes only systemd units and symlinks unless you add `--purge`. With `--purge`, you must say *which* install to remove: default user dir (no extra flags), `--dir DIR`, or `--system` for /opt. That avoids accidentally deleting the wrong directory. 6. **Repo and release** - When not in a git clone, the script needs a repo (e.g. `REPO_DEFAULT` in the one-liner or `--repo` / `GITHUB_REPO`). It errors with a clear message instead of hitting the GitHub API with an empty repo. If no asset matches your OS/arch (RID), it prints the available asset names so you can pick a different release or report a missing build. + If no asset matches your OS/arch (RID), the script prints the available asset names so you can pick a different release or report a missing build. 7. **Dependencies** The script requires `curl`, `jq`, and `unzip`. It checks for `jq` before calling the GitHub API and errors with an install hint. `--service` requires `systemctl` (systemd) and errors on non-systemd systems instead of writing a unit that won’t be used. diff --git a/scripts/install.sh b/scripts/install.sh index 332a873..8c2355d 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -1,12 +1,10 @@ #!/usr/bin/env sh # Download and install dnsmasq-webui from a GitHub release. Picks the binary # matching the current OS/arch (RID). Run from a clone or via: -# curl -sSL https://raw.githubusercontent.com/alexhopeoconnor/dnsmasq-webui/main/scripts/install.sh | sh -# When run from a clone, repo is detected from git remote origin. +# curl -sSL https://raw.githubusercontent.com/alexhopeoconnor/dnsmasq-webui/master/scripts/install.sh | sh set -e -GITHUB_REPO="${GITHUB_REPO:-}" -REPO_DEFAULT="${REPO_DEFAULT:-alexhopeoconnor/dnsmasq-webui}" +GITHUB_REPO="alexhopeoconnor/dnsmasq-webui" RELEASE_TAG="" LIST=false @@ -37,10 +35,6 @@ usage() { echo "Download and install dnsmasq-webui from a GitHub release for this machine's OS/arch." echo "Supports install, update, and switch-release (re-run with same --dir and different --version)." echo "" - echo "Repo (when not in a clone):" - echo " --repo OWNER/REPO GitHub owner/repo. Or set GITHUB_REPO. Or set REPO_DEFAULT in the script." - echo " When run inside a git clone with origin, repo is detected automatically." - echo "" echo "Release:" 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." @@ -72,7 +66,7 @@ usage() { echo " sudo $0 --uninstall --purge --system # also remove /opt/dnsmasq-webui" echo "" echo "Examples:" - echo " $0 # Install latest (clone: auto repo; curl: use REPO_DEFAULT or --repo)" + echo " $0 # Install latest" echo " $0 --update # Reinstall latest to default dir (upgrade)" echo " $0 --version v1.0.0 # Install v1.0.0 to default dir" echo " $0 --dir /opt/dnsmasq-webui # Install to custom dir" @@ -90,11 +84,6 @@ usage() { exit 0 } -# Trim whitespace and carriage return (e.g. from script downloaded on Windows or with CRLF). -trim_repo() { - echo "$1" | tr -d '\r' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//' -} - # Build CONFIG_VARS_FILE from env (DNSMASQ_WEBUI_*) then --set lines. Call before do_install when installing. # Result: key=value per line (env first, then --set so --set overrides). Safe for values with = in them. collect_config() { @@ -145,30 +134,6 @@ write_env_file() { fi } -# Detect owner/repo from git remote, REPO_DEFAULT, or require --repo/GITHUB_REPO. -detect_repo() { - if [ -n "$GITHUB_REPO" ]; then - GITHUB_REPO="$(trim_repo "$GITHUB_REPO")" - return - fi - if command -v git >/dev/null 2>&1 && git rev-parse --is-inside-work-tree >/dev/null 2>&1; then - origin="$(git remote get-url origin 2>/dev/null)" || true - if [ -n "$origin" ]; then - GITHUB_REPO="$(echo "$origin" | sed -E 's|^https://github\.com/||; s|^git@github\.com:||; s|\.git$||; s|/$||')" - GITHUB_REPO="$(trim_repo "$GITHUB_REPO")" - if [ -n "$GITHUB_REPO" ]; then - return - fi - fi - fi - if [ -n "$REPO_DEFAULT" ]; then - GITHUB_REPO="$(trim_repo "$REPO_DEFAULT")" - return - fi - echo "Error: GitHub repo not set. Use --repo owner/repo, set GITHUB_REPO, or run from a clone. For the one-liner, set REPO_DEFAULT in the script." >&2 - exit 1 -} - # Require jq for parsing GitHub API JSON. check_jq() { if command -v jq >/dev/null 2>&1; then @@ -178,6 +143,14 @@ check_jq() { exit 1 } +# Safely run jq on release JSON (avoids echo mangling). $1 = JSON, $2+ = jq args. +jq_release() { + local json + json="$1" + shift + printf '%s' "$json" | jq -r "$@" +} + # Detect RID (same logic as publish-self-contained.sh). detect_arch() { case "$(uname -m)" in @@ -234,44 +207,45 @@ fetch_release() { api_url="https://api.github.com/repos/$GITHUB_REPO/releases/tags/$RELEASE_TAG" fi resp="$(curl -sSL -A "dnsmasq-webui-install/1.0" -w "\n%{http_code}" "$api_url" &2 body="$(printf '%s' "$body" | tr -d '\000-\011\013\014\016-\037')" - echo "$body" | jq -r '.message // .' 2>/dev/null || echo "$body" >&2 + printf '%s' "$body" | jq -r '.message // .' 2>/dev/null || printf '%s' "$body" >&2 exit 1 fi # GitHub API can return release body with unescaped control chars; jq rejects them. Strip control chars (keep \n \r). body="$(printf '%s' "$body" | tr -d '\000-\011\013\014\016-\037')" - echo "$body" + printf '%s' "$body" } # List releases and exit. list_releases() { check_jq - detect_repo api_url="https://api.github.com/repos/$GITHUB_REPO/releases?per_page=20" resp="$(curl -sSL -A "dnsmasq-webui-install/1.0" -w "\n%{http_code}" "$api_url" &2 exit 1 fi body="$(printf '%s' "$body" | tr -d '\000-\011\013\014\016-\037')" - echo "$body" | jq -r '.[] | "\(.tag_name) \(.name) \(.published_at // .created_at)"' + printf '%s' "$body" | jq -r '.[] | "\(.tag_name) \(.name) \(.published_at // .created_at)"' exit 0 } -# Find asset download URL whose name contains the given RID. +# Find asset download URL whose name contains the given RID. Output trimmed single line or empty. find_asset_url() { - local release_json rid + local release_json rid raw release_json="$1" rid="$2" - echo "$release_json" | jq -r --arg rid "$rid" ' + raw="$(jq_release "$release_json" --arg rid "$rid" ' .assets[] | select(.name | test($rid)) | .browser_download_url - ' | head -n1 + ' | head -n1)" + # Trim CR and leading/trailing whitespace (defensive for API quirks) + printf '%s' "$raw" | tr -d '\r' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//' } # Remove user systemd unit (stop, disable, rm file). No root. Safe if not present. @@ -453,7 +427,6 @@ do_install() { echo "Installing dnsmasq-webui..." check_jq - detect_repo if [ "$SYSTEM_INSTALL" = true ]; then if [ "$(id -u)" -ne 0 ]; then @@ -480,18 +453,18 @@ do_install() { print_env_detection "$rid" echo "Fetching release..." release_json="$(fetch_release)" - tag="$(echo "$release_json" | jq -r '.tag_name')" + tag="$(jq_release "$release_json" '.tag_name')" echo "Release: $tag" - url="$(find_asset_url "$release_json" "$rid" | tr -d '\r' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')" + url="$(find_asset_url "$release_json" "$rid")" if [ -z "$url" ] || [ "$url" = "null" ]; then echo "Error: No asset found for RID $rid in release $tag." >&2 echo "Available assets:" >&2 - echo "$release_json" | jq -r '.assets[].name' | sed 's/^/ /' >&2 + jq_release "$release_json" '.assets[].name' | sed 's/^/ /' >&2 exit 1 fi mkdir -p "$INSTALL_DIR" - tmpzip="${TMPDIR:-/tmp}/dnsmasq-webui-$rid.zip" + tmpzip="$(mktemp)" echo "Downloading $url ..." curl -sSL -A "dnsmasq-webui-install/1.0" -o "$tmpzip" "$url" &2; exit 1; } - GITHUB_REPO="$1" - shift - ;; --list) LIST=true shift @@ -701,26 +668,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 "$RELEASE_TAG" ] || [ "$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 +if [ "$UNINSTALL" = true ]; then + if [ "$UPDATE" = true ] || [ -n "$RELEASE_TAG" ] || [ "$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 [ "$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 "$RELEASE_TAG" ] || [ "$UPDATE" = true ]; then - echo "Error: --build-from-source builds from current source; do not use --version or --update." >&2 - exit 1 - fi + if [ "$LIST" = true ]; then + echo "Error: --uninstall cannot be combined with --list." >&2 + exit 1 fi +fi +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 "$RELEASE_TAG" ] || [ "$UPDATE" = true ]; then + echo "Error: --build-from-source builds from current source; do not use --version or --update." >&2 + exit 1 + fi +fi if [ "$LIST" = true ]; then if [ "$UNINSTALL" = true ] || [ "$UPDATE" = true ] || [ "$SERVICE" = true ] || [ -n "$RELEASE_TAG" ]; then echo "Error: --list lists releases and exits; do not combine with install/uninstall options." >&2