Last active
January 8, 2024 14:14
-
-
Save xfthhxk/01ef9b489094e5f7abbae7e8cd43784c to your computer and use it in GitHub Desktop.
Project setup with babashka & spire
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bb | |
;; NB. it can be helpful to do `sudo id` on the cmdline before running this script so that the privileged commands work | |
;; Spire doesn't yet have the ability to ask for a password when sudo is used | |
;; | |
;; | |
;; This script is an example of how to use babashka and spire to automate project setup for local development. | |
;; Sets up the local development environment so that necessary software is installed and https can be used. | |
;; You would tailor it to your needs. | |
;; | |
;; | |
;; Installs: | |
;; - needed OS packages | |
;; - Docker | |
;; - gcloud CLI | |
;; - root CAs generated by mkcert | |
;; - NVM && node | |
;; - SDKMAN && jvm | |
;; - Clojure CLI | |
;; | |
;; Updates: | |
;; - `/etc/hosts` maps `+local-domain+` to 127.0.0.1 | |
;; - babashka tab completion | |
;; - gcloud tab completion | |
;; - installs a dev env shell function | |
(require '[babashka.pods]) | |
(babashka.pods/load-pod 'epiccastle/spire "0.1.0-alpha.17") | |
(ns project-setup | |
(:require [pod.epiccastle.spire.module.apt :as apt] | |
[pod.epiccastle.spire.module.apt-key :as apt-key] | |
[pod.epiccastle.spire.module.apt-repo :as apt-repo] | |
[pod.epiccastle.spire.module.attrs :as attrs] | |
[pod.epiccastle.spire.facts :as facts] | |
[pod.epiccastle.spire.module.shell :as shell] | |
[pod.epiccastle.spire.module.sudo :as sudo] | |
[pod.epiccastle.spire.module.mkdir :as mkdir] | |
[babashka.fs :as fs] | |
[clojure.string :as str])) | |
(def +docker-keyring+ "/etc/apt/keyrings/docker.gpg") | |
(def +gcp-keyring+ "/usr/share/keyrings/cloud.google.gpg") | |
(def +java-version+ "21.0.1-tem") | |
(def +node-version+ "20.3.0") | |
(def +local-domain+ "app.local.example.com") | |
(def +mkcert-domains+ ["localhost" +local-domain+]) | |
(def +system-facts+ (facts/fetch-facts)) | |
(def +system-codename+ (get-in +system-facts+ [:system :codename])) | |
(def +shell+ (get-in +system-facts+ [:system :shell])) | |
(def +username+ (get-in +system-facts+ [:user :uid :name])) | |
(def +home-dir+ (str "/home/" +username+)) | |
(def +bin-dir+ (str +home-dir+ "/bin")) | |
(def +rc-file+ (str +home-dir+ "/" (case +shell+ | |
:zsh ".zshrc" | |
".bashrc"))) | |
(def +mkcert+ (str +bin-dir+ "/mkcert")) | |
(def +mkcert-dir+ (str +home-dir+ "/.local/share/mkcert")) | |
(defn wsl? | |
[] | |
(let [s (str/lower-case (get-in +system-facts+ [:uname :string]))] | |
(and (str/includes? s "microsoft") | |
(str/includes? s "wsl")))) | |
(defn ensure-file-content! | |
[{:keys [path present? content custom]}] | |
(let [file-str (slurp path)] | |
(when-not (present? file-str) | |
;; spit won't work when sudo is required so use shell | |
(cond | |
content (shell/shell {:cmd (format "echo '%s' >> %s" content path)}) | |
custom (shell/shell {:cmd (format "echo '%s' > %s" (custom file-str) path)}))))) | |
(def +zshrc-bb-tasks-autocomplete+ " | |
_bb_tasks() { | |
local matches=(`bb tasks |tail -n +3 |cut -f1 -d ' '`) | |
compadd -a matches | |
_files # autocomplete filenames as well | |
} | |
compdef _bb_tasks bb") | |
(def +bash-bb-tasks-autocomplete+ " | |
_bb_tasks() { | |
COMPREPLY=( $(compgen -W \"$(bb tasks |tail -n +3 |cut -f1 -d ' ')\" -- ${COMP_WORDS[COMP_CWORD]}) ); | |
} | |
# autocomplete filenames as well | |
complete -f -F _bb_tasks bb") | |
(def +dev-env-activate+ " | |
function dev_env { | |
gcloud config configurations activate project-dev | |
unset GOOGLE_APPLICATION_CREDENTIALS | |
unset GOOGLE_PROJECT | |
alias db-local='psql -h 127.0.0.1 -U db-user -p 5400 -d db-dev' | |
}") | |
(defn bb-tasks-autocomplete | |
[shell] | |
(case shell | |
:zsh +zshrc-bb-tasks-autocomplete+ | |
+bash-bb-tasks-autocomplete+)) | |
(defn ensure-bb-tasks-autocomplete! | |
[] | |
(ensure-file-content! | |
{:path +rc-file+ | |
:present? (fn [s] (str/includes? s "_bb_tasks")) | |
:content (str "\n" (bb-tasks-autocomplete +shell+))})) | |
(defn ensure-gcloud-autocomplete! | |
[] | |
(let [file "/usr/lib/google-cloud-sdk/completion.bash.inc"] | |
(ensure-file-content! | |
{:path +rc-file+ | |
:present? (fn [s] (str/includes? s file)) | |
:content (format "\nif [ -f '%s' ]; then source '%s'; fi\n" file file)}))) | |
(defn ensure-dev-env-fn! | |
[] | |
(ensure-file-content! | |
{:path +rc-file+ | |
:present? (fn [s] (str/includes? s "function dev_env")) | |
:content +dev-env-activate+})) | |
(defn ensure-hosts-mapping! | |
[] | |
(sudo/sudo | |
(ensure-file-content! | |
{:path "/etc/hosts" | |
:present? (fn [s] (str/includes? s +local-domain+)) | |
:custom (fn [s] | |
(->> (str/split-lines s) | |
(map (fn [s] | |
(if-not (str/includes? s "127.0.0.1") | |
s | |
(str s " " +local-domain+)))) | |
(str/join \newline)))}))) | |
(defn download-mkcert! | |
[] | |
(let [cmd (format "curl --silent --location --output %s 'https://dl.filippo.io/mkcert/latest?for=linux/amd64'" +mkcert+)] | |
(when-not (fs/exists? +mkcert+) | |
(shell/shell {:cmd cmd}) | |
(attrs/attrs {:path +mkcert+ :mode "u+x"})))) | |
(defn setup-mkcert! | |
[] | |
(let [local-pem "./dev/docker/nginx/ssl/local-dev.pem" | |
local-key "./dev/docker/nginx/ssl/local-dev-key.pem" | |
domains (str/join " " +mkcert-domains+) | |
gen-cert-cmd (format "%s -cert-file %s -key-file %s %s" +mkcert+ local-pem local-key domains)] | |
(when-not (fs/exists? local-pem) | |
(sudo/sudo | |
;; install the root certs to allow mkcert as a CA | |
(shell/shell {:cmd (str +mkcert+ " -install")})) | |
(shell/shell {:cmd gen-cert-cmd})))) | |
(defn export-mkcert-root-ca! | |
[] | |
(let [p12 (str +mkcert-dir+ "/rootCA.p12") | |
cmd "openssl pkcs12 -export -out rootCA.p12 -in rootCA.pem -inkey rootCA-key.pem"] | |
(when-not (fs/exists? p12) | |
(shell/shell {:dir +mkcert-dir+ :cmd cmd})))) | |
(defn install-os-packages! | |
[] | |
(sudo/sudo | |
(apt/apt :update) | |
(apt/apt :install ["ca-certificates" "gnupg" "curl" "apt-transport-https"]) | |
(apt-key/apt-key :present {:public-key-url "https://download.docker.com/linux/ubuntu/gpg" | |
:keyring +docker-keyring+}) | |
(attrs/attrs {:path +docker-keyring+ | |
:mode "a+r"}) | |
(apt-repo/apt-repo :present {:repo (format "deb [arch=amd64 signed-by=%s] https://download.docker.com/linux/ubuntu %s stable" | |
+docker-keyring+ (name +system-codename+)) | |
:filename "docker"}) | |
(apt-key/apt-key :present {:public-key-url "https://packages.cloud.google.com/apt/doc/apt-key.gpg" | |
:keyring +gcp-keyring+}) | |
(apt-repo/apt-repo :present {:repo (format "deb [signed-by=%s] https://packages.cloud.google.com/apt cloud-sdk main" +gcp-keyring+) | |
:filename "google-cloud-sdk"}) | |
(apt/apt :update) | |
(apt/apt :install ["docker-ce" "docker-ce-cli" "containerd.io" "docker-buildx-plugin" "docker-compose-plugin" | |
"libnss3-tools" ;; required by mkcert | |
"google-cloud-cli"]))) | |
(defn do-all-steps! | |
[] | |
(mkdir/mkdir {:path +bin-dir+ | |
:owner +username+ | |
:group +username+ | |
:mode "u=rwx,go=rx"}) | |
(shell/shell {:dir ".git/hooks" | |
:cmd "ln -s -f ../../dev/git/hooks/pre-commit ."}) | |
(ensure-hosts-mapping!) | |
(install-os-packages!) | |
(sudo/sudo | |
(shell/shell {:cmd (format "usermod -aG docker %s" +username+)})) | |
(download-mkcert!) | |
(setup-mkcert!) | |
;; install nvm | |
(shell/shell {:cmd (format "curl --silent https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash")}) | |
;; nvm is a shell function | |
(shell/shell {:cmd (format "source %s/.nvm/nvm.sh && nvm install %s && nvm alias default %s" +home-dir+ +node-version+ +node-version+)}) | |
(shell/shell {:cmd "npm install"}) | |
;; sdk is apparently a shell function so have to source sdkman-init | |
(shell/shell {:cmd "curl --silent https://get.sdkman.io | bash"}) | |
(shell/shell {:cmd (format "source ~/.sdkman/bin/sdkman-init.sh && sdk install java %s" +java-version+)}) | |
(sudo/sudo | |
(shell/shell {:cmd "curl --silent https://github.com/clojure/brew-install/releases/latest/download/linux-install.sh | bash"})) | |
(ensure-dev-env-fn!) | |
(ensure-bb-tasks-autocomplete!) | |
(ensure-gcloud-autocomplete!)) | |
(do-all-steps!) | |
(when (wsl?) | |
(export-mkcert-root-ca!) | |
(println (format "Ensure %s/rootCA.p12 is installed on windows using mmc.exe" +mkcert-dir+))) | |
(run! println ["********************" | |
"Done!" | |
(format "You may wish to `source %s`" +rc-file+) | |
"********************"]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment