Compare commits

..

23 commits
v1.0.0 ... main

Author SHA1 Message Date
Eddie Nielsen
3a5535f945 Restore correct Update Manager README 2026-03-25 15:51:54 +00:00
Eddie Nielsen
0281c9d13c Fixed header file 2026-03-25 13:46:20 +00:00
Eddie Nielsen
08eeb039b4 Fixed header file 2026-03-25 13:37:49 +00:00
Eddie Nielsen
8401028756 Fixed header file 2026-03-25 13:36:53 +00:00
Eddie Nielsen
2eebd5a104 Fixed header file 2026-03-25 13:36:01 +00:00
Eddie Nielsen
e7b07682b3 Fix UpdateManager logo filename 2026-03-25 13:33:27 +00:00
Eddie Nielsen
2c58db376a Add logo and standardized credits & and added new credit 2026-03-25 13:29:13 +00:00
340ceeef6b fix: rename diagram file 2026-03-18 21:54:30 +01:00
2cfabb93e2 fix: rename diagram file 2026-03-18 21:53:42 +01:00
80a118c07b fix: rename diagram file 2026-03-18 21:52:38 +01:00
dddd4352f0 docs: add architecture diagram 2026-03-18 21:47:23 +01:00
36c68a7067 docs: add architecture overview 2026-03-18 21:28:04 +01:00
d3fd0f5cb1 docs: add architecture overview 2026-03-18 21:25:21 +01:00
4dfe48fa1b chore: remove old images from root, keep docs/images only 2026-03-18 21:06:31 +01:00
7e6230aa12 docs: move UI screenshots to docs/images 2026-03-18 21:05:12 +01:00
65afd0cdae docs: add UI screenshots and logging section 2026-03-18 21:02:45 +01:00
bd9f96f3a9 docs: add UI screenshots and logging section 2026-03-18 21:00:38 +01:00
2a0053dee5 feat: add UI menu 2026-03-18 20:18:28 +01:00
61f246bb25 Update UI screenshot 2026-03-18 20:13:20 +01:00
f62e31de62 Update UI screenshot 2026-03-18 20:11:49 +01:00
0c6d4263d4 Add UI demo 2026-03-18 20:09:29 +01:00
279c6ade30 Add dialog theme file 2026-03-18 20:02:44 +01:00
ab945b1caf Add logging, dialog UI, README updates and initial release 2026-03-18 19:25:01 +01:00
13 changed files with 677 additions and 159 deletions

65
CHANGELOG.md Normal file
View file

@ -0,0 +1,65 @@
# Changelog
All notable changes to this project will be documented in this file.
---
## [0.4.0] - 2026-03-18
### Added
* Structured logging system with timestamps
* Log levels: INFO, OK, ERR
* Central log file: `/opt/update-manager/log/update-manager.log`
* Log submenu in UI
* Log viewing options:
* Full log
* Live log (tail -f)
* Last 20 lines
* Log file location
### Changed
* Refactored UI to group log features into submenu
* Improved menu structure and usability
### Improved
* Better error visibility for SSH failures
* Consistent output across hosts
* README updated with UI screenshots and logging documentation
---
## [0.3.0] - 2026-03-18
### Added
* CLI menu (`update-manager-ui.sh`)
* Interactive host management
* Dialog-based UI navigation
### Changed
* Project renamed from `lanx-update` to `update-manager`
* Moved runtime files to `/opt/update-manager`
---
## [0.2.0] - 2026-03-18
### Added
* SSH-based update checking across multiple hosts
* Support for `hosts.conf`
* Config file support (`/etc/update-manager.conf`)
---
## [0.1.0] - 2026-03-17
### Added
* Initial version
* Basic apt update check functionality

View file

