494 lines
24 KiB
Makefile
Executable File
494 lines
24 KiB
Makefile
Executable File
# SysDeck - Makefile
|
|
# Author: Jeremy Anderson (https://dcos.net)
|
|
#
|
|
# v0.3.0 AI GATEWAY EDITION: two distributions in one tree —
|
|
# / the cockpit edition: 27 standalone Cockpit plugins + shared bridge
|
|
# /web the SysDeck Web Edition (Next.js console, 29 bridge modules)
|
|
# /klanker-gate — the Frosty Deno LLM gateway, vendored + pre-integrated
|
|
# (by TykoDev, https://github.com/TykoDev/klanker-gate,
|
|
# Apache-2.0 — not SysDeck code; own version 0.9.0, with
|
|
# arch/ packaging for Arch Linux)
|
|
# /web/mini-services/fester — Fester, vendored + pre-integrated (own version 0.2.1)
|
|
# Each plugin ships to /usr/share/cockpit/sysdeck-<name>/ and appears as
|
|
# its own sidebar entry in Cockpit. The Python bridge helpers stay at
|
|
# /usr/lib/sysdeck/bridge/ (called via cockpit.spawn).
|
|
#
|
|
# Targets:
|
|
# make install - install all 27 plugins + bridge + scripts + firewall templates
|
|
# make uninstall - remove all 27 plugins + bridge + scripts
|
|
# make check - validate all manifests against cockpit-podman pattern,
|
|
# syntax-check JS/Python/shell sources, run unit tests
|
|
# make plugins - verify plugins/ + shared/ match the generator catalog
|
|
# make clean - remove build artifacts
|
|
# make dist - build the source tarball (runs check first)
|
|
# make distcheck - extract the tarball into a clean dir and run check inside
|
|
# make fester-start - run the vendored fester service on :3010 (bun)
|
|
# make web-install - install + migrate the web edition (bun + prisma)
|
|
# make web-dev - start fester (background) + the web console on :3000
|
|
# make master - build the master tarball (cockpit + web + fester)
|
|
#
|
|
# Distro support: Arch Linux, Debian/Ubuntu, Fedora/RHEL/CentOS.
|
|
|
|
PACKAGE := sysdeck
|
|
VERSION := 0.4.6
|
|
LIB_DIR := $(DESTDIR)/usr/lib/$(PACKAGE)
|
|
PYTHON_DIR := $(LIB_DIR)/bridge
|
|
SHARE_DIR := $(DESTDIR)/usr/share/$(PACKAGE)
|
|
# Firewall templates (pre-built nftables rulesets the operator selects
|
|
# from the panel — applied via the org.sysdeck.firewall.modify polkit
|
|
# action). Seven topologies ship; operators can drop more in.
|
|
FIREWALL_TEMPLATES_DIR := $(SHARE_DIR)/firewall/templates
|
|
|
|
# Python bridge helpers (called via `python3 -m sysdeck.bridge.<module>`).
|
|
PY_FILES := $(wildcard bridge/*.py bridge/modules/*.py)
|
|
|
|
# Unit tests.
|
|
TEST_FILES := $(wildcard tests/*.py)
|
|
|
|
# Documentation.
|
|
DOC_FILES := $(wildcard docs/*.md)
|
|
|
|
# AppStream metainfo + PolKit policy.
|
|
METAINFO_FILE := packaging/sysdeck.metainfo.xml
|
|
POLKIT_FILE := packaging/polkit/org.sysdeck.policy
|
|
|
|
# Diagnostic + smoke-test scripts.
|
|
DIAGNOSE_SCRIPT := sysdeck-diagnose.sh
|
|
SMOKE_TEST_SCRIPT := cockpit-smoke-test.sh
|
|
UNINSTALL_SCRIPT := sysdeck-uninstall.sh
|
|
|
|
# Generator script (regenerates plugins/ and shared/).
|
|
GENERATOR := scripts/generate-plugins.py
|
|
|
|
.PHONY: install uninstall check clean dist distcheck plugins fester-start web-install web-dev master install-branding uninstall-branding
|
|
|
|
# ─── plugins: verify the tree against the generator catalog ─────────
|
|
# The shipped plugins/ and shared/ assets are hand-maintained source;
|
|
# the generator VERIFIES catalog <-> disk consistency and never writes.
|
|
# (--force exists only to bootstrap a tree with missing plugin dirs.)
|
|
plugins:
|
|
@echo ">>> Verifying plugins/ and shared/ against the catalog in $(GENERATOR)"
|
|
python3 $(GENERATOR)
|
|
|
|
# ─── install: 27 visible plugins + shared/ + bridge + scripts + metainfo ────
|
|
install:
|
|
@echo ">>> Installing $(PACKAGE) $(VERSION): 27 standalone Cockpit plugins"
|
|
# Each plugin: /usr/share/cockpit/sysdeck-<name>/{manifest.json,index.html,<module>.js}
|
|
@for plugin in plugins/sysdeck-*; do \
|
|
[ -d "$$plugin" ] || continue; \
|
|
name=$$(basename "$$plugin"); \
|
|
destdir=$(DESTDIR)/usr/share/cockpit/$$name; \
|
|
install -d "$$destdir"; \
|
|
for f in $$plugin/manifest.json $$plugin/index.html $$plugin/*.js; do \
|
|
[ -f "$$f" ] || continue; \
|
|
install -m 0644 "$$f" "$$destdir/$$(basename $$f)"; \
|
|
done; \
|
|
echo " installed $$name"; \
|
|
done
|
|
# Shared bridge + CSS + manifest: /usr/share/cockpit/sysdeck-common/
|
|
# The manifest.json is REQUIRED — without it, cockpit doesn't register
|
|
# sysdeck-common as a package, and every URL like
|
|
# /cockpit/@localhost/sysdeck-common/bridge.js returns 404.
|
|
# Verified from cockpit's pkg/static/manifest.json (just `{}`).
|
|
install -d $(DESTDIR)/usr/share/cockpit/sysdeck-common
|
|
install -m 0644 shared/manifest.json $(DESTDIR)/usr/share/cockpit/sysdeck-common/manifest.json
|
|
install -m 0644 shared/bridge.js $(DESTDIR)/usr/share/cockpit/sysdeck-common/bridge.js
|
|
install -m 0644 shared/sysdeck.css $(DESTDIR)/usr/share/cockpit/sysdeck-common/sysdeck.css
|
|
# v0.3.0: the web-edition skin (midnight/teal design of the Next.js
|
|
# console) — every plugin index.html links it after base sysdeck.css.
|
|
# Remove the file to revert plugin pages to the classic 0.1.x skin.
|
|
install -m 0644 shared/sysdeck-web.css $(DESTDIR)/usr/share/cockpit/sysdeck-common/sysdeck-web.css
|
|
# Python bridge helpers: /usr/lib/sysdeck/bridge/
|
|
# v0.0.27: install each helper as an executable script (0755, not 0644)
|
|
# so they can be invoked by absolute path:
|
|
# python3 /usr/lib/sysdeck/bridge/glances.py snapshot
|
|
# No PYTHONPATH or symlink needed — the JS calls them directly.
|
|
# Each helper has a `if __name__ == "__main__": sys.exit(main(sys.argv[1:]))` guard.
|
|
install -d $(PYTHON_DIR)
|
|
@for f in $(PY_FILES); do \
|
|
rel=$$(echo $$f | sed 's|^bridge/||'); \
|
|
install -d $$(dirname $(PYTHON_DIR)/$$rel); \
|
|
install -m 0755 $$f $(PYTHON_DIR)/$$rel; \
|
|
done
|
|
# Unit tests: /usr/lib/sysdeck/tests/
|
|
install -d $(LIB_DIR)/tests
|
|
@for f in $(TEST_FILES); do \
|
|
rel=$$(echo $$f | sed 's|^tests/||'); \
|
|
install -m 0644 $$f $(LIB_DIR)/tests/$$rel; \
|
|
done
|
|
# Documentation: /usr/share/doc/sysdeck/
|
|
install -d $(DESTDIR)/usr/share/doc/$(PACKAGE)
|
|
@for f in $(DOC_FILES); do \
|
|
install -m 0644 $$f $(DESTDIR)/usr/share/doc/$(PACKAGE)/$$(basename $$f); \
|
|
done
|
|
install -m 0644 README.md $(DESTDIR)/usr/share/doc/$(PACKAGE)/README.md
|
|
install -m 0644 QUICKSTART.md $(DESTDIR)/usr/share/doc/$(PACKAGE)/QUICKSTART.md
|
|
install -m 0644 LICENSE $(DESTDIR)/usr/share/doc/$(PACKAGE)/LICENSE
|
|
# Diagnostic + smoke-test scripts: /usr/share/sysdeck/
|
|
install -d $(DESTDIR)/usr/share/$(PACKAGE)
|
|
install -m 0755 $(DIAGNOSE_SCRIPT) $(DESTDIR)/usr/share/$(PACKAGE)/$(DIAGNOSE_SCRIPT)
|
|
install -m 0755 $(SMOKE_TEST_SCRIPT) $(DESTDIR)/usr/share/$(PACKAGE)/$(SMOKE_TEST_SCRIPT)
|
|
install -m 0755 $(UNINSTALL_SCRIPT) $(DESTDIR)/usr/share/$(PACKAGE)/$(UNINSTALL_SCRIPT)
|
|
# v0.0.31: firewall templates — pre-built nftables rulesets the
|
|
# operator selects from the Firewall panel. The bridge firewall.py
|
|
# `templates` and `apply-template` subcommands read this directory.
|
|
# Templates are executable (0755) because they are invoked by the
|
|
# bridge via `bash <template>.sh start|stop|detect|...` under the
|
|
# org.sysdeck.firewall.modify polkit action.
|
|
# v0.0.36: also install firewall/policies/*.yaml — the Cilium
|
|
# default policy file (cilium-default.yaml) lives here and is
|
|
# applied by cilium.sh `start`. Operator overrides go in
|
|
# /etc/sysdeck/firewall/ (not shipped by the package).
|
|
install -d $(FIREWALL_TEMPLATES_DIR)
|
|
@for f in firewall/templates/*.sh; do \
|
|
[ -f "$$f" ] || continue; \
|
|
install -m 0755 $$f $(FIREWALL_TEMPLATES_DIR)/$$(basename $$f); \
|
|
echo " installed firewall template: $$(basename $$f)"; \
|
|
done
|
|
install -d $(SHARE_DIR)/firewall/policies
|
|
@for f in firewall/policies/*.yaml; do \
|
|
[ -f "$$f" ] || continue; \
|
|
install -m 0644 $$f $(SHARE_DIR)/firewall/policies/$$(basename $$f); \
|
|
echo " installed firewall policy: $$(basename $$f)"; \
|
|
done
|
|
# v0.0.43: Prometheus + Grafana config files — scrape configs,
|
|
# alert rules, datasource + dashboard provisioning. Operators
|
|
# include these in their prometheus.yml / grafana provisioning
|
|
# dirs. Shipped read-only at /usr/share/sysdeck/prometheus/.
|
|
install -d $(SHARE_DIR)/prometheus
|
|
@for f in prometheus/*.yml; do \
|
|
[ -f "$$f" ] || continue; \
|
|
install -m 0644 $$f $(SHARE_DIR)/prometheus/$$(basename $$f); \
|
|
echo " installed monitoring config: $$(basename $$f)"; \
|
|
done
|
|
# AppStream metainfo: /usr/share/metainfo/
|
|
install -d $(DESTDIR)/usr/share/metainfo
|
|
install -m 0644 $(METAINFO_FILE) $(DESTDIR)/usr/share/metainfo/sysdeck.metainfo.xml
|
|
# PolKit policy: /usr/share/polkit-1/actions/
|
|
install -d $(DESTDIR)/usr/share/polkit-1/actions
|
|
install -m 0644 $(POLKIT_FILE) $(DESTDIR)/usr/share/polkit-1/actions/org.sysdeck.policy
|
|
# v0.0.46: 3rd-party-modules polkit action (org.sysdeck.modules3p.modify)
|
|
install -m 0644 packaging/polkit/org.sysdeck.modules3p.policy $(DESTDIR)/usr/share/polkit-1/actions/
|
|
# Host integration (polkit reload, AppStream refresh) runs only
|
|
# on a real install — a DESTDIR staging install (deb/rpm/pacman
|
|
# package build) must never mutate the build host.
|
|
-@if [ -z "$(DESTDIR)" ]; then \
|
|
if command -v systemctl >/dev/null 2>&1; then \
|
|
systemctl reload polkit 2>/dev/null || true; \
|
|
fi; \
|
|
if command -v appstreamcli >/dev/null 2>&1; then \
|
|
appstreamcli refresh-cache 2>/dev/null || true; \
|
|
fi; \
|
|
fi
|
|
@echo ">>> Done. Restart cockpit.socket to pick up the new plugins:"
|
|
@echo " sudo systemctl restart cockpit.socket"
|
|
@echo ">>> 27 sidebar entries should appear under 'SysDeck <Name>' in Cockpit."
|
|
|
|
# ─── uninstall: remove EVERY trace of EVERY prior version ──────────
|
|
# This target is deliberately over-aggressive. It removes:
|
|
# - /usr/share/cockpit/sysdeck/ (v0.0.9-v0.0.19 single-plugin layout)
|
|
# - /usr/share/cockpit/sysdeck-*/ (v0.0.20+ multi-plugin layout, including sysdeck-common)
|
|
# - /usr/lib/sysdeck/ (Python bridge helpers, all versions)
|
|
# - /usr/share/sysdeck/ (diagnostic scripts, v0.0.19+)
|
|
# - /usr/share/doc/sysdeck/ (docs, v0.0.20+)
|
|
# - /usr/share/metainfo/sysdeck.metainfo.xml (AppStream metainfo, v0.0.17+)
|
|
# - /usr/share/polkit-1/actions/org.sysdeck.policy (PolKit policy, v0.0.17+)
|
|
# - python site-packages sysdeck symlink (all versions)
|
|
# - the pacman-installed sysdeck package (if installed via PKGBUILD)
|
|
# Without this thorough cleanup, cockpit would discover stale manifests from
|
|
# prior versions and serve old broken code instead of the freshly-installed
|
|
# new code.
|
|
uninstall:
|
|
@echo ">>> Removing EVERY trace of $(PACKAGE) (all prior versions)"
|
|
# Try to remove pacman-tracked package first (if installed that way)
|
|
-@if command -v pacman >/dev/null 2>&1; then \
|
|
if pacman -Q sysdeck >/dev/null 2>&1; then \
|
|
echo " removing pacman package 'sysdeck'"; \
|
|
pacman -R --noconfirm sysdeck 2>/dev/null || true; \
|
|
fi; \
|
|
fi
|
|
# Old single-plugin layout (v0.0.9-v0.0.19): /usr/share/cockpit/sysdeck/
|
|
@if [ -d "$(DESTDIR)/usr/share/cockpit/sysdeck" ]; then \
|
|
echo " removing old single-plugin /usr/share/cockpit/sysdeck/ (v0.0.9-v0.0.19 layout)"; \
|
|
rm -rf "$(DESTDIR)/usr/share/cockpit/sysdeck"; \
|
|
fi
|
|
# New multi-plugin layout (v0.0.20+): /usr/share/cockpit/sysdeck-*
|
|
@for dir in $(DESTDIR)/usr/share/cockpit/sysdeck-*; do \
|
|
[ -d "$$dir" ] || continue; \
|
|
echo " removing $$(basename $$dir)"; \
|
|
rm -rf "$$dir"; \
|
|
done
|
|
# Python bridge helpers (all versions)
|
|
rm -rf "$(LIB_DIR)"
|
|
# Diagnostic + smoke-test scripts + firewall templates (v0.0.19+)
|
|
# v0.0.31: firewall/templates/*.sh live under here too.
|
|
rm -rf "$(DESTDIR)/usr/share/$(PACKAGE)"
|
|
# Documentation (v0.0.20+)
|
|
rm -rf "$(DESTDIR)/usr/share/doc/$(PACKAGE)"
|
|
# AppStream metainfo (v0.0.17+)
|
|
rm -f "$(DESTDIR)/usr/share/metainfo/sysdeck.metainfo.xml"
|
|
# PolKit policy (v0.0.17+)
|
|
rm -f "$(DESTDIR)/usr/share/polkit-1/actions/org.sysdeck.policy"
|
|
rm -f "$(DESTDIR)/usr/share/polkit-1/actions/org.sysdeck.modules3p.policy"
|
|
# Python site-packages symlink (all versions)
|
|
@SITE_PACKAGES=$$(python3 -c "import site; print(site.getsitepackages()[0])" 2>/dev/null); \
|
|
if [ -n "$$SITE_PACKAGES" ] && [ -L "$(DESTDIR)$$SITE_PACKAGES/$(PACKAGE)" ]; then \
|
|
echo " removing python site-packages symlink"; \
|
|
rm -f "$(DESTDIR)$$SITE_PACKAGES/$(PACKAGE)"; \
|
|
fi
|
|
# Reload polkit + refresh AppStream cache
|
|
-@if command -v systemctl >/dev/null 2>&1; then \
|
|
systemctl reload polkit 2>/dev/null || true; \
|
|
fi
|
|
-@if command -v appstreamcli >/dev/null 2>&1; then \
|
|
appstreamcli refresh-cache 2>/dev/null || true; \
|
|
fi
|
|
@echo ">>> Done. Verify with: ls /usr/share/cockpit/ | grep sysdeck"
|
|
@echo ">>> (should print nothing)"
|
|
@echo ">>> Then restart cockpit.socket to flush the sidebar:"
|
|
@echo " sudo systemctl restart cockpit.socket"
|
|
|
|
# ─── check: build-time guards ────────────────────────────────────────
|
|
check-metainfo-consistency:
|
|
@echo ">>> Checking packaging/sysdeck.metainfo.xml structural consistency"
|
|
@python3 tests/check_metainfo_consistency.py
|
|
|
|
# check-manifest-consistency: validate EVERY plugin's manifest against
|
|
# the working cockpit-podman reference pattern. One bad manifest = the
|
|
# plugin silently disappears from the sidebar. Catches:
|
|
# - non-zero `version` field
|
|
# - any top-level field not in cockpit-podman's manifest
|
|
# - a `content` section (real plugins don't declare one)
|
|
# - menu keys other than `index` (the magic key)
|
|
# - `path` field inside menu entries
|
|
check-manifest-consistency:
|
|
@echo ">>> Checking all 27 plugin manifests against cockpit-podman reference"
|
|
@python3 tests/check_manifest_consistency.py
|
|
|
|
check-makefile-recipes:
|
|
@echo ">>> Checking Makefile recipe indentation (must be tabs, not spaces)"
|
|
@if awk '/^[ \t]+[@a-zA-Z#]/{if ($$0 !~ /^\t/) exit 1}' Makefile; then \
|
|
echo " OK: all recipe lines use tabs"; \
|
|
else \
|
|
echo "FAIL: Makefile has recipe lines indented with spaces instead of tabs."; \
|
|
echo " Run: perl -i -pe 's{^( {8})+}{ \"\\t\" x (length(\$$&)/8) }e' Makefile"; \
|
|
exit 1; \
|
|
fi
|
|
|
|
# check-no-broken-cockpit-import: scan every JS file shipped to plugins/ and
|
|
# shared/ for `import cockpit from "..."` — that pattern is broken because
|
|
# pkg/base1/cockpit.js is a UMD/IIFE that sets window.cockpit as a global,
|
|
# NOT an ES module. The broken import returns undefined, cockpit.spawn()
|
|
# throws when mount() runs, and the plugin page stays on "Loading…".
|
|
# This was the v0.0.27 root cause of "every plugin stuck on Loading".
|
|
# Pattern verified from cockpit's build.js:71-83 (esbuild plugin rewrites
|
|
# `import cockpit from "cockpit"` to `module.exports = cockpit`).
|
|
check-no-broken-cockpit-import:
|
|
@echo ">>> Checking no JS file uses broken \`import cockpit from\` pattern"
|
|
@hits=$$(grep -rEn '^[[:space:]]*import\s+cockpit\s+from' shared/ plugins/ 2>/dev/null); \
|
|
if [ -n "$$hits" ]; then \
|
|
echo "FAIL: found 'import cockpit from' in JS files — cockpit.js is NOT an ES module;"; \
|
|
echo " use 'const cockpit = window.cockpit' instead. Affected files:"; \
|
|
echo "$$hits" | sed 's/^/ /'; \
|
|
exit 1; \
|
|
fi
|
|
echo " OK: no JS file uses the broken import-cockpit pattern"
|
|
|
|
# check-bridge-subcommands: cross-check every `bridgeCmd("<module>", ["<sub>"] )`
|
|
# call in shared/bridge.js against the COMMANDS dict declared in
|
|
# bridge/<module>.py. Catches the v0.0.27 bug class where the JS
|
|
# bridge surface references a subcommand the Python helper doesn't
|
|
# implement — every call to that bridge method returns
|
|
# "Unknown subcommand: X" and the plugin page crashes.
|
|
# Examples this guard would have caught in v0.0.27:
|
|
# - bridge.firmware.devices() calling `python3 firmware.py devices`
|
|
# (firmware.py only had `summary`)
|
|
# - bridge.benchmark.runTest(name) calling `python3 benchmark.py run-test`
|
|
# (benchmark.py had no `run-test` subcommand)
|
|
check-bridge-subcommands:
|
|
@echo ">>> Cross-checking bridge.js calls vs Python COMMANDS dicts"
|
|
@python3 tests/check_bridge_subcommands.py
|
|
|
|
# check-no-broken-python-module: verify bridge.js does NOT call
|
|
# `python3 -m sysdeck.bridge.X` — that pattern requires a Python package
|
|
# layout (sysdeck/bridge/X.py) that doesn't exist in our install.
|
|
# v0.0.27 bug: every bridge helper call returned ModuleNotFoundError
|
|
# "No module named 'sysdeck.bridge'" because the actual layout is
|
|
# /usr/lib/sysdeck/bridge/X.py (flat files, not a nested package).
|
|
# v0.0.27 fix: call helpers by absolute path:
|
|
# python3 /usr/lib/sysdeck/bridge/X.py <args>
|
|
check-no-broken-python-module:
|
|
@echo ">>> Checking no JS file uses broken \`python3 -m sysdeck.bridge\` pattern"
|
|
@hits=$$(grep -rEn '"python3".*"-m".*"sysdeck\.bridge' shared/ plugins/ 2>/dev/null); \
|
|
if [ -n "$$hits" ]; then \
|
|
echo "FAIL: found 'python3 -m sysdeck.bridge' in JS files — this pattern"; \
|
|
echo " requires a Python package layout (sysdeck/bridge/X.py) that doesn't"; \
|
|
echo " exist. Use 'python3 /usr/lib/sysdeck/bridge/X.py' (absolute path)."; \
|
|
echo " Affected files:"; \
|
|
echo "$$hits" | sed 's/^/ /'; \
|
|
exit 1; \
|
|
fi
|
|
echo " OK: no JS file uses the broken python3 -m sysdeck.bridge pattern"
|
|
|
|
# check-release-tree: supply-chain gate from docs/SECURITY-HARDENING.md —
|
|
# a release tarball builds from a clean tree, never from a working copy
|
|
# with uncommitted edits (the 2019 Webmin build-host compromise class).
|
|
# Steps down by environment: git tree → git status must be clean;
|
|
# plain tarball tree → skip (nothing to verify against).
|
|
check-release-tree:
|
|
@if [ -d .git ]; then \
|
|
if [ -n "$$(git status --porcelain 2>/dev/null)" ]; then \
|
|
echo "FAIL: working tree has uncommitted changes — commit or stash before building a release."; \
|
|
git status --porcelain | sed 's/^/ /'; \
|
|
exit 1; \
|
|
fi; \
|
|
echo " OK: git working tree is clean"; \
|
|
else \
|
|
echo " OK: not a git tree (tarball build) — nothing to verify"; \
|
|
fi
|
|
|
|
check-version-sync:
|
|
@echo ">>> Checking version consistency across release surfaces"
|
|
@v=$(VERSION); \
|
|
for f in \
|
|
bridge/__init__.py \
|
|
packaging/setup.py \
|
|
packaging/PKGBUILD \
|
|
packaging/sysdeck.spec \
|
|
packaging/debian/changelog \
|
|
compat/compat-manifest.json \
|
|
; do \
|
|
if ! grep -q "$$v" "$$f" 2>/dev/null; then \
|
|
echo "FAIL: $$f does not reference version $$v"; \
|
|
exit 1; \
|
|
fi; \
|
|
done; \
|
|
echo " OK: all release surfaces report v$$v"
|
|
|
|
check: check-metainfo-consistency check-manifest-consistency check-makefile-recipes check-no-broken-cockpit-import check-no-broken-python-module check-bridge-subcommands check-version-sync check-release-tree
|
|
@echo ">>> Syntax-checking Python sources"
|
|
@python3 -m py_compile bridge/*.py bridge/modules/*.py
|
|
@echo ">>> Syntax-checking JS sources (node --check)"
|
|
@for f in shared/bridge.js plugins/*/*.js; do \
|
|
node --check $$f || exit 1; \
|
|
done
|
|
@echo ">>> Validating all manifest.json files"
|
|
@for f in plugins/*/manifest.json; do \
|
|
python3 -c "import json; json.load(open('$$f'))" || { echo "FAIL: $$f is not valid JSON"; exit 1; }; \
|
|
done
|
|
@echo ">>> Syntax-checking shell scripts"
|
|
@for f in $(DIAGNOSE_SCRIPT) $(SMOKE_TEST_SCRIPT); do \
|
|
bash -n $$f || { echo "FAIL: $$f has syntax errors"; exit 1; }; \
|
|
done
|
|
@echo ">>> Running bridge parser unit tests"
|
|
@PYTHONPATH=bridge python3 -m unittest tests/test_bridge_parsers.py -v
|
|
@echo ">>> All checks passed."
|
|
|
|
clean:
|
|
rm -rf build dist *.tar.bz2
|
|
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
|
|
|
|
dist: check
|
|
@echo ">>> Building $(PACKAGE)-$(VERSION).tar.bz2"
|
|
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
|
|
LC_ALL=C tar cjf $(PACKAGE)-$(VERSION).tar.bz2 \
|
|
--sort=name \
|
|
--mtime="@$(SOURCE_DATE_EPOCH)" \
|
|
--owner=0 --group=0 --numeric-owner \
|
|
--exclude='__pycache__' \
|
|
--exclude='*.pyc' \
|
|
--exclude='*.tar.bz2' \
|
|
--transform 's,^,$(PACKAGE)-$(VERSION)/,' \
|
|
plugins shared bridge tests packaging compat standalone-plugins \
|
|
prometheus scripts firewall \
|
|
Makefile README.md QUICKSTART.md BLOG.md LICENSE QA.md worklog.md THIRD_PARTY.md docs \
|
|
sysdeck-diagnose.sh cockpit-smoke-test.sh sysdeck-uninstall.sh
|
|
@echo ">>> $(PACKAGE)-$(VERSION).tar.bz2 ready"
|
|
|
|
# SOURCE_DATE_EPOCH: pin the tarball mtime for reproducible builds.
|
|
# Release builds set it explicitly (e.g. `make dist SOURCE_DATE_EPOCH=$(git log -1 --format=%ct)`);
|
|
# unpacked trees without git fall back to a fixed epoch.
|
|
SOURCE_DATE_EPOCH ?= 0
|
|
|
|
# distcheck: verify the tarball extracts into <package>-<version>/ and
|
|
# passes `make check` from inside the extracted tree.
|
|
distcheck: dist
|
|
@echo ">>> Distcheck: extracting $(PACKAGE)-$(VERSION).tar.bz2"
|
|
DISTCHECK_DIR=$$(mktemp -d /tmp/sysdeck-distcheck.XXXXXX)
|
|
tar xjf $(PACKAGE)-$(VERSION).tar.bz2 -C $$DISTCHECK_DIR
|
|
@if [ ! -d $$DISTCHECK_DIR/$(PACKAGE)-$(VERSION) ]; then \
|
|
echo "FAIL: tarball did not extract into $(PACKAGE)-$(VERSION)/"; \
|
|
rm -rf $$DISTCHECK_DIR; \
|
|
exit 1; \
|
|
fi
|
|
@echo ">>> Distcheck: running make check inside extracted tree"
|
|
@(cd $$DISTCHECK_DIR/$(PACKAGE)-$(VERSION) && make check)
|
|
@rm -rf $$DISTCHECK_DIR
|
|
@echo ">>> Distcheck passed: tarball is self-sufficient and structurally correct."
|
|
|
|
# ─── v0.3.0 master edition: web + fester + klanker-gate ─────────────────────────────
|
|
# Run these from an extracted master tarball (where web/ sits alongside
|
|
# this Makefile) or the canonical dev tree with web/ present.
|
|
|
|
FESTER_DIR := web/mini-services/fester
|
|
FESTER_URL ?= http://127.0.0.1:3010
|
|
export FESTER_URL
|
|
|
|
fester-start:
|
|
@echo ">>> Starting the vendored fester service on :3010 (Ctrl+C stops it)"
|
|
cd $(FESTER_DIR) && bun install && bun run dev
|
|
|
|
web-install:
|
|
@echo ">>> Installing the SysDeck Web Edition (bun + prisma)"
|
|
cd web && bun install && bun run db:push
|
|
cd $(FESTER_DIR) && bun install
|
|
|
|
web-dev: web-install
|
|
@echo ">>> Starting fester in the background (log: /tmp/fester.log)"
|
|
cd $(FESTER_DIR) && nohup bun run dev >/tmp/fester.log 2>&1 &
|
|
@echo ">>> Starting SysDeck Web Edition on :3000 (Ctrl+C stops next; fester keeps running)"
|
|
cd web && bun run dev
|
|
|
|
# ─── branding: theme the Cockpit SHELL chrome to the web-edition look ────
|
|
# /usr/share/cockpit/branding.css is Cockpit's documented override point
|
|
# for the shell (sidebar, header, login). shared/branding.css ports the
|
|
# web edition 0.3.0 midnight/teal design onto it. Any pre-existing
|
|
# branding.css (shipped by the distro) is backed up first and restored
|
|
# by `make uninstall-branding`.
|
|
install-branding:
|
|
@echo ">>> Theming the Cockpit shell to the web-edition look"
|
|
-@if test -f $(DESTDIR)/usr/share/cockpit/branding.css; then \
|
|
cp -a $(DESTDIR)/usr/share/cockpit/branding.css $(DESTDIR)/usr/share/cockpit/branding.css.sysdeck-bak; \
|
|
echo " existing branding.css backed up (branding.css.sysdeck-bak)"; \
|
|
fi
|
|
install -d $(DESTDIR)/usr/share/cockpit
|
|
install -m 0644 shared/branding.css $(DESTDIR)/usr/share/cockpit/branding.css
|
|
@echo " installed /usr/share/cockpit/branding.css"
|
|
@echo " reload the Cockpit page (hard refresh) to see the shell skin"
|
|
|
|
uninstall-branding:
|
|
@echo ">>> Restoring the Cockpit shell branding"
|
|
rm -f $(DESTDIR)/usr/share/cockpit/branding.css
|
|
-@if test -f $(DESTDIR)/usr/share/cockpit/branding.css.sysdeck-bak; then \
|
|
mv $(DESTDIR)/usr/share/cockpit/branding.css.sysdeck-bak $(DESTDIR)/usr/share/cockpit/branding.css; \
|
|
echo " restored original branding.css from backup"; \
|
|
else \
|
|
echo " no backup found — the distro package owns branding.css"; \
|
|
echo " reinstall the cockpit-bridge package to restore defaults"; \
|
|
fi
|
|
|
|
# master: rebuild the master tarball from this tree (cockpit + web + fester + klanker-gate)
|
|
master:
|
|
@echo ">>> Building $(PACKAGE)-$(VERSION)-master.tar.bz2 (cockpit + web + fester + klanker-gate)"
|
|
tar cjf $(PACKAGE)-$(VERSION)-master.tar.bz2 \
|
|
--exclude='__pycache__' --exclude='*.pyc' --exclude='*.tar.bz2' \
|
|
--exclude='*node_modules*' --exclude='*.next' --exclude='*.tsbuildinfo' \
|
|
--exclude='*public/download*' --exclude='*.db' --exclude='*.db-*' \
|
|
--exclude='dev.log' --exclude='server.log' --exclude='*.log' --exclude='.env' \
|
|
--transform 's,^,$(PACKAGE)-$(VERSION)-master/,' \
|
|
bridge plugins shared tests packaging compat standalone-plugins \
|
|
prometheus scripts firewall docs web klanker-gate \
|
|
Makefile README.md QUICKSTART.md BLOG.md LICENSE QA.md worklog.md THIRD_PARTY.md \
|
|
sysdeck-diagnose.sh cockpit-smoke-test.sh sysdeck-uninstall.sh
|
|
@echo ">>> $(PACKAGE)-$(VERSION)-master.tar.bz2 ready"
|