Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
include_drivers.clj 3.64 KiB
(ns leiningen.include-drivers
  (:require [clojure.string :as str]
            [leiningen.core.project :as p])
  (:import java.io.File))

(defn- file-exists? [^String filename]
  (.exists (File. filename)))

(defn- driver-parents
  "As listed in a 'parents' file in the module source directory. Add to the list of drivers to add sources for"
  [driver]
  (let [parents-file (File. (format "modules/drivers/%s/parents" driver))]
    (when (.exists parents-file)
      (str/split-lines (slurp parents-file)))))

;; if :include-drivers is specified in the project, and its value is `:all`, include all drivers; if it's a collection
;; of driver names, include the specified drivers; otherwise include whatever was set in the `DRIVERS` env var
(defn- test-drivers [{:keys [include-drivers]}]
  (let [drivers
        (cond
          (= include-drivers :all)
          (.list (java.io.File. "modules/drivers"))

          (coll? include-drivers)
          include-drivers

          :else
          (some-> (System/getenv "DRIVERS") (str/split #",")))]
    (concat drivers
            (set (mapcat driver-parents drivers)))))

(defn- test-drivers-source-paths [test-drivers]
  (vec
   (for [driver test-drivers
         :let   [source-path (format "modules/drivers/%s/src" driver)]
         :when  (file-exists? source-path)]
     source-path)))

(defn- test-drivers-test-paths [test-drivers]
  (vec
   (for [driver test-drivers
         :let   [test-path (format "modules/drivers/%s/test" driver)]
         :when  (file-exists? test-path)]
     test-path)))

(defn- test-drivers-projects [test-drivers]
  (for [driver test-drivers
        :let   [project-file (format "modules/drivers/%s/project.clj" driver)]
        :when  (file-exists? project-file)]
    (p/read project-file)))

(defn- test-drivers-dependencies [test-projects]
  (vec
   (for [{:keys [dependencies]} test-projects
         dep                    dependencies
         :when                  (not= :provided (keyword (:scope (apply array-map dep))))]
     dep)))

(defn- test-drivers-repositories [test-projects]
  (vec
   (for [{:keys [repositories]} test-projects
         repo                   repositories]
     repo)))

(defn- test-drivers-aot [test-projects]
  (vec
   (for [{:keys [aot]} test-projects
         ;; if aot is something like all we don't want to merge it into the MB project
         :when         (sequential? aot)
         klass         aot]
     klass)))

(defn- test-drivers-profile [project]
  (let [test-drivers  (test-drivers project)
        test-projects (test-drivers-projects test-drivers)]
    (when (seq test-drivers)
      (println "[include drivers middleware] adding sources/deps for these drivers:" test-drivers))
    {:repositories (test-drivers-repositories test-projects)
     :dependencies (test-drivers-dependencies test-projects)
     :aot          (test-drivers-aot          test-projects)
     :source-paths (test-drivers-source-paths test-drivers)
     :test-paths   (test-drivers-test-paths   test-drivers)}))

;; When we merge a new profile into the project Leiningen will reload the project, which will cause our middleware to
;; run a second time. Make sure we don't add the profile a second time or we'll be stuck in an infinite loop of adding
;; a new profile and reloading.
(defonce ^:private has-added-test-drivers-profile? (atom false))

(defn middleware
  "Add dependencies, source paths, and test paths for to Metabase drivers that are packaged as separate projects and
  specified by the `DRIVERS` env var."
  [project]
  (if @has-added-test-drivers-profile?
    project
    (do
      (reset! has-added-test-drivers-profile? true)
      (p/merge-profiles project [(test-drivers-profile project)]))))