feat: daemon mode with named socket, scripts/run for IP:PORT
This commit is contained in:
@@ -17,16 +17,18 @@ simple (one language, two packages) and is adequate for Spine's complexity level
|
|||||||
|---------|---------|--------|---------|
|
|---------|---------|--------|---------|
|
||||||
| `simple-httpd` | MELPA latest | MELPA | HTTP server, `defservlet` routing |
|
| `simple-httpd` | MELPA latest | MELPA | HTTP server, `defservlet` routing |
|
||||||
| `mustache` | MELPA latest | MELPA | Mustache template rendering |
|
| `mustache` | MELPA latest | MELPA | Mustache template rendering |
|
||||||
| `ht` | — | transitive via `mustache` | Hash table for template context |
|
| `ht` | MELPA latest | MELPA | Hash table for template context |
|
||||||
|
|
||||||
## Project structure
|
## Project structure
|
||||||
|
|
||||||
```
|
```
|
||||||
spine/
|
spine/
|
||||||
├── spine.el # entry point: bootstrap, server start, handlers
|
├── scripts/
|
||||||
|
│ └── run # start the server as a daemon
|
||||||
|
├── spine.el # entry point: bootstrap, server start, handlers
|
||||||
├── templates/
|
├── templates/
|
||||||
│ └── hello.mustache # smoke-test template
|
│ └── hello.mustache # smoke-test template
|
||||||
├── spine-spec.md # design brief (existing)
|
├── spine-spec.md # design brief (existing)
|
||||||
├── spine-mockup-a-agenda.html
|
├── spine-mockup-a-agenda.html
|
||||||
├── spine-mockup-b-journal.html
|
├── spine-mockup-b-journal.html
|
||||||
└── README.md
|
└── README.md
|
||||||
@@ -45,7 +47,7 @@ packages from MELPA; subsequent starts skip the install step.
|
|||||||
(require 'package)
|
(require 'package)
|
||||||
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
|
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
|
||||||
(package-initialize)
|
(package-initialize)
|
||||||
(dolist (pkg '(simple-httpd mustache))
|
(dolist (pkg '(simple-httpd mustache ht))
|
||||||
(unless (package-installed-p pkg)
|
(unless (package-installed-p pkg)
|
||||||
(package-refresh-contents)
|
(package-refresh-contents)
|
||||||
(package-install pkg)))
|
(package-install pkg)))
|
||||||
@@ -60,16 +62,24 @@ start.
|
|||||||
(defvar spine-port 8080
|
(defvar spine-port 8080
|
||||||
"Port for the Spine HTTP server.")
|
"Port for the Spine HTTP server.")
|
||||||
|
|
||||||
|
(defvar spine-host nil
|
||||||
|
"Host for the Spine HTTP server.
|
||||||
|
nil means localhost only. Set to a string like \"0.0.0.0\" to listen on
|
||||||
|
all interfaces.")
|
||||||
|
|
||||||
(defvar spine-template-dir
|
(defvar spine-template-dir
|
||||||
(expand-file-name "templates/" (file-name-directory load-file-name))
|
(expand-file-name "templates/" (file-name-directory load-file-name))
|
||||||
"Directory containing .mustache templates.")
|
"Directory containing .mustache templates.")
|
||||||
|
|
||||||
|
(setq httpd-host spine-host)
|
||||||
(setq httpd-port spine-port)
|
(setq httpd-port spine-port)
|
||||||
(httpd-start)
|
(httpd-start)
|
||||||
(message "Spine listening on http://localhost:%d" spine-port)
|
(message "Spine listening on http://%s:%d"
|
||||||
|
(or spine-host "localhost") spine-port)
|
||||||
```
|
```
|
||||||
|
|
||||||
- Port defaults to 8080, overridable by setting `spine-port` before loading.
|
- Port defaults to 8080, overridable via `spine-port`.
|
||||||
|
- Host defaults to nil (localhost-only), overridable via `spine-host`.
|
||||||
- `spine-template-dir` resolves relative to `spine.el`'s directory so the app
|
- `spine-template-dir` resolves relative to `spine.el`'s directory so the app
|
||||||
works regardless of `default-directory`.
|
works regardless of `default-directory`.
|
||||||
|
|
||||||
@@ -124,6 +134,23 @@ Returns the rendered string."
|
|||||||
|
|
||||||
## Invocation
|
## Invocation
|
||||||
|
|
||||||
|
Production use — Emacs daemon with a named socket, no GUI frame:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scripts/run [HOST:]PORT
|
||||||
|
```
|
||||||
|
|
||||||
|
- `scripts/run 9090` → `http://localhost:9090/hello`
|
||||||
|
- `scripts/run 0.0.0.0:8080` → `http://0.0.0.0:8080/hello` (all interfaces)
|
||||||
|
- `scripts/run` → `http://localhost:8080/hello`
|
||||||
|
|
||||||
|
Stop: `emacsclient --socket-name=spine --eval '(kill-emacs)'`
|
||||||
|
|
||||||
|
The socket name `spine` is separate from the default Emacs daemon socket,
|
||||||
|
so your personal daemon is unaffected.
|
||||||
|
|
||||||
|
Development use — interactive Emacs in the terminal:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
emacs --quick --load spine.el
|
emacs --quick --load spine.el
|
||||||
```
|
```
|
||||||
@@ -141,5 +168,4 @@ emacs --quick --load spine.el
|
|||||||
|
|
||||||
- Book data, Org file integration, any handler beyond `/hello`.
|
- Book data, Org file integration, any handler beyond `/hello`.
|
||||||
- Static file serving (CSS, JS, icons).
|
- Static file serving (CSS, JS, icons).
|
||||||
- Emacs daemon mode, process management, systemd units.
|
|
||||||
- Tests — the scaffold is small enough to smoke-test manually.
|
- Tests — the scaffold is small enough to smoke-test manually.
|
||||||
|
|||||||
Executable
+37
@@ -0,0 +1,37 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||||
|
|
||||||
|
HOST=""
|
||||||
|
PORT="8080"
|
||||||
|
|
||||||
|
if [ $# -ge 1 ] && [ -n "$1" ]; then
|
||||||
|
ARG="$1"
|
||||||
|
if [[ "$ARG" == *:* ]]; then
|
||||||
|
HOST="${ARG%:*}"
|
||||||
|
PORT="${ARG##*:}"
|
||||||
|
else
|
||||||
|
PORT="$ARG"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Stop any existing spine daemon
|
||||||
|
emacsclient --socket-name=spine --eval '(kill-emacs)' 2>/dev/null || true
|
||||||
|
|
||||||
|
# Build emacs args
|
||||||
|
EMACS_ARGS=(emacs --quick --daemon=spine)
|
||||||
|
|
||||||
|
if [ -n "$HOST" ]; then
|
||||||
|
EMACS_ARGS+=(--eval "(setq spine-host \"$HOST\")")
|
||||||
|
fi
|
||||||
|
|
||||||
|
EMACS_ARGS+=(--eval "(setq spine-port $PORT)")
|
||||||
|
EMACS_ARGS+=(--load "$ROOT_DIR/spine.el")
|
||||||
|
|
||||||
|
"${EMACS_ARGS[@]}"
|
||||||
|
|
||||||
|
DISPLAY_HOST="${HOST:-localhost}"
|
||||||
|
echo "Spine running at http://${DISPLAY_HOST}:${PORT}/hello"
|
||||||
|
echo "Stop: emacsclient --socket-name=spine --eval '(kill-emacs)'"
|
||||||
@@ -21,6 +21,10 @@
|
|||||||
|
|
||||||
(defvar spine-port 8080
|
(defvar spine-port 8080
|
||||||
"Port for the Spine HTTP server.")
|
"Port for the Spine HTTP server.")
|
||||||
|
(defvar spine-host nil
|
||||||
|
"Host for the Spine HTTP server.
|
||||||
|
nil means localhost only. Set to a string like \"0.0.0.0\" to listen on
|
||||||
|
all interfaces.")
|
||||||
|
|
||||||
(defvar spine-template-dir
|
(defvar spine-template-dir
|
||||||
(expand-file-name "templates/"
|
(expand-file-name "templates/"
|
||||||
@@ -47,10 +51,11 @@ Returns the rendered string."
|
|||||||
("message" "Hello from Emacs.")))))
|
("message" "Hello from Emacs.")))))
|
||||||
|
|
||||||
;; --- start ------------------------------------------------------------
|
;; --- start ------------------------------------------------------------
|
||||||
|
(setq httpd-host spine-host)
|
||||||
(setq httpd-port spine-port)
|
(setq httpd-port spine-port)
|
||||||
(httpd-start)
|
(httpd-start)
|
||||||
(message "Spine listening on http://localhost:%d" spine-port)
|
(message "Spine listening on http://%s:%d"
|
||||||
|
(or spine-host "localhost") spine-port)
|
||||||
|
|
||||||
(provide 'spine)
|
(provide 'spine)
|
||||||
;;; spine.el ends here
|
;;; spine.el ends here
|
||||||
|
|||||||
Reference in New Issue
Block a user