Created
January 29, 2016 21:08
-
-
Save arichiardi/514ca2403d23e58c8853 to your computer and use it in GitHub Desktop.
Some util function for boot/files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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