Skip to content

Commit 17fb719

Browse files
committed
init
0 parents  commit 17fb719

File tree

12 files changed

+269
-0
lines changed

12 files changed

+269
-0
lines changed

.clj-kondo/babashka/fs/config.edn

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{:lint-as {babashka.fs/with-temp-dir clojure.core/let}}
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{:config-in-call {nextjournal.clerk.viewer/->viewer-fn {:linters {:unresolved-symbol {:exclude [clj->js]}
2+
:unresolved-namespace {:exclude [js]}}}
3+
nextjournal.clerk.viewer/->viewer-eval {:linters {:unresolved-symbol {:exclude [clj->js]}
4+
:unresolved-namespace {:exclude [js]}}}}
5+
:hooks {:analyze-call {nextjournal.clerk.viewer/->viewer-fn nextjournal.clerk.viewer/->viewer-fn
6+
nextjournal.clerk.viewer/->viewer-eval nextjournal.clerk.viewer/->viewer-fn}}
7+
:lint-as {nextjournal.clerk/defcached clojure.core/def}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
(ns nextjournal.clerk.viewer
2+
(:require [clj-kondo.hooks-api :as api]
3+
[clojure.string :as str]))
4+
5+
(defn ->viewer-fn [{:keys [node]}]
6+
(let [[name-node quoted-node] (:children node)
7+
quoted-tag (api/tag quoted-node)]
8+
(when (= :quote quoted-tag)
9+
(let [quoted-node {:tag :syntax-quote
10+
:children (:children quoted-node)}
11+
fn-node (first (:children quoted-node))]
12+
{:node fn-node}))))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{:lint-as
2+
{rewrite-clj.zip/subedit-> clojure.core/->
3+
rewrite-clj.zip/subedit->> clojure.core/->>
4+
rewrite-clj.zip/edit-> clojure.core/->
5+
rewrite-clj.zip/edit->> clojure.core/->>}}

.clj-kondo/taoensso/encore/config.edn

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{:hooks {:analyze-call {taoensso.encore/defalias taoensso.encore/defalias}}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
(ns taoensso.encore
2+
(:require
3+
[clj-kondo.hooks-api :as hooks]))
4+
5+
(defn defalias [{:keys [node]}]
6+
(let [[sym-raw src-raw] (rest (:children node))
7+
src (if src-raw src-raw sym-raw)
8+
sym (if src-raw
9+
sym-raw
10+
(symbol (name (hooks/sexpr src))))]
11+
{:node (with-meta
12+
(hooks/list-node
13+
[(hooks/token-node 'def)
14+
(hooks/token-node (hooks/sexpr sym))
15+
(hooks/token-node (hooks/sexpr src))])
16+
(meta src))}))

.gitignore

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
.calva/output-window/
2+
.classpath
3+
.clj-kondo/.cache
4+
.cpcache
5+
.eastwood
6+
.factorypath
7+
.hg/
8+
.hgignore
9+
.java-version
10+
.lein-*
11+
.lsp/.cache
12+
.lsp/sqlite.db
13+
.nrepl-history
14+
.nrepl-port
15+
.project
16+
.rebel_readline_history
17+
.settings
18+
.socket-repl-port
19+
.sw*
20+
.vscode
21+
*.class
22+
*.jar
23+
*.swp
24+
*~
25+
/checkouts
26+
/classes
27+
/target
28+
/cas
29+
/tags
30+
/manifests
31+
.clerk
32+
.direnv/
33+
/result
34+
.secrets.env

bb.edn

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{:paths ["src"]
2+
:deps {deps/deps {:local/root "."}}
3+
4+
:bbin/bin {clerk-cas {:main-opts ["-m" "nextjournal.cas-client.cli"]
5+
:tool true}}
6+
}

deps.edn

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{:paths ["src" "resources"]
2+
:deps {org.clojure/clojure {:mvn/version "1.11.1"}
3+
http-kit/http-kit {:mvn/version "2.6.0"}
4+
mvxcvi/multihash {:git/url "https://github.com/borkdude/clj-multihash"
5+
:git/sha "df9a134b17a3f53efb34265098ee0ac1f2f01626"}
6+
com.taoensso/timbre {:mvn/version "6.0.4"}
7+
babashka/fs {:mvn/version "0.2.12"}
8+
cheshire/cheshire {:mvn/version "5.11.0"}
9+
babashka/cli {:git/url "https://github.com/babashka/cli"
10+
:git/sha "9d83d70c5c030a075bb82c0eb41b6281ae0104d8"}
11+
ring/ring-codec {:mvn/version "1.2.0"}}
12+
:aliases
13+
{:dev {:extra-paths ["dev"]
14+
:extra-deps {io.github.nextjournal/clerk {:mvn/version "0.12.707"}
15+
org.clojars.abhinav/snitch {:mvn/version "0.1.13"}}}
16+
:neil {:project {:name cas-client/cas-client}}}}

src/nextjournal/cas_client/api.clj

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
(ns nextjournal.cas-client.api
2+
(:require [clojure.java.io :as io]
3+
[clojure.string :as str]
4+
[nextjournal.cas-client.hashing :as h]
5+
[cheshire.core :as json]
6+
[babashka.fs :as fs]
7+
[org.httpkit.client :as http]
8+
[org.httpkit.sni-client :as sni-client]
9+
[ring.util.codec :as ring-codec]))
10+
11+
;; Change default client for the whole application:
12+
;; Needed for TLS connections using SNI
13+
(alter-var-root #'org.httpkit.client/*default-client* (fn [_] sni-client/default-client))
14+
15+
(defonce ^:dynamic *cas-host* "https://cas.clerk.garden")
16+
(defonce ^:dynamic *tags-host* "https://storage.clerk.garden")
17+
18+
(defn tag-put [{:keys [host auth-token namespace tag target]
19+
:or {host *tags-host*}}]
20+
(-> @(http/post (str host "/" namespace "/" tag)
21+
{:headers {"auth-token" auth-token
22+
"content-type" "plain/text"}
23+
:body target})))
24+
25+
(defn tag-url [{:keys [host namespace tag path]
26+
:or {host *tags-host*}}]
27+
(str host "/" namespace "/" tag (when path (str "/" path))))
28+
29+
(defn tag-get [{:as opts}]
30+
(-> @(http/get (tag-url opts))
31+
:body))
32+
33+
(defn tag-exists? [opts]
34+
(-> @(http/head (tag-url opts))
35+
:status
36+
(= 200)))
37+
38+
(defn cas-url [{:as opts
39+
:keys [host key namespace tag]
40+
:or {host *cas-host*}}]
41+
(str host "/" key
42+
(let [query-params (ring-codec/form-encode (select-keys opts [:filename :content-type]))]
43+
(when (not (str/blank? query-params))
44+
(str "?" query-params)))))
45+
46+
(defn cas-exists? [opts]
47+
(-> @(http/head (cas-url opts))
48+
:status
49+
(= 200)))
50+
51+
(defn cas-get [opts]
52+
(-> @(http/get (cas-url opts))
53+
:body
54+
slurp))
55+
56+
(defn cas-put [{:keys [path host auth-token namespace tag]
57+
:or {host *cas-host*}}]
58+
(let [f (io/file path)
59+
prefix (if (fs/directory? path) (str f "/") "")
60+
files (->> (file-seq f)
61+
(filter fs/regular-file?)
62+
(map (fn [f] (let [path (str (fs/path f))
63+
hash (with-open [s (io/input-stream f)]
64+
(h/hash s))]
65+
{:path (str/replace path prefix "")
66+
:hash hash
67+
:filename (fs/file-name path)
68+
:content f}))))
69+
{files-to-upload false files-already-uploaded true} (group-by #(cas-exists? {:key (:hash %)}) files)
70+
multipart (concat (map (fn [{:keys [path filename content]}]
71+
{:name path
72+
:filename filename
73+
:content content}) files-to-upload)
74+
(map (fn [{:keys [path filename hash]}]
75+
{:name path
76+
:filename filename
77+
:content-type "application/clerk-cas-hash"
78+
:content hash}) files-already-uploaded))
79+
{:as res :keys [status body]} @(http/post
80+
host
81+
(cond-> {:multipart multipart}
82+
tag (merge {:query-params {:tag (str namespace "/" tag)}
83+
:headers {"auth-token" auth-token}})))]
84+
(if (= 200 status)
85+
(-> body
86+
(json/parse-string))
87+
res)))
88+
89+
(defn put [{:as opts
90+
:keys [cas-host tags-host auth-token namespace tag target path]
91+
:or {cas-host *cas-host*
92+
tags-host *tags-host*}}]
93+
(assert (not (every? some? #{target path}))
94+
"Set either target or path")
95+
(cond (some? target) (tag-put opts)
96+
(some? path) (cas-put opts)))
97+
98+
(defn get [{:as opts
99+
:keys [cas-host tags-host namespace tag path]
100+
:or {cas-host *cas-host*
101+
tags-host *tags-host*}}]
102+
(if (some? tag)
103+
(tag-get opts)
104+
(cas-get opts)))
105+
106+
(comment
107+
(def r (cas-put {:path "test/resources/foo"}))
108+
(def r (cas-put {:path "test/resources/foo.bak"
109+
:namespace "Sohalt"
110+
:tag "test-tag"
111+
:auth-token (System/getenv "GITHUB_TOKEN")}))
112+
(def m-path (get r "manifest-id"))
113+
(def m-path (str "/tree/" (get r "manifest-id")))
114+
(def m (get r "manifest"))
115+
(def k (get m "bar/baz.txt")))

src/nextjournal/cas_client/cli.clj

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
(ns nextjournal.cas-client.cli
2+
(:require [babashka.cli :as cli]
3+
[nextjournal.cas-client.api :as api]
4+
[clojure.string :as str]))
5+
6+
(declare cmds)
7+
8+
(def spec
9+
{:key {:desc "CAS hash"}
10+
:tag {:desc "Tag name"}
11+
:target {:desc "Tag target"}
12+
:namespace {:desc "Tag namespace"}
13+
:path {:desc "Filesystem path"
14+
:coerce :string}
15+
:filename {:desc "Filename to save as"}
16+
:content-type {:desc "Content-Type to request"}})
17+
18+
(defn print-help [_]
19+
(let [available-cmds (->> cmds
20+
(map #(str/join " " (:cmds %)))
21+
(filter (comp not str/blank?)))]
22+
(println (str/join "\n" (concat ["available commands:"
23+
""]
24+
available-cmds)))))
25+
26+
(defn- wrap-with-output-reporting [f]
27+
(fn [x]
28+
(let [res (f x)]
29+
(if-let [error (:error res)]
30+
(binding [*out* *err*]
31+
(println error))
32+
(println res)))))
33+
34+
(defn- wrap [f]
35+
(fn [{:keys [opts]}]
36+
(let [{:keys [deploy-conf]} opts]
37+
((-> f
38+
(wrap-with-output-reporting)) opts))))
39+
40+
(def cmds [{:cmds ["put"] :fn (wrap api/put)}
41+
{:cmds ["get"] :fn (wrap api/get)}
42+
{:cmds ["help"] :fn print-help}
43+
{:cmds [] :fn print-help}])
44+
45+
(defn -main [& _args]
46+
(cli/dispatch cmds
47+
*command-line-args*
48+
{:spec spec
49+
:exec-args {:deps-file "deps.edn"}})
50+
nil)
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
(ns nextjournal.cas-client.hashing
2+
(:require [multihash.core :as multihash]
3+
[multihash.digest :as digest]))
4+
5+
(defn hash [stream]
6+
(str (multihash/base58 (digest/sha2-512 stream))))

0 commit comments

Comments
 (0)