-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMakefile
More file actions
186 lines (165 loc) · 9.17 KB
/
Copy pathMakefile
File metadata and controls
186 lines (165 loc) · 9.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
PREFIX ?= ~/.local/bin
# man finds ~/.local/share/man automatically when ~/.local/bin is on PATH.
MANDIR ?= ~/.local/share/man/man1
# bash-completion's dynamic loader searches $XDG_DATA_HOME (user) and
# $XDG_DATA_DIRS (which includes /usr/local/share) for
# bash-completion/completions/<command>. zsh needs the directory on $fpath;
# fish picks up ~/.config/fish/completions on its own.
COMPDIR ?= ~/.local/share/bash-completion/completions
ZSHCOMPDIR ?= ~/.local/share/zsh/site-functions
FISHCOMPDIR ?= ~/.config/fish/completions
# Claude Code assets (skills + settings snippet) ship beside the binary so
# that `sinteractive claude install` works from an installed copy and not
# only from a git checkout. Mirrors the repo layout, so the lookup in the
# binary has one code path for both.
SHAREDIR ?= ~/.local/share/sinteractive
# The binary. zellij is compiled into it, so it is the only program the
# compute nodes ever run: the batch job execs it from wherever it is
# installed, which therefore has to be a filesystem the nodes can see. It
# links against glibc, so build it on the oldest glibc it must run on (Rocky
# 8 on Alpine); one such build runs on every node. FEATURES adds cargo
# features (web_server_capability for the zellij web client).
#
# cargo honours CARGO_TARGET_DIR, and on Alpine the build lives on /scratch
# rather than in the checkout, so the binary's location has to follow the
# same variable or `make install` looks in a target/ that was never written.
# TARGET_DIR=... overrides both.
TARGET_DIR ?= $(or $(CARGO_TARGET_DIR),target)
BIN := $(TARGET_DIR)/release/sinteractive
FEATURES ?= web_server_capability
# The binary is never written over in place. Every running session is this
# binary executing on a compute node, and on an NFS home (Alpine) unlinking
# or renaming over a file another node is executing kills that process with
# SIGBUS the next time it faults a page in. scripts/install-bin.sh puts each
# build at <bindir>/.sinteractive-<sha> and swaps a symlink, pruning only
# builds no queued or running job can be using.
INSTALL_BIN := bash scripts/install-bin.sh
# Every skill under skills/ ships, discovered rather than listed: adding one
# is a matter of dropping a directory in, with no install target to update.
# All .md files go, not just SKILL.md — the hpc-* skills keep per-cluster
# halves in alpine.md/bodhi.md beside it. The paths are relative and mirror
# the repo layout, so `install -D` into a destination root reproduces
# skills/<name>/*.md underneath it.
SKILLS := $(wildcard skills/*/*.md)
# When run as root, install system-wide: sinteractive to /usr/local/bin and
# its man page to /usr/local/share/man.
UID := $(shell id -u)
.PHONY: build man completions install install-user install-system claude-install skill-install
build:
CARGO_TARGET_DIR=$(TARGET_DIR) cargo build --release -p sint --features "$(FEATURES)"
# The man page and shell completions are generated by the binary itself, so
# they never drift from the clap definitions. Both need `make build` first;
# the generated files are committed so a checkout carries them.
man: $(BIN)
$(BIN) gen man > man/sinteractive.1
completions: $(BIN)
$(BIN) gen completions bash > completions/sinteractive.bash
$(BIN) gen completions zsh > completions/sinteractive.zsh
$(BIN) gen completions fish > completions/sinteractive.fish
$(BIN):
@echo "error: $(BIN) is not built; run 'make build' first" >&2; exit 1
ifeq ($(UID),0)
install: install-system
else
install: install-user
endif
# The share tree: skills and the settings snippet. `claude install` probes
# beside skills/.
define install_share
install -m 0644 claude/settings-snippet.json $(1)/claude/
for s in $(SKILLS); do install -D -m 0644 $$s $(1)/$$s || exit 1; done
endef
install-user: $(BIN) man completions
mkdir -p $(PREFIX)
$(INSTALL_BIN) $(BIN) $(PREFIX)
mkdir -p $(MANDIR)
cp man/sinteractive.1 $(MANDIR)/sinteractive.1
mkdir -p $(COMPDIR) $(ZSHCOMPDIR) $(FISHCOMPDIR)
cp completions/sinteractive.bash $(COMPDIR)/sinteractive
cp completions/sinteractive.zsh $(ZSHCOMPDIR)/_sinteractive
cp completions/sinteractive.fish $(FISHCOMPDIR)/sinteractive.fish
$(call install_share,$(SHAREDIR))
install-system: $(BIN) man completions
$(INSTALL_BIN) $(BIN) /usr/local/bin
install -D -m 0644 man/sinteractive.1 /usr/local/share/man/man1/sinteractive.1
install -D -m 0644 completions/sinteractive.bash /usr/local/share/bash-completion/completions/sinteractive
install -D -m 0644 completions/sinteractive.zsh /usr/local/share/zsh/site-functions/_sinteractive
install -D -m 0644 completions/sinteractive.fish /usr/local/share/fish/vendor_completions.d/sinteractive.fish
$(call install_share,/usr/local/share/sinteractive)
# ---------------------------------------------------------------------------
# Claude Code integration. Skills, which teach an agent how work is done here
# (loaded on demand from their descriptions), plus the hooks, statusline and
# MCP server, which are all subcommands of the binary and only need
# registering in the user's settings. All per-user, so this installs into
# ~/.claude regardless of UID. The merge logic lives in the binary
# (`sinteractive claude install`) rather than being mirrored here.
# ---------------------------------------------------------------------------
CLAUDE_DIR ?= $(HOME)/.claude
claude-install: $(BIN)
@CLAUDE_CONFIG_DIR=$(CLAUDE_DIR) SINTERACTIVE_SHARE=$(CURDIR) $(BIN) claude install
# Back-compat alias: this target used to install only the skill.
skill-install: claude-install
# ---------------------------------------------------------------------------
# nodes — install sinteractive onto every compute node: the same set of files
# as install-system.
#
# Only needed when /usr/local is node-local and the binary is installed
# there. A binary on a shared filesystem (a shared /home, /projects, a
# cluster-wide /opt) needs nothing on the nodes: the batch job execs it from
# where it is, and the extracted bundle lives under the (shared) cache dir.
#
# The share tree matters on the nodes and not only on the head node, because
# claude install resolves it relative to the running binary: someone who runs
# it from inside a session is running the node's /usr/local/bin copy.
#
# If this checkout lives on a cluster-wide mount, with pdsh each node can
# `install` straight from the shared path — note pdsh runs commands, it does
# NOT copy files (that's pdcp). Without pdsh, falls back to piping a tar to
# each node in turn. Run as root (sudo make nodes); this covers the compute
# nodes, the head node gets make install-system.
#
# The binary is renamed into place rather than written over: a copy of it is
# executing on the node right now for every running session (it is the
# zellij server), and overwriting a running executable is "text file busy".
# ---------------------------------------------------------------------------
NODES ?= $(shell sinfo -hN -o '%N' 2>/dev/null | sort -u)
NODELIST = $(shell echo $(NODES) | tr ' ' ',')
SSH_USER ?= root
# pdsh's built-in default rcmd module is rsh, and nothing listens on 514 here,
# so an unqualified pdsh answers with "connect: Connection refused" for every
# node. A PDSH_RCMD_TYPE=ssh in the admin's own environment does not save this
# target either: sudo resets the environment, so the value never arrives. Ask
# for the module by name instead. Override if a cluster wants another one
# (`make nodes PDSH_RCMD=exec`); `pdsh -V` lists what is compiled in.
PDSH_RCMD ?= ssh
.PHONY: nodes require-root
require-root:
@test "$(UID)" = "0" || { echo "error: this target must be run as root"; exit 1; }
nodes: require-root $(BIN)
@if command -v pdsh >/dev/null 2>&1; then \
pdsh -R $(PDSH_RCMD) -w $(NODELIST) \
'install -m 0755 $(abspath $(BIN)) /usr/local/bin/sinteractive.new \
&& mv /usr/local/bin/sinteractive.new /usr/local/bin/sinteractive \
&& install -D -m 0644 $(CURDIR)/man/sinteractive.1 /usr/local/share/man/man1/sinteractive.1 \
&& install -D -m 0644 $(CURDIR)/completions/sinteractive.bash /usr/local/share/bash-completion/completions/sinteractive \
&& install -m 0644 $(CURDIR)/claude/settings-snippet.json /usr/local/share/sinteractive/claude/ \
&& for s in $(SKILLS); do install -D -m 0644 $(CURDIR)/$$s /usr/local/share/sinteractive/$$s || exit 1; done \
&& echo ok'; \
else \
for n in $(NODES); do \
printf '==> %s: ' "$$n"; \
tar cf - man/sinteractive.1 completions/sinteractive.bash \
claude/settings-snippet.json $(SKILLS) \
-C $(abspath $(dir $(BIN))) sinteractive \
| ssh $(SSH_USER)@$$n \
'set -e; d=$$(mktemp -d); trap "rm -rf $$d" EXIT; tar xf - -C "$$d"; \
install -m 0755 "$$d/sinteractive" /usr/local/bin/sinteractive.new; \
mv /usr/local/bin/sinteractive.new /usr/local/bin/sinteractive; \
install -D -m 0644 "$$d/man/sinteractive.1" /usr/local/share/man/man1/sinteractive.1; \
install -D -m 0644 "$$d/completions/sinteractive.bash" /usr/local/share/bash-completion/completions/sinteractive; \
install -m 0644 "$$d/claude/settings-snippet.json" /usr/local/share/sinteractive/claude/; \
cd "$$d" && for s in skills/*/*.md; do install -D -m 0644 "$$s" "/usr/local/share/sinteractive/$$s"; done; \
echo ok' \
|| echo "FAILED"; \
done; \
fi