From 96555b8bd1ec42fc3f937ffd4ffcfb721faec521 Mon Sep 17 00:00:00 2001 From: Jeroen de Jong Date: Mon, 21 Oct 2019 15:09:25 +0200 Subject: [PATCH] Add pristine? cli command related to #88 --- README.md | 9 ++++ src/formatting_stack/branch_formatter.clj | 46 +++++++++++++++++++- src/formatting_stack/core.clj | 19 ++++---- src/formatting_stack/formatters/clean_ns.clj | 15 ++++++- src/formatting_stack/formatters/cljfmt.clj | 10 +++++ src/formatting_stack/linters/ns_aliases.clj | 36 ++++++++------- src/formatting_stack/protocols/linter.clj | 3 +- 7 files changed, 106 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index baecf13a..b6102ffe 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,15 @@ That's by design, to avoid intrincate DSLs or data structures. If you need something finer-grained, you are encouraged to copy the contents of the `formatting-stack.defaults` ns to your project, adapting things as needed. That ns is a deliberately thin and data-only one, with the precise purpose of being forked at no cost. +## CI integration + +**formatting-stack** can be used in a CI configuration to assert that there's no formatting errors or new warnings introduced. +```bash +lein with-profile -dev,+ci run -m formatting-stack.branch-formatter pristine? +``` + +will yield assertion errors if the build introduces new warnings / formatting changes in files touched against the `master`-branch. + ## [FAQ](https://github.com/nedap/formatting-stack/wiki/FAQ) ## License diff --git a/src/formatting_stack/branch_formatter.clj b/src/formatting_stack/branch_formatter.clj index e6b48ac8..10c0540d 100644 --- a/src/formatting_stack/branch_formatter.clj +++ b/src/formatting_stack/branch_formatter.clj @@ -1,6 +1,7 @@ (ns formatting-stack.branch-formatter "A set of defaults apt for formatting a git branch (namely, the files that a branch has modified, respective to another)." (:require + [clojure.string :as str] [formatting-stack.core] [formatting-stack.formatters.cider :as formatters.cider] [formatting-stack.formatters.clean-ns :as formatters.clean-ns] @@ -16,7 +17,9 @@ [formatting-stack.linters.loc-per-ns :as linters.loc-per-ns] [formatting-stack.linters.ns-aliases :as linters.ns-aliases] [formatting-stack.strategies :as strategies] - [medley.core :refer [mapply]])) + [medley.core :refer [mapply]] + [nedap.speced.def :as speced]) + (:gen-class)) (def third-party-indent-specs formatting-stack.indent-specs/default-third-party-indent-specs) @@ -81,3 +84,44 @@ :compilers [] :linters linters :in-background? in-background?))) + +(speced/defn -main [command & _args] + (assert (#{"pristine?"} command) + (str "Command not recognised.\n\n" + "Available commands: pristine?.\n\n" + "You can refer to the documentation in: https://github.com/nedap/formatting-stack")) + + (let [default-strategies [(fn [& {:as options}] + (mapply strategies/git-diff-against-default-branch (assoc options :target-branch "master")))] + opts {:third-party-indent-specs third-party-indent-specs} + linters [(formatters.cljfmt/map->Formatter opts) + (formatters.clean-ns/map->Formatter (assoc opts :strategies (conj default-strategies + strategies/files-with-a-namespace + strategies/exclude-cljc + strategies/exclude-cljs + strategies/exclude-edn + strategies/do-not-use-cached-results!)))] + results (formatting-stack.core/process! formatting-stack.protocols.linter/lint! + linters + nil + default-strategies + false) + + print-results (fn [results] (->> (zipmap (map type linters) results) + (reduce-kv (fn reduce-linters [memo linter filenames] + (reduce (fn reduce-files [memo filename] + (update memo filename (fnil conj []) linter)) + memo + filenames)) + {}) + (run! (fn print-violations [[filename linters]] + (println (str "📄" "\033[1m ./" filename "\033[0m\n" + (->> linters + (map #(str " - " (pr-str %) "\n")) + (apply str))))))))] + (when-not (every? empty? results) + (println "🚨 Violations found: \n") + (print-results results) + (System/exit 1))) + + (println "👍🏻 No violations found")) diff --git a/src/formatting_stack/core.clj b/src/formatting_stack/core.clj index b41aaf1d..06a95b51 100644 --- a/src/formatting_stack/core.clj +++ b/src/formatting_stack/core.clj @@ -43,15 +43,16 @@ files (memoize (fn [strategies] (files-from-strategies strategies)))] (with-serialized-output - (doseq [member members] - (let [{specific-strategies :strategies} member - strategies (or specific-strategies category-strategies default-strategies)] - (try - (->> strategies files (method member)) - (catch Exception e - (println "Encountered an exception, which will be printed in the next line." - "formatting-stack execution has *not* been aborted.") - (-> e .printStackTrace)))))))) + (map (fn [member] + (let [{specific-strategies :strategies} member + strategies (or specific-strategies category-strategies default-strategies)] + (try + (->> strategies files (method member)) + (catch Exception e + (println "Encountered an exception, which will be printed in the next line." + "formatting-stack execution has *not* been aborted.") + (-> e .printStackTrace))))) + members)))) (defn format! [& {:keys [strategies third-party-indent-specs diff --git a/src/formatting_stack/formatters/clean_ns.clj b/src/formatting_stack/formatters/clean_ns.clj index fea6f403..1bdeaac5 100644 --- a/src/formatting_stack/formatters/clean_ns.clj +++ b/src/formatting_stack/formatters/clean_ns.clj @@ -3,11 +3,14 @@ [formatting-stack.formatters.clean-ns.impl :as impl] [formatting-stack.formatters.how-to-ns] [formatting-stack.protocols.formatter] - [formatting-stack.util :refer [process-in-parallel! try-require]] + [formatting-stack.protocols.linter] + [formatting-stack.util :refer [process-in-parallel! try-require without-aliases]] [formatting-stack.util.ns :refer [replace-ns-form!]] [medley.core :refer [deep-merge]] [nedap.speced.def :as speced] - [refactor-nrepl.config])) + [refactor-nrepl.config]) + (:import + (java.io File))) (defn make-cleaner [how-to-ns-opts refactor-nrepl-opts namespaces-that-should-never-cleaned libspec-whitelist filename] (speced/fn ^{::speced/spec (complement #{"nil"})} [original-ns-form] @@ -49,6 +52,14 @@ #{'user 'dev}) (defrecord Formatter [how-to-ns-opts refactor-nrepl-opts namespaces-that-should-never-cleaned libspec-whitelist] + formatting-stack.protocols.linter/Linter + (lint! [this files] + (let [changed-files (atom [])] + (with-redefs [spit (fn [f & _] + (swap! changed-files conj (if (string? f) f (.getPath ^File f))))] + (with-out-str (formatting-stack.protocols.formatter/format! this files)) + @changed-files))) + formatting-stack.protocols.formatter/Formatter (format! [this files] (let [refactor-nrepl-opts (deep-merge refactor-nrepl.config/*config* diff --git a/src/formatting_stack/formatters/cljfmt.clj b/src/formatting_stack/formatters/cljfmt.clj index 9762f0c5..a473eb2b 100644 --- a/src/formatting_stack/formatters/cljfmt.clj +++ b/src/formatting_stack/formatters/cljfmt.clj @@ -4,10 +4,20 @@ [clojure.java.io :as io] [formatting-stack.formatters.cljfmt.impl :as impl] [formatting-stack.protocols.formatter] + [formatting-stack.protocols.linter] [formatting-stack.util :refer [process-in-parallel!]] [medley.core :refer [deep-merge]])) (defrecord Formatter [options third-party-indent-specs] + formatting-stack.protocols.linter/Linter + (lint! [this files] + (->> files + (process-in-parallel! (fn [filename] + (let [indents (impl/cljfmt-indents-for filename third-party-indent-specs)] + (#'cljfmt.main/check-one {:indents indents} filename)))) + (remove (fn [{{:keys [incorrect error]} :counts}] (zero? (+ incorrect error)))) + (map :file))) + formatting-stack.protocols.formatter/Formatter (format! [this files] (let [cljfmt-opts (deep-merge cljfmt.main/default-options diff --git a/src/formatting_stack/linters/ns_aliases.clj b/src/formatting_stack/linters/ns_aliases.clj index 470eff57..b86dd0af 100644 --- a/src/formatting_stack/linters/ns_aliases.clj +++ b/src/formatting_stack/linters/ns_aliases.clj @@ -74,22 +74,20 @@ (let [acceptable-aliases-whitelist (or acceptable-aliases-whitelist default-acceptable-aliases-whitelist)] (->> filenames - (process-in-parallel! (fn [filename] - (let [bad-require-clauses (->> filename - file/read-file-ns-decl - formatting-stack.util/require-from-ns-decl - (rest) - (remove (partial acceptable-require-clause? - acceptable-aliases-whitelist)))] - (when (seq bad-require-clauses) - (let [formatted-bad-requires (->> bad-require-clauses - (map (fn [x] - (str " " x))) - (string/join "\n"))] - (-> (str "Warning for " - filename - ": the following :require aliases are not derived from their refered namespace:" - "\n" - formatted-bad-requires - ". See https://stuartsierra.com/2015/05/10/clojure-namespace-aliases\n") - (println))))))))))) + (process-in-parallel! + (fn [filename] + (let [bad-require-clauses (->> filename + file/read-file-ns-decl + formatting-stack.util/require-from-ns-decl + (rest) + (remove (partial acceptable-require-clause? + acceptable-aliases-whitelist)))] + (when (seq bad-require-clauses) + (let [formatted-bad-requires (->> bad-require-clauses + (map (fn [x] + (str " " x))) + (string/join "\n"))] + (-> (str "Warning for " filename + ": the following :require aliases are not derived from their referred namespace:\n" + formatted-bad-requires ". See https://stuartsierra.com/2015/05/10/clojure-namespace-aliases\n") + (println))))))))))) diff --git a/src/formatting_stack/protocols/linter.clj b/src/formatting_stack/protocols/linter.clj index acaa2e4a..9048040f 100644 --- a/src/formatting_stack/protocols/linter.clj +++ b/src/formatting_stack/protocols/linter.clj @@ -2,4 +2,5 @@ (defprotocol Linter (lint! [this filenames] - "Lints `filenames` according to a linter of your choice: e.g. Eastwood, or Kibit, lein-dependency-check, etc.")) + "Lints `filenames` according to a linter of your choice: e.g. Eastwood, or Kibit, lein-dependency-check, etc. + returns a seq of filenames with violations"))