@ -1,4 +1,7 @@
# 🖥️ Update Manager <p align="center">
<img src="images/updatemanager-logo.png" width="600">
</p>
Simple CLI tool to check and manage updates across multiple Ubuntu systems over SSH. Simple CLI tool to check and manage updates across multiple Ubuntu systems over SSH.
@ -26,10 +29,10 @@ Built for Lanx environments lightweight, fast and no unnecessary dependencie
## Features ## Features
* Check updates on multiple hosts * Check updates on multiple hosts
* Run checks remotely over SSH * Run updates remotely over SSH
* Interactive CLI menu (dialog-based UI)
* Centralized logging
* Simple config files * Simple config files
* Built-in logging (file + terminal)
* Dialog-based UI menu
* No agents required * No agents required
* Works with existing SSH setup * Works with existing SSH setup
@ -37,12 +40,20 @@ Built for Lanx environments lightweight, fast and no unnecessary dependencie
## Update Manager UI ## Update Manager UI
### Main menu
<p align="center"> <p align="center">
<img src="update-manager-ui.png" width="50%" alt="Update Manager CLI UI"> <img src="docs/images/menu-main.png" width="50%" alt="Main menu">
</p>
### Log menu
<p align="center">
<img src="docs/images/menu-logs.png" width="50%" alt="Log menu">
</p> </p>
<p align="center"> <p align="center">
<em>Lightweight • Terminal-based • Works over SSH</em> <em>Lightweight • No dependencies • Works over SSH</em>
</p> </p>
--- ---
@ -57,7 +68,7 @@ sudo apt update
sudo apt install dialog openssh-client sudo apt install dialog openssh-client
sudo mkdir -p /opt/update-manager sudo mkdir -p /opt/update-manager
sudo cp update-manager.sh update-manager-ui.sh /opt/update-manager/ sudo cp update-manager.sh update-manager-ui.sh dialogrc /opt/update-manager/
sudo chmod +x /opt/update-manager/update-manager.sh sudo chmod +x /opt/update-manager/update-manager.sh
sudo chmod +x /opt/update-manager/update-manager-ui.sh sudo chmod +x /opt/update-manager/update-manager-ui.sh
@ -134,6 +145,12 @@ server3 192.168.1.30 user
## Usage ## Usage
### Check updates
```bash
update-manager check
```
### Start UI ### Start UI
```bash ```bash
@ -142,42 +159,25 @@ update-manager-ui
--- ---
### Check updates (CLI)
```bash
update-manager check
```
---
## Logging ## Logging
The tool logs both to terminal and file. Log file location:
### Primary location
```bash ```bash
/opt/update-manager/log/update-manager.log /opt/update-manager/log/update-manager.log
``` ```
### Fallback location View log:
```bash ```bash
~/update-manager/log/update-manager.log less /opt/update-manager/log/update-manager.log
``` ```
### Notes Follow log:
* Log directory is created automatically ```bash
* Log file is created automatically tail -f /opt/update-manager/log/update-manager.log
* Output is written to both terminal and file ```
* Log levels:
* INFO
* WARN
* ERROR
Logs can be viewed directly from the UI.
--- ---
@ -189,8 +189,8 @@ Logs can be viewed directly from the UI.
├── update-manager-ui.sh ├── update-manager-ui.sh
├── update-manager.conf ├── update-manager.conf
├── hosts.conf ├── hosts.conf
── log/ ── log/
└── update-manager.log └── update-manager.log
``` ```
--- ---
@ -199,6 +199,7 @@ Logs can be viewed directly from the UI.
* Uses SSH to connect to each host * Uses SSH to connect to each host
* Runs `apt` commands remotely * Runs `apt` commands remotely
* Logs results locally
* No agents or services needed * No agents or services needed
* Designed for simple and efficient operations * Designed for simple and efficient operations
@ -209,7 +210,6 @@ Logs can be viewed directly from the UI.
* SSH access to all hosts * SSH access to all hosts
* SSH keys recommended (no password prompts) * SSH keys recommended (no password prompts)
* Ubuntu/Debian-based systems * Ubuntu/Debian-based systems
* `dialog` (for UI)
--- ---
@ -218,7 +218,6 @@ Logs can be viewed directly from the UI.
* 🔔 Notifications (ntfy / push alerts) * 🔔 Notifications (ntfy / push alerts)
* 🌐 Web interface * 🌐 Web interface
* 📧 Email reporting * 📧 Email reporting
* 📜 Advanced logging / audit trail
* 📊 Basic monitoring (status, last check, pending updates) * 📊 Basic monitoring (status, last check, pending updates)
* 🧩 Plugin system (extensible modules) * 🧩 Plugin system (extensible modules)
* 🔐 Security & compliance checks * 🔐 Security & compliance checks
@ -226,6 +225,15 @@ Logs can be viewed directly from the UI.
--- ---
## ❤️ Credits
Built with ❤️ for Lanx by [NodeFox 🦊](https://nodefox.lanx.dk)
Maintained by [Eddie Nielsen](https://lanx.dk)
> Learn. Adopt. Survive. Share.
---
## License ## License
This project is licensed under the GNU GPL v3 License. This project is licensed under the GNU GPL v3 License.
@ -233,10 +241,3 @@ This project is licensed under the GNU GPL v3 License.
See the LICENSE file for full details. See the LICENSE file for full details.
--- ---
## Author
Built with ❤️ for [Lanx](https://lanx.dk) by **NodeFox** 🦊
Maintained by Eddie Nielsen
Feel free to contribute, suggest improvements or fork the project.

32
dialogrc Normal file
View file

@ -0,0 +1,32 @@
use_shadow = OFF
use_colors = ON
screen_color = (WHITE,BLUE,OFF)
dialog_color = (WHITE,BLUE,OFF)
title_color = (YELLOW,BLUE,ON)
border_color = (WHITE,BLUE,OFF)
button_active_color = (BLACK,WHITE,ON)
button_inactive_color = (WHITE,BLUE,OFF)
button_key_active_color = (BLACK,WHITE,ON)
button_key_inactive_color = (WHITE,BLUE,OFF)
button_label_active_color = (BLACK,WHITE,ON)
button_label_inactive_color = (WHITE,BLUE,OFF)
menubox_color = (WHITE,BLUE,OFF)
menubox_border_color = (WHITE,BLUE,OFF)
item_color = (WHITE,BLUE,OFF)
item_selected_color = (BLACK,WHITE,ON)
tag_color = (WHITE,BLUE,OFF)
tag_selected_color = (BLACK,WHITE,ON)
tag_key_color = (YELLOW,BLUE,OFF)
tag_key_selected_color = (BLACK,WHITE,ON)
position_indicator_color = (WHITE,BLUE,OFF)
uarrow_color = (WHITE,BLUE,OFF)
darrow_color = (WHITE,BLUE,OFF)

211
docs/architecture.md Normal file
View file

@ -0,0 +1,211 @@
<p align="center">
<img src="images/architecture-diagram.png" width="50%" alt="Architecture Diagram">
</p>
# 🧠 Update Manager Architecture
This document describes how Update Manager is structured internally and how data flows through the system.
---
## Overview
Update Manager is a lightweight, SSH-based system for managing updates across multiple Linux hosts.
It consists of two main layers:
* **UI layer** interactive menu (`update-manager-ui.sh`)
* **Core engine** update logic (`update-manager.sh`)
The system is designed to be simple, transparent, and dependency-light.
---
## High-Level Flow
```text
User
UI (update-manager-ui.sh)
Core (update-manager.sh)
SSH → Remote Hosts
Results
Logging (/opt/update-manager/log)
UI / User
```
---
## Components
### UI Layer (`update-manager-ui.sh`)
Responsible for:
* Displaying menu (via `dialog`)
* Handling user input
* Managing hosts (add/remove/edit)
* Providing access to logs
* Delegating actions to core engine
The UI does **not perform updates directly**.
---
### Core Engine (`update-manager.sh`)
Responsible for:
* Reading configuration
* Parsing hosts file
* Executing SSH commands
* Checking for updates (`apt list --upgradable`)
* Handling connection errors
* Writing structured logs
This is the **execution layer** of the system.
---
### Hosts Configuration
File:
```bash
/opt/update-manager/hosts.conf
```
Format:
```text
name ip user
```
Example:
```text
server1 192.168.1.10 user
server2 192.168.1.20 user
server3 192.168.1.30 user
```
Each line represents a target system accessed via SSH.
---
### Logging System
Log file:
```bash
/opt/update-manager/log/update-manager.log
```
Log format:
```text
YYYY-MM-DD HH:MM:SS [LEVEL] host - message
```
Example:
```text
2026-03-18 20:45:01 [INFO] lanx-www - Checking updates
2026-03-18 20:45:03 [OK] lanx-www - 3 updates available
2026-03-18 20:45:05 [ERR] lanx-db - Connection failed
```
#### Log Levels
* `INFO` informational messages
* `OK` successful operations
* `ERR` errors or failures
Logging is centralized and written locally.
---
## Execution Flow
For each host:
1. Read host entry from `hosts.conf`
2. Establish SSH connection
3. Execute:
```bash
apt list --upgradable
```
4. Parse output
5. Determine:
* Up-to-date
* Updates available
* Connection failure
6. Write result to:
* terminal output
* log file
---
## Configuration
Primary config:
```bash
/etc/update-manager.conf
```
Fallback:
```bash
./update-manager.conf
```
Supported options:
* `HOSTS_FILE`
* `SSH_OPTIONS`
---
## Design Principles
* **No agents** uses standard SSH only
* **Simple over complex** minimal dependencies
* **Transparent behavior** everything is visible/logged
* **CLI-first** designed for terminal environments
* **Modular growth** prepared for future extensions
---
## Future Architecture Direction
Planned extensions:
* Plugin system (modular features)
* Web interface (optional UI layer)
* Notification system (alerts)
* Metrics / monitoring
* Integration with Lanx AI
---
## Summary
Update Manager follows a clean separation:
* UI = interaction
* Core = execution
* Config = data
* Log = output
This keeps the system predictable, debuggable, and easy to extend.

Binary file not shown.

After

Width:  |  Height:  |  Size: 561 KiB

BIN
docs/images/menu-logs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
docs/images/menu-main.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

117
log/update-manager.sh Normal file
View file

@ -0,0 +1,117 @@
#!/usr/bin/env bash
UPDATE_MANAGER_SCRIPT="/opt/update-manager/update-manager.sh"
get_log_file() {
local primary="/opt/update-manager/log/update-manager.log"
local fallback="$HOME/update-manager/log/update-manager.log"
if [[ -f "$primary" ]]; then
echo "$primary"
else
echo "$fallback"
fi
}
run_check() {
echo
bash "$UPDATE_MANAGER_SCRIPT" check
echo
read -rp "Press Enter to continue..."
}
view_log() {
local log_file
log_file="$(get_log_file)"
echo
if [[ -f "$log_file" ]]; then
less "$log_file"
else
echo "Log file not found: $log_file"
echo
read -rp "Press Enter to continue..."
fi
}
follow_log() {
local log_file
log_file="$(get_log_file)"
echo
if [[ -f "$log_file" ]]; then
echo "Following log: $log_file"
echo "Press Ctrl+C to stop."
echo
tail -f "$log_file"
else
echo "Log file not found: $log_file"
fi
echo
read -rp "Press Enter to continue..."
}
show_log_location() {
local log_file
log_file="$(get_log_file)"
echo
echo "Log file location:"
echo "$log_file"
echo
read -rp "Press Enter to continue..."
}
show_menu() {
clear
echo "=================================="
echo " Update Manager UI"
echo "=================================="
echo
echo "1) Run update check"
echo "2) View full log"
echo "3) Follow log live"
echo "4) Show log location"
echo "0) Exit"
echo
}
main() {
if [[ ! -f "$UPDATE_MANAGER_SCRIPT" ]]; then
echo "Update manager script not found: $UPDATE_MANAGER_SCRIPT"
exit 1
fi
while true; do
show_menu
read -rp "Choose an option: " choice
case "$choice" in
1)
run_check
;;
2)
view_log
;;
3)
follow_log
;;
4)
show_log_location
;;
0)
echo
echo "Bye."
exit 0
;;
*)
echo
echo "Invalid choice."
read -rp "Press Enter to continue..."
;;
esac
done
}
main

