Code to generate docs for environment variables. You can generate docs by running: clojure -M:ee:doc environment-variables-documentation

(ns metabase.cmd.env-var-dox
  (:require
   [clojure.java.classpath :as classpath]
   [clojure.java.io :as io]
   [clojure.string :as str]
   [clojure.tools.namespace.find :as ns.find]
   [clojure.tools.reader.edn :as edn]
   [metabase.models.setting :as setting]
   [metabase.query-processor.middleware.constraints :as qp.constraints]
   [metabase.util :as u]))

Used to return a map from the registered settings atom.

(defn prep-settings
  [settings]
  (->> settings
       (into (sorted-map))
       seq
       (map (fn [[_ v]] v))))

Loads all (or a set of) of the Metabase namespaces, which loads all of the defsettings, which are registered in an atom in the settings namespace. Once settings are registered, this function derefs that atom and puts the settings into a sorted map for processing.

(defn get-settings
  ([]
   (doseq [ns-symb (ns.find/find-namespaces (classpath/system-classpath))
           :when (and
                  (str/includes? (name ns-symb) "metabase")
                  (not (str/includes? (name ns-symb) "test")))]
     (require ns-symb))
   (prep-settings @setting/registered-settings))
  ;; Or supply a set of namespaces to load
  ;; Primarily used for testing
  ([ns-set]
   (doseq [ns-symb (ns.find/find-namespaces (classpath/system-classpath))
           :when (ns-set (name ns-symb))]
     (require ns-symb))
   (prep-settings @setting/registered-settings)))

Formatting functions

Helper function to specify the format of an environment variable's type for its documentation.

(defn- format-type
  [env-var]
  (str "Type: " (name (:type env-var))))

Handles defaults not set in the defsetting.

(defn- handle-defaults-set-elsewhere
  [env-var]
  (let [n (:name env-var)]
    (cond (= :aggregated-query-row-limit n) (assoc env-var :default (:max-results (qp.constraints/default-query-constraints)))
          (= :unaggregated-query-row-limit n) (assoc env-var :default (:max-results-bare-rows (qp.constraints/default-query-constraints)))
          :else env-var)))

Helper function to specify how to format the default value of an environment variable. for use in the environment variable docs.

(defn- format-default
  [env-var]
  (let [d (:default (handle-defaults-set-elsewhere env-var))]
    (str "Default: "
         (cond
           (false? d) "`false`"
           (nil? d) "`null`"
           :else (str "`" d "`")))))

Used to build an environment variable, like MB_ENV_VAR_NAME

(defn- format-prefix
  [env-var]
  (str "MB_" (u/->SCREAMING_SNAKE_CASE_EN (:munged-name env-var))))

Takes an integer and a string and creates a Markdown heading of level n.

(defn- format-heading
  [n s]
  (str (apply str (take n (repeat "#"))) " `" s "`"))

Helper function to specify description format for enviromnent variable docs.

(defn- format-description
  [env-var]
  (->> ((:description env-var))
       u/add-period
       ;; Drop brackets used to create source code links
       (#(str/replace % #"\[\[|\]\]" ""))))

Used to mark an env var that requires a paid plan.

(def paid-message
  "> Only available on Metabase [Pro](https://www.metabase.com/product/pro) and [Enterprise](https://www.metabase.com/product/enterprise) plans.")

Does the variable require a paid license?

(defn- format-paid
  [env-var]
  (if (nil? (:feature env-var))
    ""
    paid-message))

Whether the variable is exported in serialization settings.

(defn- format-export
  [env-var]
  (if (true? (:export? env-var))
    (str "[Exported as](../installation-and-operation/serialization.md): `" (:munged-name env-var) "`.")
    ""))

Includes additional documentation for an environment variable, if it exists.

(defn- format-doc
  [env-var]
  (when-let [d (:doc env-var)]
    d))

Formats the configuration file name for an environment variable.

(defn- format-config-name
  [env-var]
  (if (= (:visibility env-var) :internal)
    ""
    (str "[Configuration file name](./config-file.md): `" (:munged-name env-var) "`")))

Create a list item for an entry, like - Default: 100.

(defn list-item
  [entry]
  (if (or (str/blank? entry)
          (nil? entry))
    ""
    (str "- " entry)))

Used to format metadata as a list.

(defn format-list
  [entries]
  (str/join "\n" (remove str/blank? (map list-item entries))))

Preps a doc entry for an environment variable as a Markdown section.

(defn- format-env-var-entry
  [env-var]
  (str/join "\n\n" (remove str/blank?
                           [(format-heading 3 (format-prefix env-var))
                            (format-paid env-var)
                            ;; metadata we should format as a list
                            ;; Like `- Default: 100`
                            (format-list [(format-type env-var)
                                          (format-default env-var)
                                          (format-export env-var)
                                          (format-config-name env-var)])
                            (format-description env-var)
                            (format-doc env-var)])))

Filter functions

Flamber advises that people avoid touching these environment variables.

(def env-vars-not-to-mess-with
  (set (edn/read-string (slurp (io/resource "metabase/cmd/resources/env-vars-to-avoid.edn")))))

Used to filter out environment variables with high foot-gun indices.

(defn- avoid?
  [env-var]
  (or (false? (:doc env-var))
              ;; Ideally, we'd move off of this list completely, but not all environment variables
              ;; are defsettings.
      (contains? env-vars-not-to-mess-with (format-prefix env-var))))

Used to filter out environment variables that cannot be set.

(defn- setter?
  [env-var]
  (not= :none (:setter env-var)))

Used to filter our deprecated enviroment variables.

(defn active?
  [env-var]
  (nil? (:deprecated env-var)))

Used to filter out environment variables that are only local.

(defn- only-local?
  [env-var]
  (or (= (:user-local env-var) :only)
      (= (:database-local env-var) :only)))

Filters for valid environment variables.

(defn filter-env-vars
  [settings]
  (->> settings
       (remove avoid?)
       (remove only-local?)
       (filter setter?)
       (filter active?)))

Preps relevant environment variable docs as a Markdown string.

(defn format-env-var-docs
  [settings]
  (map format-env-var-entry (filter-env-vars settings)))

Exists just so we can write the intro in Markdown.

(defn- format-intro
  []
  (str (slurp "src/metabase/cmd/resources/env-var-intro.md") "\n\n"))

Retrieves environment variables not specified via defsetting.

(defn- non-defsetting-env-vars
  []
  (str "\n\n" (slurp "src/metabase/cmd/resources/other-env-vars.md") "\n"))

Preps the environment variable docs for printing.

(defn prep-dox
  []
  (apply str (format-intro)
         (str/join "\n\n" (format-env-var-docs (get-settings)))
         (non-defsetting-env-vars)))

Prints the generated environment variable docs to a file.

(defn generate-dox!
  []
  (println "Generating docs for environment variables...")
  (spit (io/file "docs/configuring-metabase/environment-variables.md") (prep-dox))
  (println "Done."))