Skip to content

Instantly share code, notes, and snippets.

@arichiardi
Created January 29, 2016 21:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save arichiardi/514ca2403d23e58c8853 to your computer and use it in GitHub Desktop.
Save arichiardi/514ca2403d23e58c8853 to your computer and use it in GitHub Desktop.
Some util function for boot/files
(ns replumb.boot.util
(:require [clojure.java.io :as io]
[clojure.string :as string]
[boot.core :as core]
[boot.pod :as pod]
[boot.util :as util]
[clj-jgit.porcelain :as jgit])
(:import java.io.File
java.nio.file.Files
java.nio.file.attribute.FileAttribute
org.eclipse.jgit.lib.AnyObjectId
org.eclipse.jgit.treewalk.TreeWalk))
(defn tmp-dir!
"Create a temp folder and returns its java.nio.file.Path."
[]
(.toFile
(Files/createTempDirectory nil (make-array FileAttribute 0))))
(defn purge-dir!
[^File file]
(core/empty-dir! file)
(.delete file))
(defn pod-env
"Analogous to boot.core/get-env but wrapped in a custom pod"
[pod]
(pod/with-eval-in pod pod/env))
(defn git-files
"Returns a non-lazy sequence of java.io.File(s) in the repository at repo-path.
The 1-arity version retrieves from HEAD whereas the 2-arity accept
an (commit-ish) id. For the id string format see:
http://download.eclipse.org/jgit/site/3.7.0.201502260915-r/apidocs/index.html
The 3-arity version accepts a repo-absolute? boolean that when true
merges in repo-path and creates files with absolute path.
In case of invalid/unresolved commit id it returns an empty sequence."
([repo-path]
(git-files repo-path "HEAD" false))
([repo-path id]
(git-files repo-path id false))
([repo-path id repo-absolute?]
{:pre [(string? repo-path)]}
(jgit/with-repo repo-path
(if-let [^AnyObjectId obj-id (some-> (.getRepository repo)
(.resolve id))]
(let [commit (.parseCommit rev-walk obj-id)
twalk (doto (TreeWalk. (.getRepository repo))
(.addTree (.getTree commit))
(.setRecursive true))
files (->> (loop [next? ^boolean (.next twalk)
files []]
(if-not next?
files
(recur (.next twalk)
(conj files (if-not repo-absolute?
(io/file (.getPathString twalk))
(io/file repo-path (.getPathString twalk))))))))]
(.release twalk)
(.release rev-walk)
files)
[]))))
(defn git-delete-repo
"Delete a repository"
([repo-path]
{:pre [(string? repo-path)]}
(jgit/with-repo repo-path
(-> (.getRepository repo)
.close))))
(defn new-name
"Rename the name part of a java.io.File. Return a new java.io.File."
[^File file new-name]
(File. (.getParentFile file) new-name))
(defn add-suffix
"Append a suffix with (str name suffix \".\" ext) to the input
java.io.File. Return a new java.io.File."
[^File file suffix]
(let [[name ext] (string/split (.getName file) #"\." 2)]
(File. (.getParentFile file)
(str name suffix (when ext ".") ext))))
(defn relativize)
(defn rename-file!
"Rename a file if the regex matches against the .getAbsolutePath of
the first of the java.io.File in files (and only the first).
The last parameter, parent, is a java.io.File which represent the
parent path of the files, that in this case are considered
relative. Beware that rename relative files without parent will not
work (you will see a warning though).
The new java.io.File path will be taken from (new-file-fn found-file).
The function will return a list of files.
There are three non-exceptional results of this computation:
1) file-regex does not match, rename-file! returns nil
2) file-regex matches but rename fails, it returns the found file
3) file regex matches and rename succeeds, it returns the renamed file"
([files file-regex new-file-fn]
(rename-file! files file-regex new-file-fn nil))
([files file-regex new-file-fn ^File parent]
(let [find-pred #(re-find file-regex (.getAbsolutePath %))]
(if-let [file (first (filter find-pred files))]
(let [new-file (new-file-fn file)
abs-file (if-not parent
file
(-> (.toPath parent) (.resolve (.toPath file)) .toFile))
abs-new-file (if-not parent
new-file
(-> (.toPath parent) (.resolve (.toPath new-file)) .toFile))
renamed? (.renameTo abs-file abs-new-file)]
(if renamed?
(do (util/dbug "New file: %s\n" (.getAbsolutePath abs-new-file))
new-file)
(do (util/warn "Could not rename file\n")
file)))
(do (util/dbug "No file found for regex: %s\n" (str file-regex))
nil)))))
(def debug? (atom false))
(def clone-count (atom 0))
(defn closure-add-resource-map!
"Clone in tmp (a string!), filter and organizes the Google Closure
Library source.
It accepts an empty tmp dir the uri of the git repo, remote and
branch (both optional and defaulting to origin/master).
Very side-effectful, it returns the map to be passed as parameter to
for boot.core/add-resource."
[tmp-str uri remote branch]
(util/dbug "Cloning %s %s %s...\n" uri remote branch)
(jgit/git-clone-full uri tmp-str remote branch)
(util/dbug "Filtering the files in the repository...\n")
(let [files (->> (git-files tmp-str)
(rename-file! #"LICENSE$"
#(add-suffix % "-closure-library"))
(core/by-re [#"closure\/goog\/.*.js$"
#"third_party\/closure\/goog\/.*.js$"])
(core/not-by-re [#"third_party\/closure\/.*base.js$"
#"third_party\/closure\/.*deps.js$"]))]
(util/dbug "closure-library (including third-party): %s files\n" (count files))
files))
(defn cljs-add-resource-map!
"Clone in tmp (a string!), filter and organizes the ClojureScript
source.
It accepts an empty tmp dir the uri of the git repo, remote and
branch (both optional and defaulting to origin/master).
Very side-effectful, it returns the map to be passed as parameter to
for boot.core/add-resource."
[tmp uri remote branch]
(let [tmp-str (.getAbsolutePath tmp)]
(util/dbug "Tmp-dir is %s\n" tmp-str)
(if-not @debug?
(do (util/dbug "Cloning %s %s %s...\n" uri remote branch)
(jgit/git-clone-full uri tmp-str remote branch))
(do
(reset! util/*verbosity* 3)
(when (zero? @clone-count)
(util/dbug "Cloning %s %s %s...\n" uri remote branch)
(jgit/git-clone-full uri tmp-str remote branch)
(swap! clone-count inc))))
(let [files (git-files tmp-str)
new-license (rename-file! files
#"epl-v10.*$"
#(-> %
(new-name "LICENSE.html")
(add-suffix "-clojurescript"))
tmp)
files (->> files
(core/by-re [#"src\/main\/cljs\/.*\.(clj|cljs)$"])
(cons new-license)
(remove nil?))]
(util/dbug "clojurescript: %s files\n" (count files))
(when-not @debug?
(purge-dir! tmp))
files)))
(comment
(testing "replumb.boot.util/rename-file!"
;; TODO the missing case would require forcing a failing rename (simulate
;; read-only permissions or moved file), maybe some day
(let [files (util/git-files *repo-dir*)]
(is (= nil (util/rename-file! #"asdasd_###!" (fn [_] _) files)) "If regex does not match it should return untouched files")
(is (= "CHANGELOG.txt" (.getName (util/rename-file! #"CHANGELOG.md" #(util/new-name % "CHANGELOG.txt") files))) "If regex matches rename should succeed")))
(testing "replumb.boot.util/new-name"
(let [file (java.io.File. "foo/" "bar")]
(is (= (.getParent file) (.getParent (util/new-name file "baz"))) "It should preserve the parent path"))
(is (re-find #"baz" (.getName (util/new-name (java.io.File. "foo/" "bar") "baz"))) "If file has extension we should not override it"))
(testing "replumb.boot.util/add-suffix"
(let [file (java.io.File. "foo/bar/" "file.cljs")]
(is (= (.getParent file) (.getParent (util/add-suffix file "-suffix"))) "It should preserve the parent path"))
(is (re-find #".*\.cljs$" (.getName (util/add-suffix (java.io.File. "foo/bar/" "file.cljs") "-suffix"))) "If file has extension we should not override it")
(is (re-find #".*-suffix$" (.getName (util/add-suffix (java.io.File. "foo/bar/" "file") "-suffix"))) "If file does not have extension, suffix is last"))
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment