aboutsummaryrefslogtreecommitdiffstats
path: root/internal/templates/templates.go
diff options
context:
space:
mode:
authorClawd <ai@clawd.bot>2026-04-18 14:40:17 -0700
committerClawd <ai@clawd.bot>2026-04-18 14:40:17 -0700
commit778bef5ee6941056e06326d1eaaa6956d7307a85 (patch)
tree23b85f32fb69f85078b3debec08c1353694def6f /internal/templates/templates.go
parenteb76b1f6e1697ef170fc45d25e81b21679ea7b0d (diff)
Remove Go implementation — ship is skills-only nowmain
The skills/ directory fully replaces the old Go CLI. Drop all Go source, build files, planning docs, and the stale SECURITY.md (which described the old git-user push-deploy model that no longer exists). Trim .gitignore to match the new tree.
Diffstat (limited to 'internal/templates/templates.go')
-rw-r--r--internal/templates/templates.go358
1 files changed, 0 insertions, 358 deletions
diff --git a/internal/templates/templates.go b/internal/templates/templates.go
deleted file mode 100644
index 2163f47..0000000
--- a/internal/templates/templates.go
+++ /dev/null
@@ -1,358 +0,0 @@
1package templates
2
3import (
4 "bytes"
5 "text/template"
6)
7
8var serviceTemplate = `[Unit]
9Description={{.Name}}
10After=network.target
11
12[Service]
13Type=simple
14User={{.User}}
15WorkingDirectory={{.WorkDir}}
16EnvironmentFile={{.EnvFile}}
17ExecStart={{.BinaryPath}} {{.Args}}
18Restart=always
19RestartSec=5s
20NoNewPrivileges=true
21PrivateTmp=true
22{{- if .Memory}}
23MemoryMax={{.Memory}}
24{{- end}}
25{{- if .CPU}}
26CPUQuota={{.CPU}}
27{{- end}}
28
29[Install]
30WantedBy=multi-user.target
31`
32
33var appCaddyTemplate = `{{.Domain}} {
34 reverse_proxy 127.0.0.1:{{.Port}}
35}
36`
37
38var staticCaddyTemplate = `{{.Domain}} {
39 root * {{.RootDir}}
40 file_server
41 encode gzip
42}
43`
44
45// SystemdService generates a systemd service unit file
46func SystemdService(data map[string]string) (string, error) {
47 tmpl, err := template.New("service").Parse(serviceTemplate)
48 if err != nil {
49 return "", err
50 }
51
52 var buf bytes.Buffer
53 if err := tmpl.Execute(&buf, data); err != nil {
54 return "", err
55 }
56
57 return buf.String(), nil
58}
59
60// AppCaddy generates a Caddy config for a Go app
61func AppCaddy(data map[string]string) (string, error) {
62 tmpl, err := template.New("caddy").Parse(appCaddyTemplate)
63 if err != nil {
64 return "", err
65 }
66
67 var buf bytes.Buffer
68 if err := tmpl.Execute(&buf, data); err != nil {
69 return "", err
70 }
71
72 return buf.String(), nil
73}
74
75// StaticCaddy generates a Caddy config for a static site
76func StaticCaddy(data map[string]string) (string, error) {
77 tmpl, err := template.New("caddy").Parse(staticCaddyTemplate)
78 if err != nil {
79 return "", err
80 }
81
82 var buf bytes.Buffer
83 if err := tmpl.Execute(&buf, data); err != nil {
84 return "", err
85 }
86
87 return buf.String(), nil
88}
89
90var postReceiveHookTemplate = `#!/bin/bash
91set -euo pipefail
92
93REPO=/srv/git/{{.Name}}.git
94SRC=/var/lib/{{.Name}}/src
95NAME={{.Name}}
96
97while read oldrev newrev refname; do
98 branch=$(git rev-parse --symbolic --abbrev-ref "$refname")
99 [ "$branch" = "main" ] || { echo "Pushed to $branch, skipping deploy."; exit 0; }
100done
101
102# Ensure checkout directory exists
103sudo /bin/mkdir -p "$SRC"
104sudo /bin/chown -R git:git "/var/lib/${NAME}"
105
106echo "==> Checking out code..."
107git --work-tree="$SRC" --git-dir="$REPO" checkout -f main
108
109cd "$SRC"
110
111# If no Dockerfile, nothing to deploy
112if [ ! -f Dockerfile ]; then
113 echo "No Dockerfile found, skipping deploy."
114 exit 0
115fi
116
117# Install deployment config from repo (using full paths for sudoers)
118if [ -f "$SRC/.ship/service" ]; then
119 echo "==> Installing systemd unit..."
120 sudo /bin/cp "$SRC/.ship/service" "/etc/systemd/system/${NAME}.service"
121 sudo systemctl daemon-reload
122fi
123if [ -f "$SRC/.ship/Caddyfile" ]; then
124 echo "==> Installing Caddy config..."
125 sudo /bin/cp "$SRC/.ship/Caddyfile" "/etc/caddy/sites-enabled/${NAME}.caddy"
126 sudo systemctl reload caddy
127fi
128
129# Ensure data directory exists
130sudo /bin/mkdir -p "/var/lib/${NAME}/data"
131sudo /bin/chown -R git:git "/var/lib/${NAME}/data"
132
133echo "==> Building Docker image..."
134docker build -t ${NAME}:latest .
135
136echo "==> Restarting service..."
137sudo systemctl restart ${NAME}
138
139echo "==> Deploy complete!"
140`
141
142var postReceiveHookStaticTemplate = `#!/bin/bash
143set -euo pipefail
144
145REPO=/srv/git/{{.Name}}.git
146WEBROOT=/var/www/{{.Name}}
147NAME={{.Name}}
148
149while read oldrev newrev refname; do
150 branch=$(git rev-parse --symbolic --abbrev-ref "$refname")
151 [ "$branch" = "main" ] || { echo "Pushed to $branch, skipping deploy."; exit 0; }
152done
153
154echo "==> Deploying static site..."
155git --work-tree="$WEBROOT" --git-dir="$REPO" checkout -f main
156
157if [ -f "$WEBROOT/.ship/Caddyfile" ]; then
158 echo "==> Installing Caddy config..."
159 sudo /bin/cp "$WEBROOT/.ship/Caddyfile" "/etc/caddy/sites-enabled/${NAME}.caddy"
160 sudo systemctl reload caddy
161fi
162
163echo "==> Deploy complete!"
164`
165
166var codeCaddyTemplate = `{{.BaseDomain}} {
167 @goget query go-get=1
168 handle @goget {
169 root * /opt/ship/vanity
170 templates
171 rewrite * /index.html
172 file_server
173 }
174
175 @git path_regexp "^.*/(HEAD|info/refs|objects/info/[^/]+|git-upload-pack|git-receive-pack)$"
176 handle @git {
177 reverse_proxy unix//run/fcgiwrap.socket {
178 transport fastcgi {
179 env SCRIPT_FILENAME /usr/lib/git-core/git-http-backend
180 env GIT_PROJECT_ROOT /srv/git
181 env REQUEST_METHOD {method}
182 env QUERY_STRING {query}
183 env PATH_INFO {path}
184 }
185 }
186 }
187
188 @cgitassets path /cgit/*
189 handle @cgitassets {
190 root * /usr/share/cgit
191 uri strip_prefix /cgit
192 file_server
193 }
194
195 handle {
196 reverse_proxy unix//run/fcgiwrap.socket {
197 transport fastcgi {
198 env SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi
199 env QUERY_STRING {query}
200 env REQUEST_METHOD {method}
201 env PATH_INFO {path}
202 env HTTP_HOST {host}
203 env SERVER_NAME {host}
204 }
205 }
206 }
207}
208`
209
210var dockerServiceTemplate = `[Unit]
211Description={{.Name}}
212After=network.target docker.service
213Requires=docker.service
214
215[Service]
216Type=simple
217ExecStartPre=-/usr/bin/docker rm -f {{.Name}}
218ExecStart=/usr/bin/docker run --rm --name {{.Name}} \
219 -p 127.0.0.1:{{.Port}}:{{.ContainerPort}} \
220 --env-file /etc/ship/env/{{.Name}}.env \
221 -v /var/lib/{{.Name}}/data:/data \
222 {{.Name}}:latest
223ExecStop=/usr/bin/docker stop -t 10 {{.Name}}
224Restart=always
225RestartSec=5s
226
227[Install]
228WantedBy=multi-user.target
229`
230
231var defaultAppCaddyTemplate = `{{.Domain}} {
232 reverse_proxy 127.0.0.1:{{.Port}}
233}
234`
235
236var defaultStaticCaddyTemplate = `{{.Domain}} {
237 root * /var/www/{{.Name}}
238 file_server
239 encode gzip
240}
241`
242
243// PostReceiveHook generates a post-receive hook for git-app repos
244func PostReceiveHook(data map[string]string) (string, error) {
245 return renderTemplate("post-receive", postReceiveHookTemplate, data)
246}
247
248// PostReceiveHookStatic generates a post-receive hook for git-static repos
249func PostReceiveHookStatic(data map[string]string) (string, error) {
250 return renderTemplate("post-receive-static", postReceiveHookStaticTemplate, data)
251}
252
253// CodeCaddy generates the base domain Caddy config for vanity imports + git HTTP
254func CodeCaddy(data map[string]string) (string, error) {
255 return renderTemplate("code-caddy", codeCaddyTemplate, data)
256}
257
258var cgitrcTemplate = `virtual-root=/
259css=/cgit/cgit.css
260logo=/cgit/cgit.png
261header=/opt/ship/cgit-header.html
262scan-path=/srv/git/
263export-ok=git-daemon-export-ok
264enable-http-clone=0
265clone-url=https://{{.BaseDomain}}/$CGIT_REPO_URL
266root-title={{.BaseDomain}}
267root-desc=
268remove-suffix=.git
269`
270
271var cgitHeaderTemplate = `<style>
272body, table, td, th, div#cgit { background: #1a1a2e; color: #ccc; }
273a { color: #7aa2f7; }
274a:hover { color: #9ecbff; }
275table.list tr:hover td { background: #222244; }
276table.list td, table.list th { border-bottom: 1px solid #333; }
277th { background: #16213e; }
278td.commitgraph .column1 { color: #7aa2f7; }
279td.commitgraph .column2 { color: #9ece6a; }
280td.logheader { background: #16213e; }
281div#header { background: #16213e; border-bottom: 1px solid #333; }
282div#header .sub { color: #888; }
283table.tabs { border-bottom: 1px solid #333; }
284table.tabs td a { color: #ccc; }
285table.tabs td a.active { color: #fff; background: #1a1a2e; border: 1px solid #333; border-bottom: 1px solid #1a1a2e; }
286div.footer { color: #555; }
287div.footer a { color: #555; }
288div.diffstat-header { background: #16213e; }
289table.diffstat { border-bottom: 1px solid #333; }
290table.diffstat td.graph span.graph-moreremoved { background: #f7768e; }
291table.diffstat td.graph span.graph-moreadded { background: #9ece6a; }
292table.diffstat td.graph span.graph-removed { background: #f7768e; }
293table.diffstat td.graph span.graph-added { background: #9ece6a; }
294table.diff { background: #131320; border: 1px solid #333; }
295div.diff td { font-family: monospace; }
296div.head { color: #ccc; background: #16213e; padding: 2px 4px; }
297div.hunk { color: #7aa2f7; background: #1a1a3e; padding: 2px 4px; }
298div.add { color: #9ece6a; background: #1a2e1a; padding: 2px 4px; }
299div.del { color: #f7768e; background: #2e1a1a; padding: 2px 4px; }
300table.diff td.add { color: #9ece6a; background: #1a2e1a; }
301table.diff td.del { color: #f7768e; background: #2e1a1a; }
302table.diff td.hunk { color: #7aa2f7; background: #1a1a3e; }
303table.diff td { border: none; background: #1a1a2e; }
304table.blob td.lines { color: #ccc; }
305table.blob td.linenumbers { background: #16213e; }
306table.blob td.linenumbers a { color: #555; }
307table.blob td.linenumbers a:hover { color: #7aa2f7; }
308table.ssdiff td.add { color: #9ece6a; background: #1a2e1a; }
309table.ssdiff td.del { color: #f7768e; background: #2e1a1a; }
310table.ssdiff td { border-right: 1px solid #333; }
311table.ssdiff td.hunk { color: #7aa2f7; background: #1a1a3e; }
312table.ssdiff td.head { background: #16213e; border-bottom: 1px solid #333; }
313table.ssdiff td.foot { background: #16213e; border-top: 1px solid #333; }
314table.ssdiff td.lineno { background: #16213e; color: #555; }
315pre { color: #ccc; }
316input, textarea, select { background: #222; color: #ccc; border: 1px solid #444; }
317img#logo { display: none; }
318</style>
319`
320
321// CgitRC generates the /etc/cgitrc config file
322func CgitRC(data map[string]string) (string, error) {
323 return renderTemplate("cgitrc", cgitrcTemplate, data)
324}
325
326// CgitHeader generates the cgit header HTML file (dark theme)
327func CgitHeader() string {
328 return cgitHeaderTemplate
329}
330
331// DockerService generates a systemd unit for a Docker-based app
332func DockerService(data map[string]string) (string, error) {
333 return renderTemplate("docker-service", dockerServiceTemplate, data)
334}
335
336// DefaultAppCaddy generates a default Caddyfile for a git-app
337func DefaultAppCaddy(data map[string]string) (string, error) {
338 return renderTemplate("default-app-caddy", defaultAppCaddyTemplate, data)
339}
340
341// DefaultStaticCaddy generates a default Caddyfile for a git-static site
342func DefaultStaticCaddy(data map[string]string) (string, error) {
343 return renderTemplate("default-static-caddy", defaultStaticCaddyTemplate, data)
344}
345
346func renderTemplate(name, tmplStr string, data map[string]string) (string, error) {
347 tmpl, err := template.New(name).Parse(tmplStr)
348 if err != nil {
349 return "", err
350 }
351
352 var buf bytes.Buffer
353 if err := tmpl.Execute(&buf, data); err != nil {
354 return "", err
355 }
356
357 return buf.String(), nil
358}