package templates import ( "bytes" "text/template" ) var serviceTemplate = `[Unit] Description={{.Name}} After=network.target [Service] Type=simple User={{.User}} WorkingDirectory={{.WorkDir}} EnvironmentFile={{.EnvFile}} ExecStart={{.BinaryPath}} {{.Args}} Restart=always RestartSec=5s NoNewPrivileges=true PrivateTmp=true {{- if .Memory}} MemoryMax={{.Memory}} {{- end}} {{- if .CPU}} CPUQuota={{.CPU}} {{- end}} [Install] WantedBy=multi-user.target ` var appCaddyTemplate = `{{.Domain}} { reverse_proxy 127.0.0.1:{{.Port}} } ` var staticCaddyTemplate = `{{.Domain}} { root * {{.RootDir}} file_server encode gzip } ` // SystemdService generates a systemd service unit file func SystemdService(data map[string]string) (string, error) { tmpl, err := template.New("service").Parse(serviceTemplate) if err != nil { return "", err } var buf bytes.Buffer if err := tmpl.Execute(&buf, data); err != nil { return "", err } return buf.String(), nil } // AppCaddy generates a Caddy config for a Go app func AppCaddy(data map[string]string) (string, error) { tmpl, err := template.New("caddy").Parse(appCaddyTemplate) if err != nil { return "", err } var buf bytes.Buffer if err := tmpl.Execute(&buf, data); err != nil { return "", err } return buf.String(), nil } // StaticCaddy generates a Caddy config for a static site func StaticCaddy(data map[string]string) (string, error) { tmpl, err := template.New("caddy").Parse(staticCaddyTemplate) if err != nil { return "", err } var buf bytes.Buffer if err := tmpl.Execute(&buf, data); err != nil { return "", err } return buf.String(), nil } var postReceiveHookTemplate = `#!/bin/bash set -euo pipefail REPO=/srv/git/{{.Name}}.git SRC=/var/lib/{{.Name}}/src NAME={{.Name}} while read oldrev newrev refname; do branch=$(git rev-parse --symbolic --abbrev-ref "$refname") [ "$branch" = "main" ] || { echo "Pushed to $branch, skipping deploy."; exit 0; } done echo "==> Checking out code..." git --work-tree="$SRC" --git-dir="$REPO" checkout -f main cd "$SRC" # Install deployment config from repo if [ -f .ship/service ]; then echo "==> Installing systemd unit..." sudo cp .ship/service /etc/systemd/system/${NAME}.service sudo systemctl daemon-reload fi if [ -f .ship/Caddyfile ]; then echo "==> Installing Caddy config..." sudo cp .ship/Caddyfile /etc/caddy/sites-enabled/${NAME}.caddy sudo systemctl reload caddy fi echo "==> Building Docker image..." docker build -t ${NAME}:latest . echo "==> Restarting service..." sudo systemctl restart ${NAME} echo "==> Deploy complete!" ` var postReceiveHookStaticTemplate = `#!/bin/bash set -euo pipefail REPO=/srv/git/{{.Name}}.git WEBROOT=/var/www/{{.Name}} NAME={{.Name}} while read oldrev newrev refname; do branch=$(git rev-parse --symbolic --abbrev-ref "$refname") [ "$branch" = "main" ] || { echo "Pushed to $branch, skipping deploy."; exit 0; } done echo "==> Deploying static site..." git --work-tree="$WEBROOT" --git-dir="$REPO" checkout -f main if [ -f "$WEBROOT/.ship/Caddyfile" ]; then echo "==> Installing Caddy config..." sudo cp "$WEBROOT/.ship/Caddyfile" /etc/caddy/sites-enabled/${NAME}.caddy sudo systemctl reload caddy fi echo "==> Deploy complete!" ` var codeCaddyTemplate = `{{.BaseDomain}} { @goget query go-get=1 handle @goget { root * /opt/ship/vanity templates rewrite * /index.html file_server } @git path_regexp "^.*/(HEAD|info/refs|objects/info/[^/]+|git-upload-pack|git-receive-pack)$" handle @git { reverse_proxy unix//run/fcgiwrap.socket { transport fastcgi { env SCRIPT_FILENAME /usr/lib/git-core/git-http-backend env GIT_PROJECT_ROOT /srv/git env GIT_HTTP_EXPORT_ALL 1 env REQUEST_METHOD {method} env QUERY_STRING {query} env PATH_INFO {path} } } } handle { respond "not found" 404 } } ` var dockerServiceTemplate = `[Unit] Description={{.Name}} After=network.target docker.service Requires=docker.service [Service] Type=simple ExecStartPre=-/usr/bin/docker rm -f {{.Name}} ExecStart=/usr/bin/docker run --rm --name {{.Name}} \ -p 127.0.0.1:{{.Port}}:{{.Port}} \ --env-file /etc/ship/env/{{.Name}}.env \ -v /var/lib/{{.Name}}/data:/data \ {{.Name}}:latest ExecStop=/usr/bin/docker stop -t 10 {{.Name}} Restart=always RestartSec=5s [Install] WantedBy=multi-user.target ` var defaultAppCaddyTemplate = `{{.Domain}} { reverse_proxy 127.0.0.1:{{.Port}} } ` var defaultStaticCaddyTemplate = `{{.Domain}} { root * /var/www/{{.Name}} file_server encode gzip } ` // PostReceiveHook generates a post-receive hook for git-app repos func PostReceiveHook(data map[string]string) (string, error) { return renderTemplate("post-receive", postReceiveHookTemplate, data) } // PostReceiveHookStatic generates a post-receive hook for git-static repos func PostReceiveHookStatic(data map[string]string) (string, error) { return renderTemplate("post-receive-static", postReceiveHookStaticTemplate, data) } // CodeCaddy generates the base domain Caddy config for vanity imports + git HTTP func CodeCaddy(data map[string]string) (string, error) { return renderTemplate("code-caddy", codeCaddyTemplate, data) } // DockerService generates a systemd unit for a Docker-based app func DockerService(data map[string]string) (string, error) { return renderTemplate("docker-service", dockerServiceTemplate, data) } // DefaultAppCaddy generates a default Caddyfile for a git-app func DefaultAppCaddy(data map[string]string) (string, error) { return renderTemplate("default-app-caddy", defaultAppCaddyTemplate, data) } // DefaultStaticCaddy generates a default Caddyfile for a git-static site func DefaultStaticCaddy(data map[string]string) (string, error) { return renderTemplate("default-static-caddy", defaultStaticCaddyTemplate, data) } func renderTemplate(name, tmplStr string, data map[string]string) (string, error) { tmpl, err := template.New(name).Parse(tmplStr) if err != nil { return "", err } var buf bytes.Buffer if err := tmpl.Execute(&buf, data); err != nil { return "", err } return buf.String(), nil }