mirror of
https://github.com/penpot/penpot.git
synced 2026-10-29 05:50:35 +00:00
🚧 WIP Step 12, temporary pilot
- pilot runner for testing - will be entirely removed later AI-assisted-by: muse-spark-1.3, deepseek-flash, glm-5.3
This commit is contained in:
parent
5afac4a330
commit
9e5620600f
@ -27,6 +27,7 @@
|
||||
"build:storybook:cljs": "clojure -M:dev:shadow-cljs compile storybook",
|
||||
"build:wasm": "../render-wasm/build frontend",
|
||||
"build:renderer-benchmarks:runner": "clojure -M:dev:renderer-bench release bench-render-wasm-runner",
|
||||
"build:renderer-benchmarks:pilot": "clojure -M:dev:renderer-bench release bench-render-wasm-pilot",
|
||||
"build:app:libs": "node ./scripts/build-libs.js",
|
||||
"build:app:main": "clojure -M:dev:shadow-cljs release main worker",
|
||||
"build:app:worker": "clojure -M:dev:shadow-cljs release worker",
|
||||
|
||||
@ -195,10 +195,31 @@
|
||||
:elide-asserts true
|
||||
:anon-fn-naming-policy :off}}}
|
||||
|
||||
:bench-render-wasm-pilot
|
||||
{:target :node-script
|
||||
:output-to "target/renderer-benchmarks/pilot.cjs"
|
||||
:output-dir "target/renderer-benchmarks/pilot/"
|
||||
:main benches.render-wasm.pilot/-main
|
||||
:devtools {:autoload false}
|
||||
|
||||
:compiler-options
|
||||
{:output-feature-set :es2020
|
||||
:output-wrapper false
|
||||
:warnings {:fn-deprecated false}}
|
||||
|
||||
:release
|
||||
{:compiler-options
|
||||
{:fn-invoke-direct true
|
||||
:elide-asserts true
|
||||
:anon-fn-naming-policy :off}}}
|
||||
|
||||
:bench-render-wasm-browser
|
||||
{:target :esm
|
||||
:output-dir "target/renderer-benchmarks/browser/"
|
||||
:js-options {:js-provider :import}
|
||||
;; Bundle npm deps (date-fns via app.common): the pilot serves plain
|
||||
;; static files with no import-map resolver. TODO(10): revisit when
|
||||
;; ticket 10 owns staging; artifacts must stay self-contained.
|
||||
:js-options {:js-provider :shadow}
|
||||
|
||||
:modules
|
||||
{:browser
|
||||
|
||||
242
frontend/test/benches/render_wasm/pilot.cljs
Normal file
242
frontend/test/benches/render_wasm/pilot.cljs
Normal file
@ -0,0 +1,242 @@
|
||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) KALEIDOS SUBSIDIARY SL
|
||||
|
||||
(ns benches.render-wasm.pilot
|
||||
"Interim diagnostic: loads `:rects/load` in headless Chromium and prints the
|
||||
bridge result JSON on stdout.
|
||||
|
||||
TODO(mem:render-wasm/performance/cljs-rewrite/10-process-build-and-server):
|
||||
the inline static server and artifact layout here are pilot scaffolding.
|
||||
Ticket 10 owns staging and server ownership; this only serves the release
|
||||
browser bundle, the prepared renderer artifacts and `pilot.html` on an
|
||||
ephemeral 127.0.0.1 port.
|
||||
|
||||
TODO(mem:render-wasm/performance/cljs-rewrite/11-runner-and-failure-accounting):
|
||||
CLI surface, orchestration style, attempt loop and exit policy belong to
|
||||
ticket 11. Raw `.then` chains below are diagnostic scaffolding, not the
|
||||
runner's interop pattern.
|
||||
|
||||
Exit codes: 0 when result JSON is produced (including bridge failure maps,
|
||||
which are valid pilot output); 1 on harness errors and timeouts. A timeout
|
||||
closes the browser, so it stops even a synchronously blocked page.
|
||||
|
||||
Usage, from `frontend/` with Playwright's Chromium installed:
|
||||
|
||||
PENPOT_WASM_PREPARED=1 ../render-wasm/build frontend
|
||||
pnpm run build:renderer-benchmarks:pilot
|
||||
node target/renderer-benchmarks/pilot.cjs --seed 42 --screenshot /tmp/pilot-rects.png
|
||||
|
||||
Usage, from `frontend/` with Playwright's Chromium installed:
|
||||
|
||||
PENPOT_WASM_PREPARED=1 ../render-wasm/build frontend
|
||||
clojure -M:dev:renderer-bench release bench-render-wasm-browser
|
||||
pnpm run build:renderer-benchmarks:pilot
|
||||
node target/renderer-benchmarks/pilot.cjs --seed 42 --screenshot /tmp/pilot-rects.png
|
||||
|
||||
The pilot serves the release browser bundle, so compile
|
||||
`bench-render-wasm-browser` first (after the prepared renderer build
|
||||
refreshes `shared.js`). Flags: `--seed N` (default 42),
|
||||
`--timeout-ms N` (default 30000), `--screenshot PATH` (optional untimed
|
||||
capture for visual inspection)."
|
||||
(:require
|
||||
["fs" :as fs]
|
||||
["http" :as http]
|
||||
["path" :as path]
|
||||
["playwright" :as playwright]
|
||||
[clojure.string :as str]))
|
||||
|
||||
(def ^:private default-seed 42)
|
||||
(def ^:private default-timeout-ms 30000)
|
||||
|
||||
(defn- frontend-root
|
||||
"Resolves the frontend checkout root from the compiled pilot location
|
||||
(`target/renderer-benchmarks/pilot.cjs`, next to `runner.cjs`). Ticket 10
|
||||
owns command cwd resolution; the pilot pins its own roots instead."
|
||||
[]
|
||||
(path/resolve js/__dirname ".." ".."))
|
||||
|
||||
(def ^:private mime
|
||||
{".html" "text/html"
|
||||
".js" "text/javascript"
|
||||
".wasm" "application/wasm"
|
||||
".json" "application/json"})
|
||||
|
||||
(defn- safe-join
|
||||
"Joins `sub` under `root`, returning nil when it escapes (path traversal)."
|
||||
[root sub]
|
||||
(let [joined (path/join root sub)]
|
||||
(when-not (str/includes? (path/relative root joined) "..")
|
||||
joined)))
|
||||
|
||||
(defn- serve-file!
|
||||
[^js res file]
|
||||
(if (and (some? file) (fs/existsSync file))
|
||||
(let [ext (path/extname file)]
|
||||
(.writeHead res 200 #js {"Content-Type" (get mime ext "application/octet-stream")})
|
||||
(.end res (fs/readFileSync file)))
|
||||
(do (.writeHead res 404 #js {"Content-Type" "text/plain"})
|
||||
(.end res "not found"))))
|
||||
|
||||
(defn- start-server!
|
||||
"Serves the pilot page, the release browser bundle and the prepared
|
||||
renderer artifacts. Returns a promise of `{server port}`."
|
||||
[browser-dir js-dir html-file]
|
||||
(js/Promise.
|
||||
(fn [resolve _reject]
|
||||
(let [^js server (http/createServer
|
||||
(fn [^js req ^js res]
|
||||
(let [url (.-url ^js req)]
|
||||
(cond
|
||||
(= url "/") (serve-file! res html-file)
|
||||
(= url "/pilot.html") (serve-file! res html-file)
|
||||
(str/starts-with? url "/bench/")
|
||||
(serve-file! res (safe-join browser-dir (subs url (count "/bench/"))))
|
||||
(str/starts-with? url "/js/")
|
||||
(serve-file! res (safe-join js-dir (subs url (count "/js/"))))
|
||||
:else (do (.writeHead res 404 #js {"Content-Type" "text/plain"})
|
||||
(.end res "not found"))))))]
|
||||
(.listen server 0 "127.0.0.1"
|
||||
(fn [] (resolve #js {"server" server
|
||||
"port" (.-port (.address server))})))))))
|
||||
|
||||
(defn- usage
|
||||
[]
|
||||
"pilot [--seed N] [--timeout-ms N] [--screenshot PATH]")
|
||||
|
||||
(defn- parse-args
|
||||
[argv]
|
||||
(let [opts (loop [args argv
|
||||
opts {:seed default-seed :timeout-ms default-timeout-ms}]
|
||||
(if (empty? args)
|
||||
opts
|
||||
(let [[flag value & rest-args] args]
|
||||
(cond
|
||||
(= flag "--seed") (recur rest-args (assoc opts :seed (js/parseInt value 10)))
|
||||
(= flag "--timeout-ms") (recur rest-args (assoc opts :timeout-ms (js/parseInt value 10)))
|
||||
(= flag "--screenshot") (recur rest-args (assoc opts :screenshot value))
|
||||
:else (recur rest-args (assoc opts ::invalid flag))))))
|
||||
valid-seed? (fn [n] (and (integer? n) (<= 0 n 4294967295)))
|
||||
valid-timeout? (fn [n] (and (integer? n) (pos? n)))]
|
||||
(if (and (nil? (::invalid opts))
|
||||
(valid-seed? (:seed opts)) (valid-timeout? (:timeout-ms opts)))
|
||||
opts
|
||||
(do
|
||||
(.error js/console (str "pilot: invalid args " (pr-str opts)))
|
||||
(.error js/console (usage))
|
||||
(js/process.exit 1)))))
|
||||
|
||||
(defn- trace
|
||||
"Diagnostic line on stderr; stdout carries only the result JSON."
|
||||
[message]
|
||||
(.error js/console message))
|
||||
|
||||
(defn- print-result!
|
||||
[result]
|
||||
(println (.stringify js/JSON result nil 2)))
|
||||
|
||||
(defn -main
|
||||
[& argv]
|
||||
(trace "pilot: parsing args")
|
||||
(let [opts (parse-args (or argv []))
|
||||
root (frontend-root)
|
||||
bundle (path/join root "target" "renderer-benchmarks" "browser")
|
||||
js-dir (path/join root "resources" "public" "js")
|
||||
html (path/join root "test" "benches" "render_wasm" "pilot.html")
|
||||
seed (:seed opts)
|
||||
deadline (:timeout-ms opts)]
|
||||
(-> (start-server! bundle js-dir html)
|
||||
(.then
|
||||
(fn [info]
|
||||
(trace "pilot: server up")
|
||||
(let [server (unchecked-get info "server")
|
||||
port (unchecked-get info "port")
|
||||
url (str "http://127.0.0.1:" port "/pilot.html")
|
||||
close! (fn [] (js/Promise.resolve (.close server)))
|
||||
;; Holder so the outer timeout path can close a browser
|
||||
;; the work chain never returned (blocked page, or a
|
||||
;; launch/context/page failure after launch).
|
||||
browser-ref (atom nil)
|
||||
work (-> (-> (.-chromium playwright)
|
||||
(.launch #js {"args" #js ["--enable-gpu"]}))
|
||||
(.then (fn [^js pw-browser]
|
||||
(reset! browser-ref pw-browser)
|
||||
(let [version (.version pw-browser)]
|
||||
(-> (.newContext
|
||||
pw-browser
|
||||
#js {"viewport"
|
||||
#js {"width" 1920 "height" 1080}
|
||||
"deviceScaleFactor" 2})
|
||||
(.then (fn [^js context]
|
||||
(-> (.newPage context)
|
||||
(.then
|
||||
(fn [^js page]
|
||||
(trace "pilot: page open")
|
||||
(-> (.goto page url)
|
||||
(.then (fn [_]
|
||||
(.waitForFunction
|
||||
page
|
||||
"window.__benchReady === true")))
|
||||
(.then (fn [_]
|
||||
(trace "pilot: bridge ready, evaluating")
|
||||
;; NOTE: a string page-function evaluates
|
||||
;; as an expression, so inline the args
|
||||
;; into an IIFE; a passed arg would
|
||||
;; never arrive.
|
||||
(let [call (str "(async () => window.__benchBridge.loadRects("
|
||||
(.stringify
|
||||
js/JSON
|
||||
#js {"seed" seed
|
||||
"module-url" "/js/render-wasm.js"
|
||||
"wasm-url" "/js/render-wasm.wasm"})
|
||||
"))()")]
|
||||
(.evaluate page call))))
|
||||
(.then (fn [result]
|
||||
(let [out #js {"seed" seed
|
||||
"result" result}]
|
||||
(aset out "browser"
|
||||
#js {"chromium" version})
|
||||
(if-let [shot (:screenshot opts)]
|
||||
(-> (.screenshot
|
||||
page
|
||||
#js {"path" shot})
|
||||
(.then (fn [_]
|
||||
(aset out "screenshot" shot)
|
||||
out)))
|
||||
out))))
|
||||
(.then (fn [out]
|
||||
(-> (.close pw-browser)
|
||||
(.then (fn [_] out)))))
|
||||
(.catch (fn [cause]
|
||||
(.close pw-browser)
|
||||
(throw cause))))))))))))))
|
||||
guard (js/Promise.
|
||||
(fn [_resolve reject]
|
||||
(js/setTimeout
|
||||
(fn [] (reject (ex-info "pilot timeout" {:timeout-ms deadline})))
|
||||
deadline)))]
|
||||
(-> (js/Promise.race #js [work guard])
|
||||
(.then (fn [out]
|
||||
(-> (close!)
|
||||
(.then (fn [_]
|
||||
(print-result! out)
|
||||
(js/process.exit 0))))))
|
||||
(.catch (fn [cause]
|
||||
;; The work chain may have left a browser open
|
||||
;; (guard timeout, blocked page, post-launch
|
||||
;; failure): close it before the server so no
|
||||
;; Chromium is orphaned. Double close is safe.
|
||||
(-> (if-let [b @browser-ref]
|
||||
(-> (.close b)
|
||||
(.catch (fn [_] nil)))
|
||||
(js/Promise.resolve nil))
|
||||
(.then (fn [_] (close!)))
|
||||
(.then (fn [_]
|
||||
(println "pilot harness failure:"
|
||||
(or (ex-message cause) (str cause)))
|
||||
(js/process.exit 1))))))))))
|
||||
(.catch (fn [cause]
|
||||
(println "pilot startup failure:" (or (ex-message cause) (str cause)))
|
||||
(js/process.exit 1))))))
|
||||
17
frontend/test/benches/render_wasm/pilot.html
Normal file
17
frontend/test/benches/render_wasm/pilot.html
Normal file
@ -0,0 +1,17 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>renderer benchmark pilot</title>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Same one-liner as frontend/resources/polyfills/dynamicImport.js:
|
||||
the bench bundle is plain ESM and must not depend on app boot. -->
|
||||
<script>globalThis.dynamicImport = function (u) { return import(u); };</script>
|
||||
<script type="module">
|
||||
import bridge from "/bench/browser.js";
|
||||
window.__benchBridge = bridge;
|
||||
window.__benchReady = true;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user