Fix agent install/uninstall sudo placement, add dependency install hints
curl ... | API_URL=... bash runs as the invoking user, not root, so "sudo" has to go right after the pipe (elevating bash) rather than before curl — the old commands in the app UI, README, and scripts' own usage comments put it in a spot that still failed the root check. Also have install.sh/report-tasks.sh suggest the right package-manager command (apt/dnf/yum/apk/pacman/zypper) when curl or jq is missing instead of just failing, and bring the README up to date with both.
This commit is contained in:
1 parent
0615866311
commit
329dcab936
5 files changed
+71
-18
No files matched your search
@@ -11,8 +11,9 @@ support is a documented future addition — see [`agent/windows/README.md`](agen
|
|||||||
|
|
||||||
- `server/` — Express + TypeScript API, SQLite (via libSQL) storage, Authentik OIDC login.
|
- `server/` — Express + TypeScript API, SQLite (via libSQL) storage, Authentik OIDC login.
|
||||||
- `web/` — React + Vite single-page app, served as static files by the Express server.
|
- `web/` — React + Vite single-page app, served as static files by the Express server.
|
||||||
- `agent/linux/` — `report-tasks.sh` (collects cron + systemd timer data) and
|
- `agent/linux/` — `report-tasks.sh` (collects cron + systemd timer data),
|
||||||
`install.sh` (installs it as a systemd timer on a target server).
|
`install.sh` (installs it as a systemd timer on a target server), and
|
||||||
|
`uninstall.sh` (removes it).
|
||||||
|
|
||||||
Data model, API contract, and sync behavior are documented in code:
|
Data model, API contract, and sync behavior are documented in code:
|
||||||
[`server/src/db/schema.ts`](server/src/db/schema.ts),
|
[`server/src/db/schema.ts`](server/src/db/schema.ts),
|
||||||
@@ -64,11 +65,13 @@ automatically on server start.
|
|||||||
|
|
||||||
1. Sign in, go to **Servers**, and add a server. The generated API token is
|
1. Sign in, go to **Servers**, and add a server. The generated API token is
|
||||||
shown once — copy it.
|
shown once — copy it.
|
||||||
2. On the target Linux server (as root):
|
2. On the target Linux server, as root. If you're not already root, put
|
||||||
|
`sudo` right after the pipe so it elevates `bash`, not `curl` — `sudo curl
|
||||||
|
... | bash` would still fail the root check:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
curl -fsSL https://schedule.example.lan/agent/linux/install.sh | \
|
curl -fsSL https://schedule.example.lan/agent/linux/install.sh | \
|
||||||
API_URL=https://schedule.example.lan API_TOKEN=stm_xxx bash
|
sudo API_URL=https://schedule.example.lan API_TOKEN=stm_xxx bash
|
||||||
```
|
```
|
||||||
|
|
||||||
This installs `report-tasks.sh` to `/usr/local/bin`, writes the API
|
This installs `report-tasks.sh` to `/usr/local/bin`, writes the API
|
||||||
@@ -76,7 +79,10 @@ automatically on server start.
|
|||||||
`schedule-task-manager-agent.timer` systemd timer that reports every 15
|
`schedule-task-manager-agent.timer` systemd timer that reports every 15
|
||||||
minutes (override with `INTERVAL_MINUTES=5` before the `bash` above).
|
minutes (override with `INTERVAL_MINUTES=5` before the `bash` above).
|
||||||
|
|
||||||
Requires `curl`, `jq`, and `systemd` on the target server.
|
Requires `curl`, `jq`, and `systemd` on the target server — if either
|
||||||
|
`curl` or `jq` is missing, both this script and `report-tasks.sh` print
|
||||||
|
the right install command for the server's package manager (apt, dnf,
|
||||||
|
yum, apk, pacman, or zypper) and exit.
|
||||||
|
|
||||||
3. Tasks appear on the Dashboard, grouped by server and then by schedule type,
|
3. Tasks appear on the Dashboard, grouped by server and then by schedule type,
|
||||||
within one reporting interval.
|
within one reporting interval.
|
||||||
@@ -88,7 +94,7 @@ hidden by default — toggle "Show stale/missing tasks" to see them.
|
|||||||
To remove the agent from a server, run:
|
To remove the agent from a server, run:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
curl -fsSL https://schedule.example.lan/agent/linux/uninstall.sh | bash
|
curl -fsSL https://schedule.example.lan/agent/linux/uninstall.sh | sudo bash
|
||||||
```
|
```
|
||||||
|
|
||||||
This stops and deletes the systemd timer/service, the installed script, and
|
This stops and deletes the systemd timer/service, the installed script, and
|
||||||
|
|||||||
+31
-5
@@ -1,9 +1,10 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Installs the Schedule Task Manager agent as a systemd timer.
|
# Installs the Schedule Task Manager agent as a systemd timer.
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage (must run as root — if not already root, put sudo right after the
|
||||||
|
# pipe so it applies to bash, not to curl):
|
||||||
# curl -fsSL https://schedule.example.lan/agent/linux/install.sh | \
|
# curl -fsSL https://schedule.example.lan/agent/linux/install.sh | \
|
||||||
# API_URL=https://schedule.example.lan API_TOKEN=stm_xxx bash
|
# sudo API_URL=https://schedule.example.lan API_TOKEN=stm_xxx bash
|
||||||
#
|
#
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
@@ -17,16 +18,41 @@ INTERVAL_MINUTES="${INTERVAL_MINUTES:-15}"
|
|||||||
|
|
||||||
if [[ "$EUID" -ne 0 ]]; then
|
if [[ "$EUID" -ne 0 ]]; then
|
||||||
echo "This installer must be run as root (it installs a systemd timer)." >&2
|
echo "This installer must be run as root (it installs a systemd timer)." >&2
|
||||||
|
echo "If you're piping from curl, put sudo right after the pipe so it elevates bash, not curl:" >&2
|
||||||
|
echo " curl -fsSL ... | sudo API_URL=... API_TOKEN=... bash" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for bin in curl jq systemctl; do
|
suggest_package_install() {
|
||||||
|
local pkg="$1"
|
||||||
|
if command -v apt-get >/dev/null 2>&1; then
|
||||||
|
echo " sudo apt-get update && sudo apt-get install -y $pkg"
|
||||||
|
elif command -v dnf >/dev/null 2>&1; then
|
||||||
|
echo " sudo dnf install -y $pkg"
|
||||||
|
elif command -v yum >/dev/null 2>&1; then
|
||||||
|
echo " sudo yum install -y $pkg"
|
||||||
|
elif command -v apk >/dev/null 2>&1; then
|
||||||
|
echo " sudo apk add $pkg"
|
||||||
|
elif command -v pacman >/dev/null 2>&1; then
|
||||||
|
echo " sudo pacman -S $pkg"
|
||||||
|
elif command -v zypper >/dev/null 2>&1; then
|
||||||
|
echo " sudo zypper install -y $pkg"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
for bin in curl jq; do
|
||||||
if ! command -v "$bin" >/dev/null 2>&1; then
|
if ! command -v "$bin" >/dev/null 2>&1; then
|
||||||
echo "Required dependency '$bin' is not installed." >&2
|
echo "Required dependency '$bin' is not installed. Try:" >&2
|
||||||
|
suggest_package_install "$bin" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
if ! command -v systemctl >/dev/null 2>&1; then
|
||||||
|
echo "systemctl was not found — this installer requires a systemd-based Linux distribution." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Installing Schedule Task Manager agent from $API_URL ..."
|
echo "Installing Schedule Task Manager agent from $API_URL ..."
|
||||||
|
|
||||||
curl -fsSL "$API_URL/agent/linux/report-tasks.sh" -o "$INSTALL_DIR/schedule-task-manager-agent.sh"
|
curl -fsSL "$API_URL/agent/linux/report-tasks.sh" -o "$INSTALL_DIR/schedule-task-manager-agent.sh"
|
||||||
@@ -69,4 +95,4 @@ echo "Installed. Running an initial report now..."
|
|||||||
"$INSTALL_DIR/schedule-task-manager-agent.sh"
|
"$INSTALL_DIR/schedule-task-manager-agent.sh"
|
||||||
|
|
||||||
echo "Done. The agent reports every ${INTERVAL_MINUTES} minute(s) via the 'schedule-task-manager-agent.timer' systemd timer."
|
echo "Done. The agent reports every ${INTERVAL_MINUTES} minute(s) via the 'schedule-task-manager-agent.timer' systemd timer."
|
||||||
echo "To remove it later: curl -fsSL $API_URL/agent/linux/uninstall.sh | bash"
|
echo "To remove it later: curl -fsSL $API_URL/agent/linux/uninstall.sh | sudo bash"
|
||||||
@@ -23,9 +23,27 @@ if [[ -z "$API_URL" || -z "$API_TOKEN" ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
suggest_package_install() {
|
||||||
|
local pkg="$1"
|
||||||
|
if command -v apt-get >/dev/null 2>&1; then
|
||||||
|
echo " sudo apt-get update && sudo apt-get install -y $pkg"
|
||||||
|
elif command -v dnf >/dev/null 2>&1; then
|
||||||
|
echo " sudo dnf install -y $pkg"
|
||||||
|
elif command -v yum >/dev/null 2>&1; then
|
||||||
|
echo " sudo yum install -y $pkg"
|
||||||
|
elif command -v apk >/dev/null 2>&1; then
|
||||||
|
echo " sudo apk add $pkg"
|
||||||
|
elif command -v pacman >/dev/null 2>&1; then
|
||||||
|
echo " sudo pacman -S $pkg"
|
||||||
|
elif command -v zypper >/dev/null 2>&1; then
|
||||||
|
echo " sudo zypper install -y $pkg"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
for bin in curl jq; do
|
for bin in curl jq; do
|
||||||
if ! command -v "$bin" >/dev/null 2>&1; then
|
if ! command -v "$bin" >/dev/null 2>&1; then
|
||||||
echo "Required dependency '$bin' is not installed." >&2
|
echo "Required dependency '$bin' is not installed. Try:" >&2
|
||||||
|
suggest_package_install "$bin" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
# Removes the Schedule Task Manager agent from this server: stops and deletes
|
# Removes the Schedule Task Manager agent from this server: stops and deletes
|
||||||
# its systemd timer/service, the installed script, and its credentials file.
|
# its systemd timer/service, the installed script, and its credentials file.
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage (must run as root — if not already root, put sudo right after the
|
||||||
# curl -fsSL https://schedule.example.lan/agent/linux/uninstall.sh | bash
|
# pipe so it applies to bash, not to curl):
|
||||||
|
# curl -fsSL https://schedule.example.lan/agent/linux/uninstall.sh | sudo bash
|
||||||
#
|
#
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
@@ -13,6 +14,8 @@ SYSTEMD_DIR="/etc/systemd/system"
|
|||||||
|
|
||||||
if [[ "$EUID" -ne 0 ]]; then
|
if [[ "$EUID" -ne 0 ]]; then
|
||||||
echo "This uninstaller must be run as root." >&2
|
echo "This uninstaller must be run as root." >&2
|
||||||
|
echo "If you're piping from curl, put sudo right after the pipe so it elevates bash, not curl:" >&2
|
||||||
|
echo " curl -fsSL ... | sudo bash" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -89,8 +89,8 @@ export default function ServersAdmin() {
|
|||||||
<h3>Agent token for {newToken.server.name}</h3>
|
<h3>Agent token for {newToken.server.name}</h3>
|
||||||
<p>This token is only shown once — copy it now.</p>
|
<p>This token is only shown once — copy it now.</p>
|
||||||
<code className="token-value">{newToken.token}</code>
|
<code className="token-value">{newToken.token}</code>
|
||||||
<p>Install the agent on the server:</p>
|
<p>Install the agent on the server (run as root — put sudo right after the pipe, not before curl):</p>
|
||||||
<pre className="install-command">{`curl -fsSL ${window.location.origin}/agent/linux/install.sh | API_URL=${window.location.origin} API_TOKEN=${newToken.token} bash`}</pre>
|
<pre className="install-command">{`curl -fsSL ${window.location.origin}/agent/linux/install.sh | sudo API_URL=${window.location.origin} API_TOKEN=${newToken.token} bash`}</pre>
|
||||||
<button onClick={() => setNewToken(null)} className="btn">
|
<button onClick={() => setNewToken(null)} className="btn">
|
||||||
Dismiss
|
Dismiss
|
||||||
</button>
|
</button>
|
||||||
@@ -100,8 +100,8 @@ export default function ServersAdmin() {
|
|||||||
{uninstallFor && (
|
{uninstallFor && (
|
||||||
<section className="token-reveal uninstall-reveal">
|
<section className="token-reveal uninstall-reveal">
|
||||||
<h3>Uninstall agent from {uninstallFor.name}</h3>
|
<h3>Uninstall agent from {uninstallFor.name}</h3>
|
||||||
<p>Run this on the server to stop and remove the agent (its entry here is kept):</p>
|
<p>Run this on the server as root to stop and remove the agent (its entry here is kept):</p>
|
||||||
<pre className="install-command">{`curl -fsSL ${window.location.origin}/agent/linux/uninstall.sh | bash`}</pre>
|
<pre className="install-command">{`curl -fsSL ${window.location.origin}/agent/linux/uninstall.sh | sudo bash`}</pre>
|
||||||
<button onClick={() => setUninstallFor(null)} className="btn">
|
<button onClick={() => setUninstallFor(null)} className="btn">
|
||||||
Dismiss
|
Dismiss
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
Reference in new issue
Block a user