From a90542e59dd599d12682b5f4df15290c48121d01 Mon Sep 17 00:00:00 2001 From: Ade Attwood Date: Sat, 4 Nov 2023 09:05:08 +0000 Subject: [PATCH] feat(shell): add the small procfile process manager in tmux MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A Procfile runner for tmux. Each procfile will have its own tmux session and each process will have its own window. This way you can use tmux to attach view logs and also restart individual processes. --- site-modules/core/files/bin/pf | 80 ++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100755 site-modules/core/files/bin/pf diff --git a/site-modules/core/files/bin/pf b/site-modules/core/files/bin/pf new file mode 100755 index 0000000..82bf357 --- /dev/null +++ b/site-modules/core/files/bin/pf @@ -0,0 +1,80 @@ +#!/usr/bin/env bb +;; vi: ft=clojure +;; +;; A Procfile runner for tmux. Each procfile will have its own tmux session and +;; each process will have its own window. This way you can use tmux to attach +;; view logs and also restart individual processes. +;; +(require '[babashka.process :refer [shell]] + '[babashka.cli :as cli] + '[babashka.fs :as fs] + '[clojure.string :as string]) + +(defn current-project-name + "Returns the current project name based on the current working directory. + This assumes you have our porjects organised in a GOPATH style. This will + return the last two components of the current working directory, that should + be {org}/{project}" + [] + (string/join "/" (map #(.toString %) (take 2 (reverse(fs/components (fs/cwd))))))) + +(defn parse-procfile + "Parses a procfile file and returns a list of [name command] pairs." + [] + (map #(string/split % #":\s+") (string/split (slurp "Procfile.dev") #"\n" ))) + +(defn has-session? + "Tests to see if a session already exists with a given name." + [session-name] + (string/includes? (:out (shell {:out :string} "tmux list-sessions")) session-name)) + +(defn has-window? + "Tests to see if a window already exists within a tmux session." + [session-name window-name] + (string/includes? (:out (shell {:out :string} (format "tmux list-windows -t '%s'" session-name))) window-name)) + +(defn create-window + "Creates a new tmux window in a session if it dose not already exists. Then + it will run any commands that are passed inside the new window." + [project name & commands] + (when-not (has-window? project name) + (shell (format "tmux new-window -t '%s' -n '%s'" project name)) + (doseq [command commands] + (shell (format "tmux send-keys -t %s:'%s' '%s' C-m" project name command))))) + +(defn command-start [_] + (when (not (fs/exists? "Procfile.dev")) + (println "No Procfile.dev found in the current directory") + (System/exit 0)) + + (when (not (has-session? (current-project-name))) + (shell (format "tmux new-session -d -c %s -s '%s'" (fs/cwd) (current-project-name)))) + + (doseq [[name command] (parse-procfile)] + (create-window (current-project-name) name command))) + +(defn command-stop [_] + (when (has-session? (current-project-name)) + (shell (format "tmux kill-session -t '%s'" (current-project-name))))) + +(defn command-restart [args] + (command-stop args) + (command-start args)) + +(defn help [_m] + (println "A Procfile runner for tmux") + (println "") + (println "\033[1mCommands:\033[0m") + (println " start Start all of the processes in your Procfile.dev") + (println " stop Stop all the currently running processes") + (println " restart Restart all of the processes") + (println "")) + +(def command-table + [{:cmds ["start"] :fn command-start} + {:cmds ["stop"] :fn command-stop} + {:cmds ["restart"] :fn command-restart} + {:cmds [] :fn help}]) + +(cli/dispatch command-table *command-line-args*) +