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.
@ -26,10 +29,10 @@ Built for Lanx environments lightweight, fast and no unnecessary dependencie
## Features
* 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
* Built-in logging (file + terminal)
* Dialog-based UI menu
* No agents required
* Works with existing SSH setup
@ -37,12 +40,20 @@ Built for Lanx environments lightweight, fast and no unnecessary dependencie
## Update Manager UI
### Main menu
<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 align="center">
<em>Lightweight • Terminal-based • Works over SSH</em>
<em>Lightweight • No dependencies • Works over SSH</em>
</p>
---
@ -57,7 +68,7 @@ sudo apt update
sudo apt install dialog openssh-client
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-ui.sh
@ -134,6 +145,12 @@ server3 192.168.1.30 user
## Usage
### Check updates
```bash
update-manager check
```
### Start UI
```bash
@ -142,42 +159,25 @@ update-manager-ui
---
### Check updates (CLI)
```bash
update-manager check
```
---
## Logging
The tool logs both to terminal and file.
### Primary location
Log file location:
```bash
/opt/update-manager/log/update-manager.log
```
### Fallback location
View log:
```bash
~/update-manager/log/update-manager.log
less /opt/update-manager/log/update-manager.log
```
### Notes
Follow log:
* Log directory is created automatically
* Log file is created automatically
* Output is written to both terminal and file
* Log levels:
* INFO
* WARN
* ERROR
Logs can be viewed directly from the UI.
```bash
tail -f /opt/update-manager/log/update-manager.log
```
---
@ -189,8 +189,8 @@ Logs can be viewed directly from the UI.
├── update-manager-ui.sh
├── update-manager.conf
├── hosts.conf
── log/
└── update-manager.log
── log/
└── update-manager.log
```
---
@ -199,6 +199,7 @@ Logs can be viewed directly from the UI.
* Uses SSH to connect to each host
* Runs `apt` commands remotely
* Logs results locally
* No agents or services needed
* Designed for simple and efficient operations
@ -209,7 +210,6 @@ Logs can be viewed directly from the UI.
* SSH access to all hosts
* SSH keys recommended (no password prompts)
* Ubuntu/Debian-based systems
* `dialog` (for UI)
---
@ -218,7 +218,6 @@ Logs can be viewed directly from the UI.
* 🔔 Notifications (ntfy / push alerts)
* 🌐 Web interface
* 📧 Email reporting
* 📜 Advanced logging / audit trail
* 📊 Basic monitoring (status, last check, pending updates)
* 🧩 Plugin system (extensible modules)
* 🔐 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
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.
---
## 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
CONFIG_FILE="/etc/update-manager.conf"
[[ -f "$CONFIG_FILE" ]] || CONFIG_FILE="./update-manager.conf"
UPDATE_MANAGER_SCRIPT="$HOME/update-manager/update-manager.sh"
HOSTS_FILE="/opt/update-manager/hosts.conf"
if [[ -f "$CONFIG_FILE" ]]; then
# shellcheck disable=SC1090
source "$CONFIG_FILE"
fi
get_log_file() {
local primary="/opt/update-manager/log/update-manager.log"
local fallback="$HOME/update-manager/log/update-manager.log"
DEFAULT_HOSTS_FILE="${HOSTS_FILE:-/opt/update-manager/hosts.conf}"
SSH_OPTIONS="${SSH_OPTIONS:--o BatchMode=yes -o ConnectTimeout=5}"
########################################
# 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"
if [[ -f "$primary" ]]; then
echo "$primary"
else
echo "$DEFAULT_HOSTS_FILE"
echo "$fallback"
fi
}
usage() {
cat <<USAGE
Usage:
$0 check [hosts_file]
while true; do
choice=$(dialog --clear \
--backtitle "Update Manager" \
--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:
$0 check
$0 check /path/to/hosts.conf
HOSTS_FILE=/path/to/hosts.conf $0 check
USAGE
}
clear
########################################
# Check single host
########################################
check_host() {
local name="$1"
local ip="$2"
local user="$3"
local result
local rc
result=$(ssh -n $SSH_OPTIONS "${user}@${ip}" \
"apt list --upgradable 2>/dev/null | sed '/^Listing/d'" 2>&1)
rc=$?
printf "===== %s (%s) =====\n" "$name" "$ip"
if [[ $rc -ne 0 ]]; then
echo "❌ connection failed"
echo "$result"
echo
return
fi
if [[ -z "$result" ]]; then
echo "Up-to-date"
else
echo "$result"
fi
echo
}
########################################
# Check all hosts
########################################
check_all() {
local hosts_file="$1"
ensure_hosts_file
while IFS= read -r line; do
[[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continue
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
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
echo "$name $ip $user" >> "$HOSTS_FILE"
echo "Host added."
read -rp "Press Enter to continue..."
;;
5)
nl -w2 -s'. ' "$HOSTS_FILE"
read -rp "Line to remove: " line
sed -i "${line}d" "$HOSTS_FILE"
echo "Host removed."
read -rp "Press Enter to continue..."
;;
6)
less "$(get_log_file)"
;;
7)
echo "Press Ctrl+C to stop"
tail -f "$(get_log_file)"
read -rp "Press Enter to continue..."
;;
8)
echo "Log file:"
echo "$(get_log_file)"
read -rp "Press Enter to continue..."
;;
0)
clear
exit 0
;;
esac
done