Skip to content

Commit fc760c2

Browse files
committed
Initial commit
0 parents  commit fc760c2

11 files changed

+528
-0
lines changed

.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/.cpcache
2+
/.idea
3+
/target
4+
/.nrepl-port
5+
/.rebel_readline_history

deps.edn

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{:deps {org.clojure/clojurescript {:mvn/version "1.10.339"}
2+
com.bhauman/figwheel-main {:mvn/version "0.1.5"}
3+
com.bhauman/rebel-readline-cljs {:mvn/version "0.1.4"}
4+
cider/piggieback {:mvn/version "0.3.8"}}
5+
:paths ["src" "target" "resources"]
6+
:aliases {:build-dev {:main-opts ["-m" "figwheel.main" "-b" "dev" "-r"]}}}

dev.cljs.edn

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
{:main cache.core
2+
:target :nodejs}

figwheel-main.edn

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{:watch-dirs ["src"]}

readme.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Magento Cache Cleaner
2+
3+
# Work In Progress!
4+
5+
Small filewatcher to automate selectively cleaning affected cache segments in the Magento 2 file cache backend during development.
6+

src/cache/cache.cljs

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
(ns cache.cache
2+
(:require [cache.filestorage :as storage]
3+
[file.system :as file]
4+
[cache.log :as log]
5+
[clojure.string :as string]))
6+
7+
(defn cachetype->tag [type]
8+
(or
9+
(get {"collections" "COLLECTION_DATA"
10+
"config_webservices" "WEBSERVICE"
11+
"layout" "LAYOUT_GENERAL_CACHE_TAG"
12+
"full_page" "FPC"
13+
"config_integration_consolidated" "INTEGRATION_CONSOLIDATED"
14+
"config_integration_api" "INTEGRATION_API_CONFIG"
15+
"config_integration" "INTEGRATION"} type)
16+
(string/upper-case type)))
17+
18+
(defn- match-name? [name-pattern file]
19+
(cond
20+
(regexp? name-pattern) (re-find name-pattern file)
21+
:else (= name-pattern (file/basename file))))
22+
23+
(defn- file-fingerprint-fn [name-pattern content-head-pattern]
24+
(fn [file]
25+
(and (match-name? name-pattern file)
26+
(file/exists? file)
27+
(or (nil? content-head-pattern)
28+
(re-find content-head-pattern (file/head file))))))
29+
30+
(defn- tuples->fingerprint-fns [type tuples]
31+
(reduce (fn [acc [filename content]]
32+
(assoc acc (file-fingerprint-fn filename content) type)) {} tuples))
33+
34+
(defn- config-filetypes []
35+
(let [t [["di.xml" #"urn:magento:framework:ObjectManager/etc/config\.xsd"]
36+
["crontab.xml" #"urn:magento:module:Magento_Cron:etc/crontab\.xsd"]
37+
["events.xml" #"urn:magento:framework:Event/etc/events\.xsd"]
38+
["extension_attributes.xml" #"urn:magento:framework:Api/etc/extension_attributes\.xsd"]
39+
["routes.xml" #"urn:magento:framework:App/etc/routes\.xsd"]
40+
["widget.xml" #"urn:magento:module:Magento_Widget:etc/widget\.xsd"]
41+
["product_types.xml" #"urn:magento:module:Magento_Catalog:etc/product_types\.xsd"]
42+
["product_options.xml" #"urn:magento:module:Magento_Catalog:etc/product_options\.xsd"]
43+
["payment.xml" #"urn:magento:module:Magento_Payment:etc/payment\.xsd"]
44+
["search_request.xml" #"urn:magento:framework:Search/etc/search_request\.xsd"]
45+
["config.xml" #"urn:magento:module:Magento_Store:etc/config\.xsd"]
46+
[#"/ui_component/.+\.xml$" #"urn:magento:module:Magento_Ui:etc/ui_configuration\.xsd"]
47+
["menu.xml" #"urn:magento:module:Magento_Backend:etc/menu\.xsd"]
48+
["acl.xml" #"urn:magento:framework:Acl/etc/acl\.xsd"]
49+
["indexer.xml" #"urn:magento:framework:Indexer/etc/indexer\.xsd"]]]
50+
(tuples->fingerprint-fns ::config t)))
51+
52+
(defn- layout-filetypes []
53+
(let [t [[#"/layout/.+\.xml$" #"<page [^>]+\"urn:magento:framework:View/Layout/etc/page_configuration\.xsd\""]]]
54+
(tuples->fingerprint-fns ::layout t)))
55+
56+
(defn- translation-filetypes []
57+
(let [t [[#"/i18n/.+\.csv$" #".+,.+"]]]
58+
(tuples->fingerprint-fns ::translation t)))
59+
60+
(defn- template-filetypes []
61+
(let [t [[#"/templates/.+\.phtml"]]]
62+
(tuples->fingerprint-fns ::template t)))
63+
64+
(def file->type
65+
(merge (config-filetypes)
66+
(layout-filetypes)
67+
(translation-filetypes)
68+
(template-filetypes)))
69+
70+
(defn- magefile->filetype [file]
71+
(reduce (fn [_ [filetype? type]]
72+
(when (filetype? file) (reduced type))) nil file->type))
73+
74+
(defn tag->ids [tag]
75+
(if (file/exists? (storage/tag->filepath tag))
76+
(storage/tag->ids tag)
77+
[]))
78+
79+
(defn id->file [id]
80+
(storage/id->filepath id))
81+
82+
(def filetype->cachetypes
83+
{::config ["config"]
84+
::translation ["translate"]
85+
::layout ["layout" "full_page"]
86+
::template ["block_html" "full_page"]})
87+
88+
(defn magefile->cachetypes [file]
89+
(let [filetype (magefile->filetype file)]
90+
(get filetype->cachetypes filetype [])))
91+
92+
(defn- rm-tagfile [tag]
93+
(let [file (storage/tag->filepath tag)]
94+
(when (file/exists? file)
95+
(file/rm file))))
96+
97+
(defn- clean
98+
([] (storage/clean-all))
99+
([type]
100+
(let [tag (cachetype->tag type)]
101+
(log/debug "Cleaning tag" tag)
102+
(run! storage/delete (tag->ids tag))
103+
(rm-tagfile tag)))
104+
([type & types]
105+
(run! #(clean %) (into [type] types))))
106+
107+
(defn clean-cache-types [cache-types]
108+
(apply log/notice "Cleaning cache type(s)" cache-types)
109+
(when (or (empty? cache-types) (not= ["full_page"] cache-types))
110+
(log/debug "Using cache dir var/cache...")
111+
(binding [storage/*cachedir* "var/cache/"]
112+
(let [cache-types (remove #(= "full_page" %) cache-types)]
113+
(apply clean cache-types))))
114+
(when (or (empty? cache-types) (some #{"full_page"} cache-types))
115+
(log/debug "Using cache dir var/page_cache...")
116+
(binding [storage/*cachedir* "var/page_cache/"]
117+
(clean))))

src/cache/core.cljs

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
(ns cache.core
2+
(:require [cache.cache :as cache]
3+
[cache.filestorage :as storage]
4+
[file.system :as fs]
5+
[cache.log :as log]
6+
[magento.watcher :as watcher]))
7+
8+
(defn has-switch?
9+
"Return true is one of the given opts strings is contained within the args seq"
10+
[switches args]
11+
(let [matches-switch (set (if (string? switches) #{switches} switches))]
12+
(reduce (fn [x arg]
13+
(when (matches-switch arg) (reduced true))) nil args)))
14+
15+
(defn find-arg
16+
"If args contains one of the given argument switches, returns the next arg in
17+
the seq, otherwise returns nil."
18+
[flags args]
19+
(let [matches-flag (set (if (string? flags) #{flags} flags))]
20+
(let [r (reduce (fn [x arg]
21+
(cond
22+
(matches-flag arg) true
23+
x (reduced arg))) nil args)]
24+
(if (= true r) nil r))))
25+
26+
(defn find-basedir-in-path [dir]
27+
(let [dir (fs/realpath dir)]
28+
(if (fs/exists? (str dir "/app/etc/env.php")) dir
29+
(let [parent (fs/dirname dir)]
30+
(when (not= parent dir)
31+
(recur parent))))))
32+
33+
(defn find-basedir-in-args [args]
34+
(find-arg ["-d" "--directory"] args))
35+
36+
(defn find-basedir [args]
37+
(let [basedir (or (find-basedir-in-args args) (find-basedir-in-path (fs/cwd)))]
38+
(when-not basedir
39+
(throw (ex-info "Unable to determine Magento directory." {})))
40+
(when-not (fs/dir? basedir)
41+
(throw (ex-info (str "Magento directory " basedir " does not exist.") {})))
42+
(log/info "Magento dir" basedir)
43+
basedir))
44+
45+
(defn find-log-level [args]
46+
(letfn [(arg->verbosity [arg]
47+
(case arg
48+
( "-v" "--verbose") 1
49+
("-vv" "--shout") 2
50+
("-vvv" "--debug") 3
51+
0))]
52+
(reduce + 0 (map arg->verbosity args))))
53+
54+
(defn help-the-needfull []
55+
(println "Usage: clean-cache [options and flags] [cache-types...]
56+
Clear the given cache types. If none are given, clear all cache types.
57+
58+
--directory|-d <dir> Magento base directory
59+
--watch|-w Watch for file changes
60+
--verbose|-v Display cleared cache types
61+
--shout|-vv Display more info
62+
--debug|-vvv Display too much information
63+
--help|-h This help message"))
64+
65+
(defn help-needed? [args]
66+
(has-switch? ["-h" "--help"] args))
67+
68+
(defn arg-with-val? [arg]
69+
(#{"--directory" "-d"} arg))
70+
71+
(defn switch? [arg]
72+
(#{"--watch" "-w"
73+
"--verbose" "-v"
74+
"--shout" "-vv"
75+
"--debug" "-vvv"
76+
"--help" "-h" } arg))
77+
78+
(defn remove-switches-and-args-with-vals [args]
79+
(let [args (vec args)]
80+
(loop [xs [] i 0]
81+
(if (= i (count args)) xs
82+
(let [arg (get args i)]
83+
(cond
84+
(arg-with-val? arg) (recur xs (+ i 2))
85+
(switch? arg) (recur xs (inc i))
86+
:else (recur (conj xs arg) (inc i))))))))
87+
88+
(defn -main [& args]
89+
(log/always "Sponsored by https://www.mage2.tv\n")
90+
(if (help-needed? args)
91+
(help-the-needfull)
92+
(do
93+
(log/set-verbosity! (find-log-level args))
94+
(storage/set-magento-dir! (find-basedir args))
95+
(if (has-switch? ["-w" "--watch"] args)
96+
(watcher/start)
97+
(let [cache-types (remove-switches-and-args-with-vals args)]
98+
(cache/clean-cache-types cache-types))))))
99+
100+
(set! *main-cli-fn* -main)

src/cache/filestorage.cljs

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
(ns cache.filestorage
2+
(:require [file.system :as file]
3+
[cache.log :as log]
4+
[clojure.string :as string]))
5+
6+
(def options
7+
{:file-name-prefix "mage"
8+
:hashed-directory-level 1})
9+
10+
(def magento-basedir (atom "./"))
11+
12+
(def ^:dynamic *cachedir* "var/cache/")
13+
14+
(def add-trailing-slash
15+
(memoize
16+
(fn [dir]
17+
(if (= \/ (last dir))
18+
dir
19+
(str dir "/")))))
20+
21+
(defn set-magento-dir! [dir]
22+
(reset! magento-basedir dir))
23+
24+
(defn base-dir []
25+
(add-trailing-slash @magento-basedir))
26+
27+
(defn- cache-dir []
28+
(add-trailing-slash (str (base-dir) *cachedir*)))
29+
30+
(defn- md5 [^String data]
31+
(let [crypto (js/require
32+
"crypto")]
33+
(-> crypto (.createHash "md5") (.update data) (.digest "hex"))))
34+
35+
(defn- file-name-prefix []
36+
(:file-name-prefix options))
37+
38+
(defn- cache-id-prefix []
39+
(str (subs (md5 (str (file/realpath (base-dir)) "/app/etc/")) 0 3) "_"))
40+
41+
(defn- chars-from-end [^String s length]
42+
(subs s (- (count s) length)))
43+
44+
(defn- path [^String id]
45+
(let [length (:hashed-directory-level options)
46+
dir (cache-dir)]
47+
(if (< 0 length)
48+
(str dir (file-name-prefix) "--" (chars-from-end (md5 (str (cache-id-prefix) id)) length) "/")
49+
dir)))
50+
51+
(defn- tag-path []
52+
(str (cache-dir) (file-name-prefix) "-tags/"))
53+
54+
(defn id->filename [^String id]
55+
(str (file-name-prefix) "---" (cache-id-prefix) id))
56+
57+
(defn id->filepath [^String id]
58+
(str (path id) (id->filename id)))
59+
60+
(defn tag->filepath [^String tag]
61+
(str (tag-path) (id->filename tag)))
62+
63+
(defn- remove-cache-id-prefix [id-with-prefix]
64+
(subs id-with-prefix 4))
65+
66+
(defn tag->ids [tag]
67+
(let [file (tag->filepath tag)]
68+
(when (file/exists? file)
69+
(doall (map remove-cache-id-prefix (string/split-lines (file/slurp file)))))))
70+
71+
(defn clean-all []
72+
(log/debug "Cleaning dir" (cache-dir))
73+
(file/rm-contents (cache-dir)))
74+
75+
(defn delete [id]
76+
(log/debug "Cleaning id" id)
77+
(let [file (id->filepath id)]
78+
(when (file/exists? file)
79+
(file/rm file))))

src/cache/log.cljs

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
(ns cache.log)
2+
3+
(def levels {::debug 3
4+
::info 2
5+
::notice 1
6+
::error 0
7+
::all -1})
8+
9+
(defn- ->level [n]
10+
(when (keyword? n)
11+
(get levels n)))
12+
13+
(defonce verbosity (atom (->level :error)))
14+
15+
(defn set-verbosity! [v]
16+
(reset! verbosity v))
17+
18+
(defn- out [level & msg]
19+
(when (>= @verbosity (->level level)
20+
(apply println msg))))
21+
22+
(defn debug [msg & msgs]
23+
(apply out ::debug msg msgs))
24+
25+
(defn info [msg & msgs]
26+
(apply out ::info msg msgs))
27+
28+
(defn notice [msg & msgs]
29+
(apply out ::notice msg msgs))
30+
31+
(defn error [msg & msgs]
32+
(apply out ::error msg msgs))
33+
34+
(defn always [msg & msgs]
35+
(apply out ::all msg msgs))

0 commit comments

Comments
 (0)