diff --git a/API.md b/API.md index a9d525e..b0416ba 100644 --- a/API.md +++ b/API.md @@ -261,7 +261,7 @@ Returns creation time as FileTime. ``` Returns current working directory as path -
+ ## `delete` ``` clojure @@ -362,7 +362,7 @@ If [[`path`](#babashka.fs/path)](#babashka.fs/path) begins with a tilde (`~`), e directory. This is (naively) assumed to be a directory with the same name as the user relative to the parent of the current value of `user.home`. - + ## `extension` ``` clojure @@ -468,7 +468,7 @@ Returns true if f is hidden. With no arguments, returns the current value of the `user.home` system property. If a `user` is passed, returns that user's home directory as found in the parent of home with no args. - + ## `instant->file-time` ``` clojure @@ -837,7 +837,7 @@ Returns `java.io.tmpdir` property as path. ``` Returns path as string with Unix-style file separators (`/`). - + ## `unzip` ``` clojure @@ -866,7 +866,7 @@ Updates the contents of text file [`path`](#babashka.fs/path) using `f` applied Options: * `:charset` - charset of file, default to "utf-8" - + ## `walk-file-tree` ``` clojure @@ -912,7 +912,7 @@ Returns every Path to `program` found in ([`exec-paths`](#babashka.fs/exec-paths ``` Returns true if OS is Windows. - + ## `with-temp-dir` ``` clojure @@ -927,7 +927,7 @@ Evaluate body with binding-name bound to a temporary directory. and will be removed with [`delete-tree`](#babashka.fs/delete-tree) on exit from the scope. `options` is a map with the keys as for create-temp-dir. - + ## `writable?` ``` clojure @@ -959,7 +959,7 @@ Writes `bytes` to [`path`](#babashka.fs/path) via `java.nio.file.Files/write`. (fs/write-bytes f (.getBytes (String. "foo"))) ;; overwrites + truncates or creates new file (fs/write-bytes f (.getBytes (String. "foo")) {:append true}) ``` - + ## `write-lines` ``` clojure @@ -979,7 +979,7 @@ Writes `lines`, a seqable of strings to [`path`](#babashka.fs/path) via `java.ni * `:write` (default `true`) * `:append` (default `false`) * or any `java.nio.file.StandardOption`. - + ## `xdg-cache-home` ``` clojure @@ -990,7 +990,7 @@ Writes `lines`, a seqable of strings to [`path`](#babashka.fs/path) via `java.ni Path representing the base directory relative to which user-specific non-essential data files should be stored as described in the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html). Returns path based on the value of env-var `XDG_CACHE_HOME` (if set), else `(fs/path (fs/home) ".cache")`. - + ## `xdg-config-home` ``` clojure @@ -1001,7 +1001,7 @@ Path representing the base directory relative to which user-specific non-essenti Path representing the base directory relative to which user-specific configuration files should be stored as described in the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html). Returns path based on the value of env-var `XDG_CONFIG_HOME` (if set), else `(fs/path (fs/home) ".config")`. - + ## `xdg-data-home` ``` clojure @@ -1012,7 +1012,7 @@ Path representing the base directory relative to which user-specific configurati Path representing the base directory relative to which user-specific data files should be stored as described in the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html). Returns path based on the value of env-var `XDG_DATA_HOME` (if set), else `(fs/path (fs/home) ".local" "share")`. - + ## `xdg-state-home` ``` clojure @@ -1023,16 +1023,21 @@ Path representing the base directory relative to which user-specific data files Path representing the base directory relative to which user-specific state files should be stored as described in the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html). Returns path based on the value of env-var `XDG_STATE_HOME` (if set), else `(fs/path (fs/home) ".local" "state")`. - + ## `zip` ``` clojure (zip zip-file entries) -(zip zip-file entries _opts) +(zip zip-file entries opts) ``` Zips entry or entries into zip-file. An entry may be a file or directory. Directories are included recursively and their names are preserved in the zip file. Currently only accepts relative entries. - + + Options: + * `:root`: directory which will be elided in zip. E.g.: `(fs/zip ["src"] {:root "src"})` + * `:path-fn`: a single-arg function from file system path to zip entry path. + + diff --git a/CHANGELOG.md b/CHANGELOG.md index 32202a7..52d9b93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,15 @@ For a list of breaking changes, check [here](#breaking-changes). Babashka [fs](https://github.com/babashka/fs): file system utility library for Clojure +## v0.3.17 (2023-02-28) + +- #67: add `:root` and `:path-fn` options to `fs/zip` + ## v0.2.16 (2023-02-08) - [#89](https://github.com/babashka/fs/issues/89): change default in `walk-file-tree` from throwing to continue-ing, which works better for `glob`, `match`. You can still throw by providing your own - `visit-file-failed` function. + `visit-file-failed` function. ## v0.2.15 diff --git a/project.clj b/project.clj index 7ccb954..e896fec 100644 --- a/project.clj +++ b/project.clj @@ -1,4 +1,4 @@ -(defproject babashka/fs "0.2.16" +(defproject babashka/fs "0.3.17" :description "Babashka file system utilities." :url "https://github.com/babashka/fs" :scm {:name "git" diff --git a/src/babashka/fs.cljc b/src/babashka/fs.cljc index ad307a8..cc1d1ff 100644 --- a/src/babashka/fs.cljc +++ b/src/babashka/fs.cljc @@ -930,16 +930,11 @@ ;; partially borrowed from tools.build (defn- add-zip-entry - [^ZipOutputStream output-stream ^File file] + [^ZipOutputStream output-stream ^File file fpath] (let [dir (.isDirectory file) attrs (Files/readAttributes (as-path file) BasicFileAttributes (->link-opts [])) - fpath (str file) - fpath (if (and dir (not (.endsWith fpath "/"))) (str fpath "/") fpath) - fpath (str/replace fpath \\ \/) ;; only use unix-style paths in jars - entry (doto (ZipEntry. fpath) - ;(.setSize (.size attrs)) - ;(.setLastAccessTime (.lastAccessTime attrs)) + entry (doto (ZipEntry. (str fpath)) (.setLastModifiedTime (.lastModifiedTime attrs)))] (.putNextEntry output-stream entry) (when-not dir @@ -950,29 +945,47 @@ ;; partially borrowed from tools.build (defn- copy-to-zip - [^ZipOutputStream jos f] + [^ZipOutputStream jos f path-fn] (let [files (file-seq (file f))] (run! (fn [^File f] - (add-zip-entry jos f)) + (let [dir (.isDirectory f) + fpath (str f) + fpath (if (and dir (not (.endsWith fpath "/"))) (str fpath "/") fpath) + ;; only use unix-style paths in jars + fpath (str/replace fpath \\ \/) + fpath (path-fn fpath)] + (when-not (str/blank? fpath) + (add-zip-entry jos f fpath)))) files))) ;; partially borrowed from tools.build (defn zip "Zips entry or entries into zip-file. An entry may be a file or directory. Directories are included recursively and their names are - preserved in the zip file. Currently only accepts relative entries." + preserved in the zip file. Currently only accepts relative entries. + + Options: + * `:root`: directory which will be elided in zip. E.g.: `(fs/zip [\"src\"] {:root \"src\"})` + * `:path-fn`: a single-arg function from file system path to zip entry path. + " ([zip-file entries] (zip zip-file entries nil)) - ([zip-file entries _opts] - (let [entries (if (string? entries) + ([zip-file entries opts] + (let [entries (if (or (string? entries) + (instance? File entries) + (instance? Path entries)) [entries] - entries)] + entries) + path-fn (or (:path-fn opts) + (when-let [root (:root opts)] + #(str/replace % (re-pattern (str "^" (java.util.regex.Pattern/quote root) "/")) "")) + identity)] (assert (every? relative? entries) "All entries must be relative") (with-open [zos (ZipOutputStream. (FileOutputStream. (file zip-file)))] (doseq [zpath entries] - (copy-to-zip zos zpath)))))) + (copy-to-zip zos zpath path-fn)))))) ;;;; End zip diff --git a/test/babashka/fs_test.clj b/test/babashka/fs_test.clj index d0c977e..eca89b5 100644 --- a/test/babashka/fs_test.clj +++ b/test/babashka/fs_test.clj @@ -474,7 +474,18 @@ (fs/zip zip-file ["src" "README.md"]) (fs/unzip zip-file td-out) (is (fs/exists? (fs/file td-out "src"))) - (is (fs/exists? (fs/file td-out "README.md")))))) + (is (fs/exists? (fs/file td-out "README.md"))))) + (testing "Elide parent dir" + (let [td (fs/create-temp-dir) + td-out (fs/path td "out") + zip-file (fs/path td "foo.zip")] + (fs/zip zip-file "src" {:root "src"}) + (fs/unzip zip-file td-out) + (is (not (fs/exists? (fs/file td-out "src")))) + (is (fs/exists? (fs/file td-out "babashka"))) + (is (fs/directory? (fs/file td-out "babashka"))) + (is (fs/exists? (fs/file td-out "babashka" "fs.cljc"))) + (is (not (fs/directory? (fs/file td-out "babashka" "fs.cljc"))))))) (deftest with-temp-dir-test (let [capture-dir (volatile! nil)]