Add Debian packaging support for socktop-agent (#25)

* Add Debian packaging support with cargo-deb

- Add cargo-deb metadata to socktop and socktop_agent Cargo.toml
- Create systemd service file for socktop_agent
- Add postinst/postrm maintainer scripts for user/group management
- Create GitHub Actions workflow to build .deb packages for AMD64 and ARM64
- Add comprehensive documentation in docs/DEBIAN_PACKAGING.md
- Packages will be available as artifacts on every push
- Automatic GitHub releases for version tags

* Add summary documentation for debian packaging

* fix unit test, move to macro cargo_bin!

* hotfix for issue with socktop agent not creating ssl certificate on first launch after upgrade of axum server version.

* Add helpful post-install message to guide users on enabling socktop-agent service

* Fix CI build by installing libdrm development dependencies

* Fix package rename script - cargo-deb already includes architecture in filename

* Make GPU support optional to enable RISC-V builds without libdrm

- Add 'gpu' feature flag (enabled by default)
- Make gfxinfo dependency optional
- Provide no-op GPU metrics when gpu feature disabled
- Disable GPU support for RISC-V builds in CI (libdrm unavailable)
- All other architectures (amd64, arm64, armhf) still get GPU support

* feature gate GPU stats for arm v7

* specify correct package names.

* install aarch64-linux-gnu-gcc build dep

* specify correct package name

* add RISC-V GCC compiler

* add .cargo to gitignore to elimicate issue with riscv64-linux-gnu-gcc linker in config.toml

* add gcc-arm-linux-gnueabihf linker fore armv7

* set correct x-compile lib gcc-aarch64-linux-gnu for arm64 builds.

* add ports.ubuntu.com to sources

* Add ARM64 as a foreign architecture

* fixe for ARM64 build.

* security.ubuntu.com` aNNOYING

* apt repo github page

* copy output to apt repo

* fix secrets path

* fix secrets path

* change build dep

* Fix postinst message box alignment

* ci(deb): restrict APT publish to v* release tags

Previously the workflow built and published on every push to master
and feature/debian-packaging in addition to v* tags. That meant the
gh-pages APT repo got overwritten on every commit with same-version
.debs, causing apt clients to see a phantom "update available" each
time and burning ~5-10 min of cross-compile CI per merge.

After this change:
  - PRs into master still cross-build .debs as a sanity check.
  - v* tags build, publish to gh-pages, and create a GitHub release.
  - workflow_dispatch remains as the manual escape hatch.
  - master pushes no longer trigger this workflow (ci.yml still runs).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-02 13:12:51 -07:00
committed by GitHub
parent 697a77bdab
commit ee4468ca23
23 changed files with 1293 additions and 14 deletions
+23 -2
View File
@@ -6,6 +6,8 @@ description = "Socktop agent daemon. Serves host metrics over WebSocket."
edition = "2024"
license = "MIT"
readme = "README.md"
homepage = "https://github.com/jasonwitty/socktop"
repository = "https://github.com/jasonwitty/socktop"
[dependencies]
# Tokio: Use minimal features instead of "full" to reduce binary size
@@ -21,7 +23,7 @@ flate2 = { version = "1", default-features = false, features = ["rust_backend"]
futures-util = "0.3.31"
tracing = { version = "0.1", optional = true }
tracing-subscriber = { version = "0.3", features = ["env-filter"], optional = true }
gfxinfo = "0.1.2"
gfxinfo = { version = "0.1.2", optional = true }
once_cell = "1.19"
axum-server = { version = "0.7", features = ["tls-rustls"] }
rustls = { version = "0.23", features = ["aws-lc-rs"] }
@@ -33,7 +35,8 @@ prost = { workspace = true }
time = { version = "0.3", default-features = false, features = ["formatting", "macros", "parsing" ] }
[features]
default = []
default = ["gpu"]
gpu = ["gfxinfo"]
logging = ["tracing", "tracing-subscriber"]
[build-dependencies]
@@ -45,3 +48,21 @@ protoc-bin-vendored = "3"
assert_cmd = "2.0"
tempfile = "3.10"
tokio-tungstenite = "0.21"
[package.metadata.deb]
maintainer = "Jason Witty <jasonpwitty+socktop@proton.me>"
copyright = "2024, Jason Witty <jasonpwitty+socktop@proton.me>"
license-file = ["../LICENSE", "4"]
extended-description = """\
socktop_agent is the daemon component that runs on remote hosts to collect \
and serve system metrics over WebSocket. It gathers CPU, memory, disk, network, \
GPU, and process information that can be monitored remotely by the socktop TUI client."""
depends = "$auto"
section = "admin"
priority = "optional"
assets = [
["target/release/socktop_agent", "usr/bin/", "755"],
["../README.md", "usr/share/doc/socktop_agent/", "644"],
]
maintainer-scripts = "debian/"
systemd-units = { unit-name = "socktop-agent", unit-scripts = ".", enable = false }
+57
View File
@@ -0,0 +1,57 @@
#!/bin/sh
set -e
# Create socktop user and group if they don't exist
if ! getent group socktop >/dev/null; then
addgroup --system socktop
fi
if ! getent passwd socktop >/dev/null; then
adduser --system --ingroup socktop --home /var/lib/socktop \
--no-create-home --disabled-password --disabled-login \
--gecos "Socktop Agent" socktop
fi
# Create state directory
mkdir -p /var/lib/socktop
chown socktop:socktop /var/lib/socktop
chmod 755 /var/lib/socktop
# Create config directory if it doesn't exist
mkdir -p /etc/socktop
chmod 755 /etc/socktop
#DEBHELPER#
# Print helpful message to the user
cat <<EOF
┌─────────────────────────────────────────────────────────────────────┐
│ socktop-agent has been installed successfully! │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ The systemd service has been installed but is NOT enabled by │
│ default. To enable and start the service: │
│ │
│ sudo systemctl enable --now socktop-agent │
│ │
│ To start without enabling on boot: │
│ │
│ sudo systemctl start socktop-agent │
│ │
│ To check service status: │
│ │
│ sudo systemctl status socktop-agent │
│ │
│ Default settings: │
│ - Port: 3000 (use -p or --port to change) │
│ - SSL/TLS: disabled (use --enableSSL to enable) │
│ │
│ For more information, see: │
│ /usr/share/doc/socktop_agent/README.md │
│ │
└─────────────────────────────────────────────────────────────────────┘
EOF
exit 0
+34
View File
@@ -0,0 +1,34 @@
#!/bin/sh
set -e
case "$1" in
purge)
# Remove user and group on purge
if getent passwd socktop >/dev/null; then
deluser --quiet socktop || true
fi
if getent group socktop >/dev/null; then
delgroup --quiet socktop || true
fi
# Remove state directory on purge
rm -rf /var/lib/socktop
# Remove config directory if empty
rmdir --ignore-fail-on-non-empty /etc/socktop 2>/dev/null || true
;;
remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
# Do nothing on remove/upgrade
;;
*)
echo "postrm called with unknown argument \`$1'" >&2
exit 1
;;
esac
#DEBHELPER#
exit 0
+27
View File
@@ -0,0 +1,27 @@
[Unit]
Description=Socktop Agent - Remote System Monitor
Documentation=https://github.com/jasonwitty/socktop
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/bin/socktop_agent --port 3000
Environment=RUST_LOG=info
# Optional authentication token:
# Environment=SOCKTOP_TOKEN=changeme
Restart=on-failure
RestartSec=5
User=socktop
Group=socktop
NoNewPrivileges=true
# Security hardening
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/socktop
StateDirectory=socktop
[Install]
WantedBy=multi-user.target
+8
View File
@@ -1,4 +1,5 @@
// gpu.rs
#[cfg(feature = "gpu")]
use gfxinfo::active_gpu;
#[derive(Debug, Clone, serde::Serialize)]
@@ -9,6 +10,7 @@ pub struct GpuMetrics {
pub mem_total_bytes: u64,
}
#[cfg(feature = "gpu")]
pub fn collect_all_gpus() -> Result<Vec<GpuMetrics>, Box<dyn std::error::Error>> {
let gpu = active_gpu()?; // Use ? to unwrap Result
let info = gpu.info();
@@ -22,3 +24,9 @@ pub fn collect_all_gpus() -> Result<Vec<GpuMetrics>, Box<dyn std::error::Error>>
Ok(vec![metrics])
}
#[cfg(not(feature = "gpu"))]
pub fn collect_all_gpus() -> Result<Vec<GpuMetrics>, Box<dyn std::error::Error>> {
// GPU support not available on this platform
Ok(vec![])
}