Binary file not shown.

Before

Width:  |  Height:  |  Size: 409 KiB

140
update-manager-ui.sh Executable file
View file

@ -0,0 +1,140 @@
#!/usr/bin/env bash
export DIALOGRC="/opt/update-manager/dialogrc"
UPDATE_MANAGER_SCRIPT="$HOME/update-manager/update-manager.sh"
HOSTS_FILE="/opt/update-manager/hosts.conf"
get_log_file() {
local primary="/opt/update-manager/log/update-manager.log"
local fallback="$HOME/update-manager/log/update-manager.log"
if [[ -f "$primary" ]]; then
echo "$primary"
elif [[ -f "$fallback" ]]; then
echo "$fallback"
else
echo ""
fi
}
show_log_menu() {
while true; do
log_choice=$(dialog --clear \
--backtitle "Update Manager" \
--title "Log menu" \
--menu "Select log action:" 18 60 10 \
1 "View full log" \
2 "Follow log live" \
3 "Show log location" \
4 "Show last 20 log lines" \
0 "Back" \
3>&1 1>&2 2>&3)
clear
case "$log_choice" in
1)
log_file="$(get_log_file)"
if [[ -n "$log_file" ]]; then
less "$log_file"
else
echo "No log file found yet."
read -rp "Press Enter to continue..."
fi
;;
2)
log_file="$(get_log_file)"
if [[ -n "$log_file" ]]; then
echo "Press Ctrl+C to stop"
tail -n 20 -f "$log_file"
else
echo "No log file found yet."
fi
read -rp "Press Enter to continue..."
;;
3)
log_file="$(get_log_file)"
if [[ -n "$log_file" ]]; then
echo "Log file:"
echo "$log_file"
else
echo "No log file found yet."
fi
read -rp "Press Enter to continue..."
;;
4)
log_file="$(get_log_file)"
if [[ -n "$log_file" ]]; then
tail -n 20 "$log_file"
else
echo "No log file found yet."
fi
read -rp "Press Enter to continue..."
;;
0|"")
break
;;
esac
done
}
while true; do
choice=$(dialog --clear \
--backtitle "Update Manager" \
--title "Choose an action" \
--menu "Select option:" 20 60 10 \
1 "Check all hosts" \
2 "View hosts file" \
3 "Edit hosts file" \
4 "Add host" \
5 "Remove host" \
6 "Log menu" \
0 "Exit" \
3>&1 1>&2 2>&3)
clear
case "$choice" in
1)
bash "$UPDATE_MANAGER_SCRIPT" check
read -rp "Press Enter to continue..."
;;
2)
less "$HOSTS_FILE"
;;
3)
nano "$HOSTS_FILE"
;;
4)
read -rp "Name: " name
read -rp "IP: " ip
read -rp "User: " user
if [[ -z "$name" || -z "$ip" || -z "$user" ]]; then
echo "All fields are required."
else
echo "$name $ip $user" >> "$HOSTS_FILE"
echo "Host added."
fi
read -rp "Press Enter to continue..."
;;
5)
nl -w2 -s'. ' "$HOSTS_FILE"
read -rp "Line to remove: " line
if [[ "$line" =~ ^[0-9]+$ ]]; then
sed -i "${line}d" "$HOSTS_FILE"
echo "Host removed."
else
echo "Invalid line number."
fi
read -rp "Press Enter to continue..."
;;
6)
show_log_menu
;;
0|"")
clear
exit 0
;;
esac
done

