SysDeck/packaging/setup.py

176 lines
5.9 KiB
Python
Executable File

#!/usr/bin/env python3
"""
SysDeck - setup.py
Author: Jeremy Anderson (https://dcos.net)
Pip-installable package that lays out the Cockpit plugin suite under
/usr/share/cockpit/sysdeck-*/ (one directory per plugin) and the Python
bridge helpers under /usr/lib/sysdeck/bridge/.
Works on all supported distros (Arch, Debian, Fedora/RHEL).
The distro detection at install time determines which Python
site-packages directory gets the bridge symlink.
Usage:
pip3 install packaging/
pip3 install packaging/ --target=/tmp/overlay
The standalone web console (web/) is not installed by this package —
it ships in the master tarball and runs from its own directory.
"""
import os
import shutil
import subprocess
from setuptools import setup
VERSION = "0.4.5"
PACKAGE = "sysdeck"
# The repository root (setup.py lives in packaging/).
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
def repo(*parts):
"""Path to a file in the repository root."""
return os.path.join(ROOT, *parts)
def glob_files(directory, pattern="*"):
"""Return all files matching pattern under directory."""
result = []
for root, _dirs, files in os.walk(directory):
for f in files:
if pattern == "*" or f.endswith(pattern):
result.append(os.path.join(root, f))
return result
def plugin_data_files():
"""One data_files entry per sysdeck-* plugin directory.
The multi-plugin layout (v0.0.20+): each plugins/sysdeck-<name>/
directory installs to /usr/share/cockpit/sysdeck-<name>/.
"""
entries = []
plugins_dir = repo("plugins")
if not os.path.isdir(plugins_dir):
return entries
for name in sorted(os.listdir(plugins_dir)):
pdir = os.path.join(plugins_dir, name)
if not os.path.isdir(pdir) or not name.startswith("sysdeck-"):
continue
files = [
os.path.join(pdir, f)
for f in sorted(os.listdir(pdir))
if f.endswith((".json", ".html", ".js", ".css"))
]
if files:
entries.append((f"/usr/share/cockpit/{name}", files))
return entries
def detect_distro():
"""Detect distro for distro-aware post-install actions."""
try:
with open("/etc/os-release", encoding="utf-8") as fh:
for line in fh:
if line.startswith("ID="):
return line.split("=", 1)[1].strip().strip('"').lower()
except (FileNotFoundError, PermissionError):
pass
return "unknown"
def _existing(*parts):
"""Return the path when it exists, else None (tolerant helper)."""
path = repo(*parts)
return path if os.path.exists(path) else None
def _dir_files(*parts, ext):
"""All files with the given suffix in a repository directory."""
directory = repo(*parts)
if not os.path.isdir(directory):
return []
return [
os.path.join(directory, f)
for f in sorted(os.listdir(directory))
if f.endswith(ext)
]
setup(
name=PACKAGE,
version=VERSION,
description=(
"Unified operations surface for Linux infrastructure — the Cockpit "
"plugin edition (27 domain modules plus the Python bridge); the "
"standalone web console ships in the master tarball."
),
long_description=open(repo("README.md")).read() if os.path.exists(repo("README.md")) else "",
long_description_content_type="text/markdown",
author="Jeremy Anderson",
author_email="info@dcos.net",
url="https://dcos.net",
license="MIT",
python_requires=">=3.9",
extras_require={
"glances": ["glances"],
"benchmark": ["sysbench"],
},
data_files=(
plugin_data_files()
+ [
# Shared bridge + styles: /usr/share/cockpit/sysdeck-common/
# (the manifest.json is REQUIRED — without it every
# /cockpit/@localhost/sysdeck-common/bridge.js URL 404s)
(
"/usr/share/cockpit/sysdeck-common",
[
p
for p in (
_existing("shared", "manifest.json"),
_existing("shared", "bridge.js"),
_existing("shared", "sysdeck.css"),
_existing("shared", "sysdeck-web.css"),
)
if p
],
),
# Python bridge helpers: /usr/lib/sysdeck/bridge/
# (invoked by absolute path — python3 /usr/lib/sysdeck/bridge/<module>.py)
("/usr/lib/sysdeck/bridge", _dir_files("bridge", ext=".py")),
("/usr/lib/sysdeck/bridge/modules", _dir_files("bridge", "modules", ext=".py")),
# Firewall templates + policies: /usr/share/sysdeck/firewall/
("/usr/share/sysdeck/firewall/templates", _dir_files("firewall", "templates", ext=".sh")),
("/usr/share/sysdeck/firewall/policies", _dir_files("firewall", "policies", ext=".yaml")),
# Prometheus + Grafana provisioning: /usr/share/sysdeck/prometheus/
("/usr/share/sysdeck/prometheus", _dir_files("prometheus", ext=".yml")),
# Documentation: /usr/share/doc/sysdeck/
(
"/usr/share/doc/sysdeck",
[
p
for p in (
_existing("README.md"),
_existing("QUICKSTART.md"),
_existing("LICENSE"),
)
if p
],
),
]
),
classifiers=[
"Development Status :: 4 - Beta",
"Environment :: Web Environment",
"Intended Audience :: System Administrators",
"License :: OSI Approved :: MIT License",
"Operating System :: POSIX :: Linux",
"Programming Language :: JavaScript",
"Programming Language :: Python :: 3",
"Topic :: System :: Systems Administration",
],
)