tmux observer
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Collapse systemd unit to single template; add make deploy/upgrade

Make `make` the single entry point for both dev (install/test/ci) and deployment (deploy/upgrade/service-*). Extract the systemd unit into one template shipped inside the wheel as a package resource, consumed by both `make deploy` and `solstone-tmux install-service`. Make install-service idempotent via byte-exact unit comparison, with `--force` as the explicit rewrite escape hatch. Collapse INSTALL.md to three numbered steps plus an updating section.

No observer behavior changes; packaging and docs only.

Co-Authored-By: OpenAI Codex <codex@openai.com>

+94 -62
+24 -39
INSTALL.md
··· 17 17 18 18 if it's already active and connected, you're done. 19 19 20 - ## what to sort out together 21 - 22 - - **stream name.** this identifies the capture source. the convention is `hostname.tmux` (e.g., `fedora.tmux`). 23 - 24 - ## install sequence 20 + ## install 25 21 26 - 1. if not already cloned, clone into solstone's observers directory and install with pipx: 22 + 1. **clone and deploy.** 27 23 ``` 28 - cd "$(sol root)/observers" 29 - git clone https://github.com/solpbc/solstone-tmux.git 24 + git clone https://github.com/solpbc/solstone-tmux.git solstone-tmux 30 25 cd solstone-tmux 31 - pipx install . 26 + make deploy 32 27 ``` 28 + this installs the `solstone-tmux` command via pipx and sets up the systemd user unit. 33 29 34 - 2. register the observer with solstone and save the API key: 30 + 2. **run setup.** 35 31 ``` 36 - sol observer create solstone-tmux 32 + solstone-tmux setup 37 33 ``` 34 + this prompts for your solstone server URL, registers the observer, and writes the config file. 38 35 39 - 3. write the config to `~/.local/share/solstone-tmux/config/config.json`: 40 - ```json 41 - { 42 - "server_url": "http://localhost:5015", 43 - "key": "THE_API_KEY_FROM_STEP_2", 44 - "stream": "HOSTNAME.tmux" 45 - } 36 + 3. **verify.** 46 37 ``` 38 + solstone-tmux status 39 + systemctl --user status solstone-tmux 40 + ``` 41 + 42 + ## updating after a code change 47 43 48 - **optional: cache retention.** by default, synced segments are deleted after 7 days. to change this, add `cache_retention_days` to config.json: 49 - - positive number: keep synced segments for that many days (default: `7`) 50 - - `0`: delete immediately after confirmed sync 51 - - `-1`: keep forever (never auto-delete) 44 + ``` 45 + git pull && make upgrade 46 + ``` 52 47 53 - ```json 54 - { 55 - "server_url": "http://localhost:5015", 56 - "key": "THE_API_KEY_FROM_STEP_2", 57 - "stream": "HOSTNAME.tmux", 58 - "cache_retention_days": 7 59 - } 60 - ``` 48 + `make upgrade` runs `make ci` first; if tests fail, the upgrade aborts before touching the installed service. 49 + 50 + ## optional cache retention 61 51 62 - 4. install and start the systemd user service: 63 - ``` 64 - solstone-tmux install-service 65 - ``` 52 + by default, synced segments are deleted after 7 days. to change this, add `cache_retention_days` to config.json: 66 53 67 - 5. verify it's running and connected: 68 - ``` 69 - systemctl --user status solstone-tmux 70 - sol observer list 71 - ``` 54 + - positive number: keep synced segments for that many days (default: `7`) 55 + - `0`: delete immediately after confirmed sync 56 + - `-1`: keep forever (never auto-delete) 72 57 73 58 ## status bar indicator 74 59
+41 -1
Makefile
··· 1 1 # solstone-tmux Makefile 2 2 # Standalone tmux terminal observer for solstone 3 3 4 - .PHONY: install test test-only format ci clean clean-install uninstall 4 + .PHONY: install test test-only format ci clean clean-install uninstall deploy upgrade service-restart service-status service-logs uninstall-service 5 + 6 + # Service deployment 7 + APP := solstone-tmux 8 + PIPX_FLAGS := 9 + UNIT := solstone-tmux.service 5 10 6 11 # Default target 7 12 all: install ··· 70 75 @$(MAKE) test 71 76 @echo "" 72 77 @echo "All CI checks passed!" 78 + 79 + deploy: 80 + @command -v pipx >/dev/null 2>&1 || { echo "pipx not found. Install with: sudo dnf install pipx (or apt install pipx)"; exit 1; } 81 + @echo "==> Installing $(APP) with pipx" 82 + pipx install --force $(PIPX_FLAGS) . 83 + # Never use editable pipx installs here — they couple the running service 84 + # to the working tree; `git checkout` silently downgrades the deployed version. 85 + @echo "==> Installing systemd user unit" 86 + $(APP) install-service 87 + @echo "==> Service status" 88 + systemctl --user --no-pager status $(UNIT) | head 89 + 90 + upgrade: ci 91 + @command -v pipx >/dev/null 2>&1 || { echo "pipx not found. Install with: sudo dnf install pipx (or apt install pipx)"; exit 1; } 92 + @echo "==> Upgrading $(APP) with pipx" 93 + pipx install --force $(PIPX_FLAGS) . 94 + @echo "==> Reloading systemd and restarting $(UNIT)" 95 + systemctl --user daemon-reload 96 + systemctl --user restart $(UNIT) 97 + systemctl --user --no-pager status $(UNIT) | head 98 + 99 + service-restart: 100 + systemctl --user restart $(UNIT) 101 + 102 + service-status: 103 + systemctl --user --no-pager status $(UNIT) 104 + 105 + service-logs: 106 + journalctl --user -u $(APP) -n 100 --no-pager -f 107 + 108 + uninstall-service: 109 + -systemctl --user disable --now $(UNIT) 110 + -rm -f $$HOME/.config/systemd/user/$(UNIT) 111 + -systemctl --user daemon-reload 112 + -pipx uninstall $(APP) 73 113 74 114 # Clean build artifacts and caches 75 115 clean:
+2 -2
contrib/solstone-tmux.service src/solstone_tmux/contrib/solstone-tmux.service.in
··· 4 4 5 5 [Service] 6 6 Type=simple 7 - # Environment=PATH=... (injected at install time by solstone-tmux install-service) 8 - ExecStart=%h/.local/bin/solstone-tmux run 7 + Environment=PATH={PATH} 8 + ExecStart={BINARY} run 9 9 Restart=on-failure 10 10 RestartSec=5 11 11 StartLimitIntervalSec=300
+3
pyproject.toml
··· 22 22 [build-system] 23 23 requires = ["hatchling"] 24 24 build-backend = "hatchling.build" 25 + 26 + [tool.hatch.build.targets.wheel] 27 + packages = ["src/solstone_tmux"]
+23 -19
src/solstone_tmux/cli.py
··· 21 21 import socket 22 22 import subprocess 23 23 import sys 24 + from importlib import resources 24 25 from pathlib import Path 25 26 26 27 from .config import load_config, save_config ··· 152 153 unit_dir.mkdir(parents=True, exist_ok=True) 153 154 unit_path = unit_dir / "solstone-tmux.service" 154 155 155 - unit_content = f"""\ 156 - [Unit] 157 - Description=Solstone Tmux Terminal Observer 158 - After=basic.target 159 - 160 - [Service] 161 - Type=simple 162 - Environment=PATH={service_path} 163 - ExecStart={binary} run 164 - Restart=on-failure 165 - RestartSec=5 166 - StartLimitIntervalSec=300 167 - StartLimitBurst=5 168 - 169 - [Install] 170 - WantedBy=default.target 171 - """ 156 + template = ( 157 + resources.files("solstone_tmux") 158 + .joinpath("contrib/solstone-tmux.service.in") 159 + .read_text() 160 + ) 161 + unit_content = template.replace("{BINARY}", binary).replace("{PATH}", service_path) 162 + unit_bytes = unit_content.encode() 163 + if unit_path.exists() and unit_path.read_bytes() == unit_bytes and not args.force: 164 + print("Unit unchanged; nothing to do") 165 + return 0 172 166 173 - unit_path.write_text(unit_content) 167 + unit_path.write_bytes(unit_bytes) 174 168 print(f"Wrote {unit_path}") 175 169 176 170 # Reload, enable, start ··· 180 174 ["systemctl", "--user", "enable", "--now", "solstone-tmux.service"], 181 175 check=True, 182 176 ) 177 + if args.force: 178 + subprocess.run( 179 + ["systemctl", "--user", "restart", "solstone-tmux.service"], 180 + check=True, 181 + ) 183 182 print("Service enabled and started.") 184 183 subprocess.run( 185 184 ["systemctl", "--user", "status", "solstone-tmux.service"], ··· 295 294 subparsers.add_parser("setup", help="Interactive configuration") 296 295 297 296 # install-service 298 - subparsers.add_parser("install-service", help="Install systemd user service") 297 + install_parser = subparsers.add_parser( 298 + "install-service", help="Install systemd user service" 299 + ) 300 + install_parser.add_argument( 301 + "--force", action="store_true", help="Always rewrite unit and restart service." 302 + ) 299 303 300 304 # status 301 305 subparsers.add_parser("status", help="Show capture and sync state")
+1 -1
tests/test_cli.py
··· 25 25 monkeypatch.delenv("PATH", raising=False) 26 26 27 27 with patch("solstone_tmux.cli.subprocess.run"): 28 - cmd_install_service(argparse.Namespace()) 28 + cmd_install_service(argparse.Namespace(force=False)) 29 29 30 30 unit_path = tmp_path / ".config" / "systemd" / "user" / "solstone-tmux.service" 31 31 return unit_path.read_text()