View file

@ -1,127 +1,79 @@
#!/usr/bin/env bash #!/usr/bin/env bash
CONFIG_FILE="/etc/update-manager.conf" UPDATE_MANAGER_SCRIPT="$HOME/update-manager/update-manager.sh"
[[ -f "$CONFIG_FILE" ]] || CONFIG_FILE="./update-manager.conf" HOSTS_FILE="/opt/update-manager/hosts.conf"
if [[ -f "$CONFIG_FILE" ]]; then get_log_file() {
# shellcheck disable=SC1090 local primary="/opt/update-manager/log/update-manager.log"
source "$CONFIG_FILE" local fallback="$HOME/update-manager/log/update-manager.log"
fi
DEFAULT_HOSTS_FILE="${HOSTS_FILE:-/opt/update-manager/hosts.conf}" if [[ -f "$primary" ]]; then
SSH_OPTIONS="${SSH_OPTIONS:--o BatchMode=yes -o ConnectTimeout=5}" echo "$primary"
########################################
# Ensure hosts file exists
########################################
ensure_hosts_file() {
if [[ ! -f "$DEFAULT_HOSTS_FILE" ]]; then
echo "Creating example hosts file: $DEFAULT_HOSTS_FILE"
mkdir -p "$(dirname "$DEFAULT_HOSTS_FILE")"
cat > "$DEFAULT_HOSTS_FILE" <<'EOF'
# Example hosts file
# Format:
# name ip user
#
# server1 192.168.1.10 user
# server2 192.168.1.20 user
# server3 10.0.0.5 root
EOF
fi
}
########################################
# Helpers
########################################
get_hosts_file() {
if [[ -n "$2" ]]; then
echo "$2"
else else
echo "$DEFAULT_HOSTS_FILE" echo "$fallback"
fi fi
} }
usage() { while true; do
cat <<USAGE choice=$(dialog --clear \
Usage: --backtitle "Update Manager" \
$0 check [hosts_file] --title "Choose an action" \
--menu "Select option:" 15 50 10 \
1 "Check all hosts" \
2 "View hosts file" \
3 "Edit hosts file" \
4 "Add host" \
5 "Remove host" \
6 "View log" \
7 "Follow log live" \
8 "Show log location" \
0 "Exit" \
3>&1 1>&2 2>&3)
Examples: clear
$0 check
$0 check /path/to/hosts.conf
HOSTS_FILE=/path/to/hosts.conf $0 check
USAGE
}
######################################## case $choice in
# Check single host 1)
######################################## bash "$UPDATE_MANAGER_SCRIPT" check
read -rp "Press Enter to continue..."
check_host() { ;;
local name="$1" 2)
local ip="$2" less "$HOSTS_FILE"
local user="$3" ;;
local result 3)
local rc nano "$HOSTS_FILE"
;;
result=$(ssh -n $SSH_OPTIONS "${user}@${ip}" \ 4)
"apt list --upgradable 2>/dev/null | sed '/^Listing/d'" 2>&1) read -rp "Name: " name
rc=$? read -rp "IP: " ip
read -rp "User: " user
printf "===== %s (%s) =====\n" "$name" "$ip" echo "$name $ip $user" >> "$HOSTS_FILE"
echo "Host added."
if [[ $rc -ne 0 ]]; then read -rp "Press Enter to continue..."
echo "❌ connection failed" ;;
echo "$result" 5)
echo nl -w2 -s'. ' "$HOSTS_FILE"
return read -rp "Line to remove: " line
fi sed -i "${line}d" "$HOSTS_FILE"
echo "Host removed."
if [[ -z "$result" ]]; then read -rp "Press Enter to continue..."
echo "Up-to-date" ;;
else 6)
echo "$result" less "$(get_log_file)"
fi ;;
7)
echo echo "Press Ctrl+C to stop"
} tail -f "$(get_log_file)"
read -rp "Press Enter to continue..."
######################################## ;;
# Check all hosts 8)
######################################## echo "Log file:"
echo "$(get_log_file)"
check_all() { read -rp "Press Enter to continue..."
local hosts_file="$1" ;;
0)
ensure_hosts_file clear
exit 0
while IFS= read -r line; do ;;
[[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continue esac
done
local name ip user
read -r name ip user <<< "$line"
[[ -z "$name" || -z "$ip" || -z "$user" ]] && continue
check_host "$name" "$ip" "$user"
done < "$hosts_file"
}
########################################
# Main
########################################
case "${1:-}" in
check)
HOSTS_FILE_TO_USE="$(get_hosts_file "$@")"
check_all "$HOSTS_FILE_TO_USE"
;;
*)
usage
exit 1
;;
esac