mirror of
https://github.com/logseq/logseq.git
synced 2026-06-01 19:01:22 +00:00
enhance(cli-e2e): add bb cleanup
This commit is contained in:
@@ -10,4 +10,6 @@ Shell-first end-to-end tests for logseq CLI.
|
||||
- Run cli-e2e cases with build preflight unless --skip-build is provided: `bb test`
|
||||
- `bb test --help` for more help info
|
||||
|
||||
|
||||
## Clean up
|
||||
- Execute cleanup: terminate stale db-worker-node processes and remove cli-e2e temp graph directories: `bb cleanup`
|
||||
- Preview cleanup actions only (no kill/delete): `bb cleanup --dry-run`
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
:h :help}
|
||||
:coerce {:include []
|
||||
:help :boolean
|
||||
:dry-run :boolean
|
||||
:skip-build :boolean
|
||||
:verbose :boolean}}))
|
||||
|
||||
@@ -28,4 +29,8 @@
|
||||
|
||||
test
|
||||
{:doc "Run cli-e2e cases with build preflight unless --skip-build is provided"
|
||||
:task (main/test! cli-opts)}}}
|
||||
:task (main/test! cli-opts)}
|
||||
|
||||
cleanup
|
||||
{:doc "Terminate cli-e2e db-worker-node processes and remove temp graph dirs"
|
||||
:task (main/cleanup! cli-opts)}}}
|
||||
|
||||
120
cli-e2e/src/logseq/cli/e2e/cleanup.clj
Normal file
120
cli-e2e/src/logseq/cli/e2e/cleanup.clj
Normal file
@@ -0,0 +1,120 @@
|
||||
(ns logseq.cli.e2e.cleanup
|
||||
(:require [babashka.fs :as fs]
|
||||
[clojure.java.shell :as java-shell]
|
||||
[clojure.string :as string]))
|
||||
|
||||
(def ^:private cli-e2e-temp-prefix "logseq-cli-e2e-")
|
||||
|
||||
(defn- parse-ps-line
|
||||
[line]
|
||||
(when-let [[_ pid-str command] (re-matches #"\s*(\d+)\s+(.*)" (or line ""))]
|
||||
{:pid (Long/parseLong pid-str)
|
||||
:command command}))
|
||||
|
||||
(defn- cli-e2e-db-worker-command?
|
||||
[command]
|
||||
(and (re-find #"db-worker-node(?:\.js)?\b" command)
|
||||
(re-find #"logseq-cli-e2e-" command)))
|
||||
|
||||
(defn list-cli-e2e-db-worker-pids
|
||||
([]
|
||||
(list-cli-e2e-db-worker-pids {}))
|
||||
([{:keys [shell-fn]
|
||||
:or {shell-fn java-shell/sh}}]
|
||||
(let [{:keys [exit out err]} (shell-fn "ps" "-ax" "-o" "pid=" "-o" "command=")]
|
||||
(when-not (zero? exit)
|
||||
(throw (ex-info "Unable to scan processes"
|
||||
{:exit exit
|
||||
:err err})))
|
||||
(->> (string/split-lines (or out ""))
|
||||
(keep parse-ps-line)
|
||||
(filter (fn [{:keys [command]}]
|
||||
(cli-e2e-db-worker-command? command)))
|
||||
(mapv :pid)))))
|
||||
|
||||
(defn- pid-alive?
|
||||
[pid]
|
||||
(zero? (:exit (java-shell/sh "kill" "-0" (str pid)))))
|
||||
|
||||
(defn- kill-pid!
|
||||
[pid]
|
||||
(try
|
||||
(if-not (pid-alive? pid)
|
||||
:not-found
|
||||
(do
|
||||
(java-shell/sh "kill" "-TERM" (str pid))
|
||||
(Thread/sleep 100)
|
||||
(if-not (pid-alive? pid)
|
||||
:killed
|
||||
(do
|
||||
(java-shell/sh "kill" "-KILL" (str pid))
|
||||
(Thread/sleep 50)
|
||||
(if (pid-alive? pid)
|
||||
:failed
|
||||
:killed)))))
|
||||
(catch Exception _
|
||||
:failed)))
|
||||
|
||||
(defn cleanup-db-worker-processes!
|
||||
([]
|
||||
(cleanup-db-worker-processes! {}))
|
||||
([{:keys [dry-run list-pids-fn kill-pid-fn]}]
|
||||
(let [list-pids-fn (or list-pids-fn list-cli-e2e-db-worker-pids)
|
||||
kill-pid-fn (or kill-pid-fn kill-pid!)
|
||||
found-pids (vec (list-pids-fn))]
|
||||
(if dry-run
|
||||
{:dry-run? true
|
||||
:found-pids found-pids
|
||||
:would-kill-pids found-pids
|
||||
:killed-pids []
|
||||
:failed-pids []}
|
||||
(let [{:keys [killed-pids failed-pids]}
|
||||
(reduce (fn [acc pid]
|
||||
(if (= :failed (kill-pid-fn pid))
|
||||
(update acc :failed-pids conj pid)
|
||||
(update acc :killed-pids conj pid)))
|
||||
{:killed-pids []
|
||||
:failed-pids []}
|
||||
found-pids)]
|
||||
{:dry-run? false
|
||||
:found-pids found-pids
|
||||
:would-kill-pids []
|
||||
:killed-pids killed-pids
|
||||
:failed-pids failed-pids})))))
|
||||
|
||||
(defn- list-cli-e2e-temp-graph-dirs
|
||||
[tmp-root]
|
||||
(->> (fs/glob tmp-root (str cli-e2e-temp-prefix "*/graphs"))
|
||||
(filter fs/directory?)
|
||||
(mapv str)))
|
||||
|
||||
(defn cleanup-temp-graph-dirs!
|
||||
([]
|
||||
(cleanup-temp-graph-dirs! {}))
|
||||
([{:keys [dry-run tmp-root list-dirs-fn delete-dir-fn]
|
||||
:or {tmp-root (System/getProperty "java.io.tmpdir")
|
||||
delete-dir-fn fs/delete-tree}}]
|
||||
(let [list-dirs-fn (or list-dirs-fn
|
||||
#(list-cli-e2e-temp-graph-dirs tmp-root))
|
||||
found-dirs (vec (list-dirs-fn))]
|
||||
(if dry-run
|
||||
{:dry-run? true
|
||||
:found-dirs found-dirs
|
||||
:would-remove-dirs found-dirs
|
||||
:removed-dirs []
|
||||
:failed-dirs []}
|
||||
(let [{:keys [removed-dirs failed-dirs]}
|
||||
(reduce (fn [acc dir]
|
||||
(try
|
||||
(delete-dir-fn dir)
|
||||
(update acc :removed-dirs conj dir)
|
||||
(catch Exception _
|
||||
(update acc :failed-dirs conj dir))))
|
||||
{:removed-dirs []
|
||||
:failed-dirs []}
|
||||
found-dirs)]
|
||||
{:dry-run? false
|
||||
:found-dirs found-dirs
|
||||
:would-remove-dirs []
|
||||
:removed-dirs removed-dirs
|
||||
:failed-dirs failed-dirs})))))
|
||||
@@ -1,5 +1,6 @@
|
||||
(ns logseq.cli.e2e.main
|
||||
(:require [clojure.set :as set]
|
||||
[logseq.cli.e2e.cleanup :as cleanup]
|
||||
[logseq.cli.e2e.coverage :as coverage]
|
||||
[logseq.cli.e2e.manifests :as manifests]
|
||||
[logseq.cli.e2e.preflight :as preflight]
|
||||
@@ -103,6 +104,54 @@
|
||||
(doseq [{:keys [id]} (manifests/load-cases)]
|
||||
(println id)))
|
||||
|
||||
(defn- print-cleanup-help!
|
||||
[]
|
||||
(println "Usage: bb -f cli-e2e/bb.edn cleanup [options]")
|
||||
(println)
|
||||
(println "Options:")
|
||||
(println " -h, --help Show this help and exit")
|
||||
(println " --dry-run Scan and report only; do not kill/delete")
|
||||
(println)
|
||||
(println "Cleanups performed:")
|
||||
(println " - Terminate cli-e2e db-worker-node processes")
|
||||
(println " - Remove cli-e2e temp graph directories")
|
||||
(flush))
|
||||
|
||||
(defn cleanup!
|
||||
[opts]
|
||||
(if (:help opts)
|
||||
(do
|
||||
(print-cleanup-help!)
|
||||
{:status :help})
|
||||
(let [dry-run? (boolean (:dry-run opts))
|
||||
cleanup-opts (cond-> {}
|
||||
dry-run? (assoc :dry-run true))
|
||||
processes (cleanup/cleanup-db-worker-processes! cleanup-opts)
|
||||
temp-graphs (cleanup/cleanup-temp-graph-dirs! cleanup-opts)]
|
||||
(println "==> Running cli-e2e cleanup")
|
||||
(if dry-run?
|
||||
(do
|
||||
(println (format "[dry-run] db-worker-node processes: found %d, would kill %d"
|
||||
(count (:found-pids processes))
|
||||
(count (:would-kill-pids processes))))
|
||||
(println (format "[dry-run] temp graph directories: found %d, would remove %d"
|
||||
(count (:found-dirs temp-graphs))
|
||||
(count (:would-remove-dirs temp-graphs)))))
|
||||
(do
|
||||
(println (format "db-worker-node processes: found %d, killed %d, failed %d"
|
||||
(count (:found-pids processes))
|
||||
(count (:killed-pids processes))
|
||||
(count (:failed-pids processes))))
|
||||
(println (format "temp graph directories: found %d, removed %d, failed %d"
|
||||
(count (:found-dirs temp-graphs))
|
||||
(count (:removed-dirs temp-graphs))
|
||||
(count (:failed-dirs temp-graphs))))))
|
||||
(flush)
|
||||
{:status :ok
|
||||
:dry-run? dry-run?
|
||||
:processes processes
|
||||
:temp-graphs temp-graphs})))
|
||||
|
||||
(defn- print-failure-details!
|
||||
[error]
|
||||
(let [data (ex-data error)]
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
logseq.cli.e2e.preflight-test
|
||||
logseq.cli.e2e.shell-test
|
||||
logseq.cli.e2e.runner-test
|
||||
logseq.cli.e2e.cleanup-test
|
||||
logseq.cli.e2e.main-test])
|
||||
|
||||
(defn run!
|
||||
|
||||
94
cli-e2e/test/logseq/cli/e2e/cleanup_test.clj
Normal file
94
cli-e2e/test/logseq/cli/e2e/cleanup_test.clj
Normal file
@@ -0,0 +1,94 @@
|
||||
(ns logseq.cli.e2e.cleanup-test
|
||||
(:require [babashka.fs :as fs]
|
||||
[clojure.test :refer [deftest is testing]]
|
||||
[logseq.cli.e2e.cleanup :as cleanup]))
|
||||
|
||||
(deftest list-cli-e2e-db-worker-pids-filters-processes
|
||||
(let [shell-fn (fn [& _]
|
||||
{:exit 0
|
||||
:out (str " 101 node /repo/dist/db-worker-node.js --repo graph-a --data-dir /tmp/logseq-cli-e2e-graph-a-123/graphs --owner-source cli\n"
|
||||
" 202 node /repo/dist/db-worker-node.js --repo production --data-dir /tmp/production-graphs --owner-source cli\n"
|
||||
" 303 node /repo/static/logseq-cli.js graph list\n"
|
||||
" 404 /usr/bin/python3 background.py\n"
|
||||
" 505 node /repo/static/db-worker-node.js --repo graph-b --data-dir /private/tmp/logseq-cli-e2e-graph-b-999/graphs --owner-source cli\n")
|
||||
:err ""})]
|
||||
(is (= [101 505]
|
||||
(cleanup/list-cli-e2e-db-worker-pids {:shell-fn shell-fn}))))
|
||||
|
||||
(testing "throws when ps invocation fails"
|
||||
(is (thrown-with-msg?
|
||||
clojure.lang.ExceptionInfo
|
||||
#"Unable to scan processes"
|
||||
(cleanup/list-cli-e2e-db-worker-pids {:shell-fn (fn [& _]
|
||||
{:exit 1
|
||||
:out ""
|
||||
:err "permission denied"})})))))
|
||||
|
||||
(deftest cleanup-db-worker-processes-separates-killed-and-failed
|
||||
(let [killed (atom [])
|
||||
result (cleanup/cleanup-db-worker-processes! {:list-pids-fn (fn [] [11 22 33])
|
||||
:kill-pid-fn (fn [pid]
|
||||
(swap! killed conj pid)
|
||||
(if (= pid 22)
|
||||
:failed
|
||||
:killed))})]
|
||||
(is (= [11 22 33] (:found-pids result)))
|
||||
(is (= [11 33] (:killed-pids result)))
|
||||
(is (= [22] (:failed-pids result)))
|
||||
(is (= [11 22 33] @killed))))
|
||||
|
||||
(deftest cleanup-db-worker-processes-dry-run-does-not-kill
|
||||
(let [killed (atom [])
|
||||
result (cleanup/cleanup-db-worker-processes! {:dry-run true
|
||||
:list-pids-fn (fn [] [11 22])
|
||||
:kill-pid-fn (fn [pid]
|
||||
(swap! killed conj pid)
|
||||
:killed)})]
|
||||
(is (= [11 22] (:found-pids result)))
|
||||
(is (true? (:dry-run? result)))
|
||||
(is (= [11 22] (:would-kill-pids result)))
|
||||
(is (= [] (:killed-pids result)))
|
||||
(is (= [] (:failed-pids result)))
|
||||
(is (= [] @killed))))
|
||||
|
||||
(deftest cleanup-temp-graph-dirs-removes-only-cli-e2e-graphs
|
||||
(let [tmp-root (fs/create-temp-dir {:prefix "cleanup-e2e-test-"})
|
||||
matching-graphs (fs/path tmp-root "logseq-cli-e2e-case-a-123" "graphs")
|
||||
another-matching-graphs (fs/path tmp-root "logseq-cli-e2e-case-b-456" "graphs")
|
||||
non-matching-graphs (fs/path tmp-root "other-case" "graphs")]
|
||||
(try
|
||||
(fs/create-dirs matching-graphs)
|
||||
(fs/create-dirs another-matching-graphs)
|
||||
(fs/create-dirs non-matching-graphs)
|
||||
|
||||
(let [result (cleanup/cleanup-temp-graph-dirs! {:tmp-root (str tmp-root)})
|
||||
expected-dirs (sort [(str matching-graphs)
|
||||
(str another-matching-graphs)])]
|
||||
(is (= expected-dirs
|
||||
(sort (:found-dirs result))))
|
||||
(is (= expected-dirs
|
||||
(sort (:removed-dirs result))))
|
||||
(is (empty? (:failed-dirs result)))
|
||||
(is (false? (fs/exists? matching-graphs)))
|
||||
(is (false? (fs/exists? another-matching-graphs)))
|
||||
(is (true? (fs/exists? non-matching-graphs))))
|
||||
(finally
|
||||
(fs/delete-tree tmp-root)))))
|
||||
|
||||
(deftest cleanup-temp-graph-dirs-dry-run-does-not-delete
|
||||
(let [deleted (atom [])
|
||||
result (cleanup/cleanup-temp-graph-dirs! {:dry-run true
|
||||
:list-dirs-fn (fn [] ["/tmp/logseq-cli-e2e-a/graphs"
|
||||
"/tmp/logseq-cli-e2e-b/graphs"])
|
||||
:delete-dir-fn (fn [dir]
|
||||
(swap! deleted conj dir))})]
|
||||
(is (= ["/tmp/logseq-cli-e2e-a/graphs"
|
||||
"/tmp/logseq-cli-e2e-b/graphs"]
|
||||
(:found-dirs result)))
|
||||
(is (true? (:dry-run? result)))
|
||||
(is (= ["/tmp/logseq-cli-e2e-a/graphs"
|
||||
"/tmp/logseq-cli-e2e-b/graphs"]
|
||||
(:would-remove-dirs result)))
|
||||
(is (= [] (:removed-dirs result)))
|
||||
(is (= [] (:failed-dirs result)))
|
||||
(is (= [] @deleted))))
|
||||
@@ -1,6 +1,7 @@
|
||||
(ns logseq.cli.e2e.main-test
|
||||
(:require [clojure.string :as string]
|
||||
[clojure.test :refer [deftest is testing]]
|
||||
[logseq.cli.e2e.cleanup :as cleanup]
|
||||
[logseq.cli.e2e.main :as main]))
|
||||
|
||||
(def sample-cases
|
||||
@@ -189,3 +190,54 @@
|
||||
(is (string/includes? output "==> Detailed case logging enabled (--case global-help)"))
|
||||
(is (string/includes? output " [main] $ node static/logseq-cli.js --help"))
|
||||
(is (true? (:stream-output? @command-opts)))))
|
||||
|
||||
(deftest cleanup-help-prints-usage
|
||||
(let [output (with-out-str (main/cleanup! {:help true}))]
|
||||
(is (string/includes? output "Usage: bb -f cli-e2e/bb.edn cleanup"))
|
||||
(is (string/includes? output "Terminate cli-e2e db-worker-node processes"))
|
||||
(is (string/includes? output "--dry-run"))))
|
||||
|
||||
(deftest cleanup-prints-summary-and-returns-status
|
||||
(with-redefs [cleanup/cleanup-db-worker-processes! (fn [_] {:found-pids [101 202]
|
||||
:killed-pids [101]
|
||||
:failed-pids [202]})
|
||||
cleanup/cleanup-temp-graph-dirs! (fn [_] {:found-dirs ["/tmp/logseq-cli-e2e-a/graphs"
|
||||
"/tmp/logseq-cli-e2e-b/graphs"]
|
||||
:removed-dirs ["/tmp/logseq-cli-e2e-a/graphs"]
|
||||
:failed-dirs ["/tmp/logseq-cli-e2e-b/graphs"]})]
|
||||
(let [result (atom nil)
|
||||
output (with-out-str
|
||||
(reset! result (main/cleanup! {})))]
|
||||
(is (= :ok (:status @result)))
|
||||
(is (= [101] (get-in @result [:processes :killed-pids])))
|
||||
(is (= ["/tmp/logseq-cli-e2e-a/graphs"] (get-in @result [:temp-graphs :removed-dirs])))
|
||||
(is (string/includes? output "db-worker-node processes: found 2, killed 1, failed 1"))
|
||||
(is (string/includes? output "temp graph directories: found 2, removed 1, failed 1")))))
|
||||
|
||||
(deftest cleanup-dry-run-prints-summary-and-passes-option
|
||||
(let [process-opts (atom nil)
|
||||
dir-opts (atom nil)]
|
||||
(with-redefs [cleanup/cleanup-db-worker-processes! (fn [opts]
|
||||
(reset! process-opts opts)
|
||||
{:dry-run? true
|
||||
:found-pids [101 202]
|
||||
:would-kill-pids [101 202]
|
||||
:killed-pids []
|
||||
:failed-pids []})
|
||||
cleanup/cleanup-temp-graph-dirs! (fn [opts]
|
||||
(reset! dir-opts opts)
|
||||
{:dry-run? true
|
||||
:found-dirs ["/tmp/logseq-cli-e2e-a/graphs"]
|
||||
:would-remove-dirs ["/tmp/logseq-cli-e2e-a/graphs"]
|
||||
:removed-dirs []
|
||||
:failed-dirs []})]
|
||||
(let [result (atom nil)
|
||||
output (with-out-str
|
||||
(reset! result (main/cleanup! {:dry-run true})))]
|
||||
(is (= {:dry-run true} @process-opts))
|
||||
(is (= {:dry-run true} @dir-opts))
|
||||
(is (= :ok (:status @result)))
|
||||
(is (true? (get-in @result [:processes :dry-run?])))
|
||||
(is (true? (get-in @result [:temp-graphs :dry-run?])))
|
||||
(is (string/includes? output "[dry-run] db-worker-node processes: found 2, would kill 2"))
|
||||
(is (string/includes? output "[dry-run] temp graph directories: found 1, would remove 1"))))))
|
||||
|
||||
Reference in New Issue
Block a user