Commit Description:
Small style fixes.
Commit Description:
Small style fixes.
File last commit:
Show/Diff file:
Action:
src/project_checkup/core.clj
139 lines | 5.0 KiB | text/x-clojure | ClojureLexer
#!/usr/bin/env lumo
(ns project-checkup.core
(:gen-class)
(:require [clojure.java.shell :as shell]
[clojure.string :as string]))
(defn get-extension [path]
"Extracts the extension of a path.
Returns the extension with the period, e.g., '.txt' because that's the format
people are used to seeing extensions in."
(re-find #"\.[a-zA-Z0-9]+$" path))
(defn gather-untracked
[vcs-systems]
"Gather untracked files in Git or Mercurial."
(reduce into [
(if (contains? vcs-systems ".hg")
(string/split (:out (shell/sh "chg" "st" "-u" "-n")) #"\n"))
(if (contains? vcs-systems ".git")
(string/split (:out (shell/sh "git" "ls-files" "--others"
"--exclude-standard")) #"\n"))]
)
)
(defn gather-project-info
"Creates a dictionary of project information."
[]
(let [all-files (map str (file-seq (clojure.java.io/file ".")))
; files (string/split (:out (shell/sh "hg" "st" "-m" "-a" "-r" "-d" "-c" "-n" )) #"\n")
files (map #(clojure.string/replace % #"./(.*)" "$1") all-files )
; file-set (set files)
;note that using some here means that if both are present, hg is
;ignored:
vcs-systems (set (vector (some #{".git" ".hg"} files))) ]
{:files files
:extensions (frequencies (map get-extension files ))
:path (System/getProperty "user.dir")
:untracked-files (gather-untracked vcs-systems)
:readme (if-let [filename (some #{"README.md" "README.txt" "README.mkd"} files)] (slurp filename) "")
}) )
(defn color [color string]
(let [color-sequence (case color
:green "\u001B[32m"
:yellow "\u001B[33m"
:blue "\u001B[34m"
:red "\u001B[31m"
:cyan "\u001B[36m"
:magenta "\u001B[35m"
)
reset "\u001B[m" ]
(str color-sequence string reset)) )
(defn check-vcs [project]
(let [{files :files } project]
(boolean (some #{".git" ".hg"} files)) ))
(defn check-readme [project]
(let [{files :files } project]
(boolean (some #{"README.md" "README.txt" "README.mkd" "README"} files)) ))
(defn check-untracked [project]
(let [{untracked :untracked-files } project]
(= (count untracked) 0)) )
(defn check-taskpaper [project]
(let [{extensions :extensions files :files } project]
(or (>= (get ".taskpaper" extensions 0) 1)
(some #{"TODO" "TODO.txt" } files))))
(defn check-readme-placeholders [project]
(= (count (re-find #"(FIXME|TODO)" (:readme project) )) 0))
(def checks [{:name "Project is checked into revision control"
:description ""
:function check-vcs
:level :error
:follow-up "Initialize a repository." }
{:name "Always True"
:function #(or true %)
:level :error
:follow-up "This is a bug." }
{:name "All files are tracked or ignored"
:description ""
:function check-untracked
:level :warning
:follow-up "Commit or ignore files from 'hg st -u' or 'git ls-files --others --exclude-standard'." }
{:name "Project has a todo file"
:function check-taskpaper
:description ""
:level :suggestion
:follow-up "Add a todo file using Taskpaper." }
{:name "Has README"
:function check-readme
:description "Readme exists"
:level :suggestion
:follow-up "Add a README." }
{:name "README has no placeholders"
:function check-readme-placeholders
:description "No placeholders in README"
:level :error
:follow-up "Address placeholders or convert them to tasks."
} ])
(defn perform-check [check project]
(let [{check-name :name function :function follow-up :follow-up
level :level } check
result (function project)
false-color (case level
:suggestion :blue
:warning :yellow
:error :red
:red)
prefix (case level
:suggestion "Suggested follow-up"
:warning "Recommended follow-up"
:error "Required follow-up"
"Follow-up" )]
{:name check-name
:result result
:output (if result
(color :green (str "✔" check-name "…passed!"))
(str (color false-color (str "❌" check-name "…failed!")) "\n\t" prefix " Follow up: " follow-up))}))
(defn -main
"Run checks."
[& args]
(try
(let [project-info (gather-project-info)]
(doseq [check checks]
(println (:output (perform-check check project-info )))))
(catch Exception ex
(.printStackTrace ex)
(str "caught exception: " (.getMessage ex)))
(finally (shutdown-agents) )) )