0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-12 07:41:43 -05:00

Merge pull request #1290 from penpot/devenv-improvements

Dev Environment improvements
This commit is contained in:
Andrey Antukh 2021-10-20 16:03:24 +02:00 committed by GitHub
commit 1573d794b9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 1233 additions and 337 deletions

View file

@ -10,6 +10,23 @@ if [ ! -e ~/.fixtures-loaded ]; then
touch ~/.fixtures-loaded
fi
clojure -A:dev -M -m app.main
if [ "$1" = "--watch" ]; then
echo "Start Watch..."
clojure -A:dev -M -m app.main &
PID=$!
npx nodemon \
--watch src \
--watch ../common \
--ext "clj" \
--signal SIGKILL \
--exec 'echo "(user/restart)" | nc -N localhost 6062'
kill -9 $PID
else
clojure -A:dev -M -m app.main
fi

View file

@ -83,8 +83,7 @@
:ldap-attrs-photo "jpegPhoto"
;; a server prop key where initial project is stored.
:initial-project-skey "initial-project"
})
:initial-project-skey "initial-project"})
(s/def ::flags ::us/words)

View file

@ -141,7 +141,8 @@
["/webhooks"
["/sns" {:post (:sns-webhook cfg)}]]
["/api" {:middleware [[middleware/etag]
["/api" {:middleware [[middleware/cors]
[middleware/etag]
[middleware/format-response-body]
[middleware/params]
[middleware/multipart-params]

View file

@ -8,6 +8,7 @@
(:require
[app.common.logging :as l]
[app.common.transit :as t]
[app.config :as cf]
[app.metrics :as mtx]
[app.util.json :as json]
[buddy.core.codecs :as bc]
@ -176,3 +177,29 @@
:uri (str (:uri request) (when qstring (str "?" qstring)))
:method (name (:request-method request)))
(handler request)))))
(defn- wrap-cors
[handler]
(if-not (contains? cf/flags :cors)
handler
(letfn [(add-cors-headers [response request]
(-> response
(update
:headers
(fn [headers]
(-> headers
(assoc "access-control-allow-origin" (get-in request [:headers "origin"]))
(assoc "access-control-allow-methods" "GET,POST,DELETE,OPTIONS,PUT,HEAD,PATCH")
(assoc "access-control-allow-credentials" "true")
(assoc "access-control-expose-headers" "x-requested-with, content-type, cookie")
(assoc "access-control-allow-headers" "x-frontend-version, content-type, accept, x-requested-width"))))))]
(fn [request]
(if (= (:request-method request) :options)
(-> {:status 200 :body ""}
(add-cors-headers request))
(let [response (handler request)]
(add-cors-headers response request)))))))
(def cors
{:name ::cors
:compile (constantly wrap-cors)})

View file

@ -28,6 +28,7 @@ RUN set -ex; \
rlwrap \
unzip \
fakeroot \
netcat \
; \
echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen; \
locale-gen; \
@ -172,9 +173,10 @@ COPY files/vimrc /root/.vimrc
COPY files/tmux.conf /root/.tmux.conf
COPY files/sudoers /etc/sudoers
COPY files/start-tmux.sh /home/start-tmux.sh
COPY files/entrypoint.sh /home/entrypoint.sh
COPY files/init.sh /home/init.sh
COPY files/start-tmux.sh /home/start-tmux.sh
COPY files/start-tmux-back.sh /home/start-tmux-back.sh
COPY files/entrypoint.sh /home/entrypoint.sh
COPY files/init.sh /home/init.sh
ENTRYPOINT ["/home/entrypoint.sh"]
CMD ["/home/init.sh"]

View file

@ -13,6 +13,7 @@ volumes:
services:
main:
profiles: ["full"]
privileged: true
image: "penpotapp/devenv:latest"
build:
@ -49,6 +50,57 @@ services:
- PENPOT_SMTP_PASSWORD=
- PENPOT_SMTP_SSL=false
- PENPOT_SMTP_TLS=false
- PENPOT_FLAGS="enable-cors"
# LDAP setup
- PENPOT_LDAP_HOST=ldap
- PENPOT_LDAP_PORT=10389
- PENPOT_LDAP_SSL=false
- PENPOT_LDAP_STARTTLS=false
- PENPOT_LDAP_BASE_DN=ou=people,dc=planetexpress,dc=com
- PENPOT_LDAP_BIND_DN=cn=admin,dc=planetexpress,dc=com
- PENPOT_LDAP_BIND_PASSWORD=GoodNewsEveryone
- PENPOT_LDAP_ATTRS_USERNAME=uid
- PENPOT_LDAP_ATTRS_EMAIL=mail
- PENPOT_LDAP_ATTRS_FULLNAME=cn
- PENPOT_LDAP_ATTRS_PHOTO=jpegPhoto
backend:
profiles: ["backend"]
privileged: true
image: "penpotapp/devenv:latest"
build:
context: "."
container_name: "penpot-backend"
stop_signal: SIGINT
depends_on:
- postgres
- redis
volumes:
- "user_data:/home/penpot/"
- "${PWD}:/home/penpot/penpot"
ports:
- 6060:6060
- 6061:6061
- 9090:9090
environment:
- EXTERNAL_UID=${CURRENT_USER_ID}
- PENPOT_SECRET_KEY=super-secret-devenv-key
# STMP setup
- PENPOT_SMTP_ENABLED=true
- PENPOT_SMTP_DEFAULT_FROM=no-reply@example.com
- PENPOT_SMTP_DEFAULT_REPLY_TO=no-reply@example.com
- PENPOT_SMTP_HOST=mailer
- PENPOT_SMTP_PORT=1025
- PENPOT_SMTP_USERNAME=
- PENPOT_SMTP_PASSWORD=
- PENPOT_SMTP_SSL=false
- PENPOT_SMTP_TLS=false
- PENPOT_FLAGS="enable-cors"
# LDAP setup
- PENPOT_LDAP_HOST=ldap

View file

@ -0,0 +1,33 @@
#!/usr/bin/env bash
sudo chown penpot:users /home/penpot
cd ~;
source ~/.bashrc
set -e;
echo "[start-tmux.sh] Installing node dependencies"
pushd ~/penpot/exporter/
yarn install
popd
tmux -2 new-session -d -s penpot
tmux rename-window -t penpot:0 'exporter'
tmux select-window -t penpot:0
tmux send-keys -t penpot 'cd penpot/exporter' enter C-l
tmux send-keys -t penpot 'rm -f target/app.js*' enter C-l
tmux send-keys -t penpot 'clojure -M:dev:shadow-cljs watch main' enter
tmux split-window -v
tmux send-keys -t penpot 'cd penpot/exporter' enter C-l
tmux send-keys -t penpot './scripts/wait-and-start.sh' enter
tmux new-window -t penpot:1 -n 'backend'
tmux select-window -t penpot:1
tmux send-keys -t penpot 'cd penpot/backend' enter C-l
tmux send-keys -t penpot './scripts/start-dev' enter
tmux -2 attach-session -t penpot

View file

@ -18,6 +18,11 @@ popd
tmux -2 new-session -d -s penpot
tmux rename-window -t penpot:0 'gulp'
tmux select-window -t penpot:0
tmux send-keys -t penpot 'cd penpot/frontend' enter C-l
tmux send-keys -t penpot 'npx gulp watch' enter
tmux new-window -t penpot:1 -n 'shadow watch'
tmux select-window -t penpot:1
tmux send-keys -t penpot 'cd penpot/frontend' enter C-l
@ -38,9 +43,4 @@ tmux select-window -t penpot:3
tmux send-keys -t penpot 'cd penpot/backend' enter C-l
tmux send-keys -t penpot './scripts/start-dev' enter
tmux rename-window -t penpot:0 'gulp'
tmux select-window -t penpot:0
tmux send-keys -t penpot 'cd penpot/frontend' enter C-l
tmux send-keys -t penpot 'npx gulp watch' enter
tmux -2 attach-session -t penpot

View file

@ -12,7 +12,15 @@
"defaults"
],
"scripts": {
"validate-translations": "node ./scripts/validate-translations.js"
"validate-translations": "node ./scripts/validate-translations.js",
"watch-main": "shadow-cljs watch main",
"watch-gulp": "gulp watch",
"test-watch-compile": "shadow-cljs watch test",
"test-run": "node target/tests.js",
"test-watch-run": "nodemon --signal SIGKILL --watch target --exec npm run test-run",
"test-watch": "npm-run-all --parallel test-watch-compile test-watch-run",
"test": "npm run test-watch",
"start": "npm-run-all --parallel watch-gulp watch-main"
},
"devDependencies": {
"autoprefixer": "^10.2.4",
@ -29,6 +37,8 @@
"map-stream": "0.0.7",
"marked": "^3.0.4",
"mkdirp": "^1.0.4",
"nodemon": "^2.0.13",
"npm-run-all": "^4.1.5",
"postcss": "^8.3.5",
"postcss-clean": "^1.2.2",
"rimraf": "^3.0.0",

View file

@ -93,7 +93,8 @@
(when (false? registration)
(swap! flags disj :registration)))
(def public-uri
(defn get-public-uri
[]
(let [uri (u/uri (or (obj/get global "penpotPublicURI")
(.-origin ^js location)))]
;; Ensure that the path always ends with "/"; this ensures that
@ -102,6 +103,8 @@
(not (str/ends-with? (:path uri) "/"))
(update :path #(str % "/")))))
(def public-uri (get-public-uri))
;; --- Helper Functions
(defn ^boolean check-browser? [candidate]

View file

@ -48,6 +48,7 @@
[id params]
(->> (http/send! {:method :get
:uri (u/join base-uri "api/rpc/query/" (name id))
:credentials "include"
:query params})
(rx/map http/conditional-decode-transit)
(rx/mapcat handle-response)))
@ -58,6 +59,7 @@
[id params]
(->> (http/send! {:method :post
:uri (u/join base-uri "api/rpc/mutation/" (name id))
:credentials "include"
:body (http/transit-data params)})
(rx/map http/conditional-decode-transit)
(rx/mapcat handle-response)))
@ -87,7 +89,10 @@
[_ {:keys [provider] :as params}]
(let [uri (u/join base-uri "api/auth/oauth/" (d/name provider))
params (dissoc params :provider)]
(->> (http/send! {:method :post :uri uri :query params})
(->> (http/send! {:method :post
:uri uri
:credentials "include"
:query params})
(rx/map http/conditional-decode-transit)
(rx/mapcat handle-response))))
@ -95,6 +100,7 @@
[_ params]
(->> (http/send! {:method :post
:uri (u/join base-uri "api/feedback")
:credentials "include"
:body (http/transit-data params)})
(rx/map http/conditional-decode-transit)
(rx/mapcat handle-response)))
@ -104,6 +110,7 @@
(->> (http/send! {:method :post
:uri (u/join base-uri "export")
:body (http/transit-data params)
:credentials "include"
:response-type :blob})
(rx/mapcat handle-response)))
@ -112,6 +119,7 @@
(->> (http/send! {:method :post
:uri (u/join base-uri "export-frames")
:body (http/transit-data params)
:credentials "include"
:response-type :blob})
(rx/mapcat handle-response)))
@ -123,6 +131,7 @@
[id params]
(->> (http/send! {:method :post
:uri (u/join base-uri "api/rpc/mutation/" (name id))
:credentials "include"
:body (http/form-data params)})
(rx/map http/conditional-decode-transit)
(rx/mapcat handle-response)))

View file

@ -54,8 +54,10 @@
{"x-frontend-version" (:full @cfg/version)})
(defn fetch
[{:keys [method uri query headers body mode omit-default-headers]
:or {mode :cors headers {}}}]
[{:keys [method uri query headers body mode omit-default-headers credentials]
:or {mode :cors
headers {}
credentials "same-origin"}}]
(rx/Observable.create
(fn [subscriber]
(let [controller (js/AbortController.)
@ -83,7 +85,7 @@
:body body
:mode (d/name mode)
:redirect "follow"
:credentials "same-origin"
:credentials credentials
:referrerPolicy "no-referrer"
:signal signal}]
(-> (js/fetch (str uri) params)
@ -165,7 +167,6 @@
:uri uri
:response-type :blob
:omit-default-headers true})
(rx/filter #(= 200 (:status %)))
(rx/map :body)
(rx/mapcat wapi/read-file-as-data-url)

View file

@ -9,6 +9,8 @@
(:require
[app.common.transit :as t]
[app.common.uuid :as uuid]
[app.util.globals :refer [global]]
[app.util.object :as obj]
[beicon.core :as rx]))
(declare handle-response)
@ -28,11 +30,13 @@
data (t/encode-str message)
instance (:instance worker)]
(.postMessage instance data)
(->> (:stream worker)
(rx/filter #(= (:reply-to %) sender-id))
(take-messages)
(rx/map handle-response)))))
(if (some? instance)
(do (.postMessage instance data)
(->> (:stream worker)
(rx/filter #(= (:reply-to %) sender-id))
(take-messages)
(rx/map handle-response)))
(rx/empty)))))
(defn ask!
[worker message]
@ -79,6 +83,11 @@
(.addEventListener instance "message" handle-message)
(.addEventListener instance "error" handle-error)
(ask! worker
{:cmd :configure
:params
{"penpotPublicURI" (obj/get global "penpotPublicURI")}})
worker))
(defn- handle-response

View file

@ -7,6 +7,8 @@
(ns app.worker.impl
(:require
[app.common.pages.changes :as ch]
[app.util.globals :refer [global]]
[app.util.object :as obj]
[okulary.core :as l]))
(enable-console-print!)
@ -50,3 +52,8 @@
(assoc :cmd :selection/update-index)))
(handler (-> message
(assoc :cmd :snaps/update-index))))))
(defmethod handler :configure
[{:keys [params]}]
(doseq [[param-key param-value] params]
(obj/set! global param-key param-value)))

View file

@ -7,6 +7,8 @@
(ns app.worker.thumbnails
(:require
["react-dom/server" :as rds]
[app.common.uri :as u]
[app.config :as cfg]
[app.main.exports :as exports]
[app.main.fonts :as fonts]
[app.util.http :as http]
@ -29,11 +31,15 @@
(defn- request-page
[file-id page-id]
(let [uri "/api/rpc/query/page"]
(let [uri (u/join (cfg/get-public-uri) "api/rpc/query/page")
params {:file-id file-id
:id page-id
:strip-thumbnails true}]
(->> (http/send!
{:uri uri
:query {:file-id file-id :id page-id :strip-thumbnails true}
:method :get})
{:method :get
:uri uri
:credentials "include"
:query params})
(rx/map http/conditional-decode-transit)
(rx/mapcat handle-response))))

View file

@ -19,356 +19,338 @@
(t/use-fixtures :each
{:before thp/reset-idmap!})
(t/deftest test-add-component-from-single-shape
(t/async done
(try
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"}))]
;; Test using potok
#_(t/deftest test-add-component-from-single-shape
(t/testing "test-add-component-from-single-shape"
(t/async
done
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"}))
store (ptk/store {:state state})
stream (ptk/input-stream store)
end? (->> stream (rx/filter #(= ::end %)))]
(->> state
(the/do-update (dw/select-shape (thp/id :shape1)))
(the/do-watch-update dwl/add-component)
(rx/do
(fn [new-state]
(let [shape1 (thp/get-shape new-state :shape1)
(->> stream
(rx/take-until end?)
(rx/last)
(rx/do
(fn []
(let [new-state @store
shape1 (thp/get-shape new-state :shape1)
[[group shape1] [c-group c-shape1] component]
(thl/resolve-instance-and-main
[[group shape1] [c-group c-shape1] component]
(thl/resolve-instance-and-main
new-state
(:parent-id shape1))
file (dwlh/get-local-file new-state)]
file (dwlh/get-local-file new-state)]
(t/is (= (:name shape1) "Rect 1"))
(t/is (= (:name group) "Component-1"))
(t/is (= (:name component) "Component-1"))
(t/is (= (:name c-shape1) "Rect 1"))
(t/is (= (:name c-group) "Component-1"))
(t/is (= (:name shape1) "Rect 1"))
(t/is (= (:name group) "Component-1"))
(t/is (= (:name component) "Component-1"))
(t/is (= (:name c-shape1) "Rect 1"))
(t/is (= (:name c-group) "Component-1"))
(thl/is-from-file group file))))
(thl/is-from-file group file))))
(rx/subs
done
#(do
(println (.-stack %))
(done)))))
(rx/subs done #(throw %)))
(catch :default e
(println (.-stack e))
(done)))))
(ptk/emit!
store
(dw/select-shape (thp/id :shape1))
(dwl/add-component)
::end)))))
(t/deftest test-add-component-from-several-shapes
(t/async done
(try
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"})
(thp/sample-shape :shape2 :rect
{:name "Rect 2"}))]
;; FAILING
#_(t/deftest test-add-component-from-single-shape
(t/async
done
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"}))]
(->> state
(the/do-update (dw/select-shapes (lks/set
(thp/id :shape1)
(thp/id :shape2))))
(the/do-watch-update dwl/add-component)
(rx/do
(fn [new-state]
(let [shape1 (thp/get-shape new-state :shape1)
(->> state
(the/do-update (dw/select-shape (thp/id :shape1)))
(the/do-watch-update dwl/add-component)
(rx/do
(fn [new-state]
(let [shape1 (thp/get-shape new-state :shape1)
[[group shape1 shape2]
[c-group c-shape1 c-shape2]
component]
(thl/resolve-instance-and-main
new-state
(:parent-id shape1))
[[group shape1] [c-group c-shape1] component]
(thl/resolve-instance-and-main
new-state
(:parent-id shape1))
file (dwlh/get-local-file new-state)]
file (dwlh/get-local-file new-state)]
;; NOTE: the group name depends on having executed
;; the previous test.
(t/is (= (:name group) "Component-1"))
(t/is (= (:name shape1) "Rect 1"))
(t/is (= (:name shape2) "Rect 2"))
(t/is (= (:name component) "Component-1"))
(t/is (= (:name c-group) "Component-1"))
(t/is (= (:name c-shape1) "Rect 1"))
(t/is (= (:name c-shape2) "Rect 2"))
(t/is (= (:name shape1) "Rect 1"))
(t/is (= (:name group) "Component-1"))
(t/is (= (:name component) "Component-1"))
(t/is (= (:name c-shape1) "Rect 1"))
(t/is (= (:name c-group) "Component-1"))
(thl/is-from-file group file))))
(thl/is-from-file group file))))
(rx/subs
done
#(do
(println (.-stack %))
(done)))))
(rx/subs done #(throw %))))))
(catch :default e
(println (.-stack e))
(done)))))
;; FAILING
#_(t/deftest test-add-component-from-several-shapes
(t/async
done
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"})
(thp/sample-shape :shape2 :rect
{:name "Rect 2"}))]
(->> state
(the/do-update (dw/select-shapes (lks/set
(thp/id :shape1)
(thp/id :shape2))))
(the/do-watch-update dwl/add-component)
(rx/do
(fn [new-state]
(let [shape1 (thp/get-shape new-state :shape1)
[[group shape1 shape2]
[c-group c-shape1 c-shape2]
component]
(thl/resolve-instance-and-main
new-state
(:parent-id shape1))
file (dwlh/get-local-file new-state)]
;; NOTE: the group name depends on having executed
;; the previous test.
(t/is (= (:name group) "Component-1"))
(t/is (= (:name shape1) "Rect 1"))
(t/is (= (:name shape2) "Rect 2"))
(t/is (= (:name component) "Component-1"))
(t/is (= (:name c-group) "Component-1"))
(t/is (= (:name c-shape1) "Rect 1"))
(t/is (= (:name c-shape2) "Rect 2"))
(thl/is-from-file group file))))
(rx/subs done #(throw %))))))
(t/deftest test-add-component-from-group
(t/async done
(try
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"})
(thp/sample-shape :shape2 :rect
{:name "Rect 2"})
(thp/group-shapes :group1
[(thp/id :shape1)
(thp/id :shape2)]))]
#_(t/deftest test-add-component-from-group
(t/async
done
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"})
(thp/sample-shape :shape2 :rect
{:name "Rect 2"})
(thp/group-shapes :group1
[(thp/id :shape1)
(thp/id :shape2)]))]
(->> state
(the/do-update (dw/select-shape (thp/id :group1)))
(the/do-watch-update dwl/add-component)
(rx/do
(fn [new-state]
(let [[[group shape1 shape2]
[c-group c-shape1 c-shape2]
component]
(thl/resolve-instance-and-main
new-state
(thp/id :group1))
(->> state
(the/do-update (dw/select-shape (thp/id :group1)))
(the/do-watch-update dwl/add-component)
(rx/do
(fn [new-state]
(let [[[group shape1 shape2]
[c-group c-shape1 c-shape2]
component]
(thl/resolve-instance-and-main
new-state
(thp/id :group1))
file (dwlh/get-local-file new-state)]
file (dwlh/get-local-file new-state)]
(t/is (= (:name shape1) "Rect 1"))
(t/is (= (:name shape2) "Rect 2"))
(t/is (= (:name group) "Group-1"))
(t/is (= (:name component) "Group-1"))
(t/is (= (:name c-shape1) "Rect 1"))
(t/is (= (:name c-shape2) "Rect 2"))
(t/is (= (:name c-group) "Group-1"))
(t/is (= (:name shape1) "Rect 1"))
(t/is (= (:name shape2) "Rect 2"))
(t/is (= (:name group) "Group-1"))
(t/is (= (:name component) "Group-1"))
(t/is (= (:name c-shape1) "Rect 1"))
(t/is (= (:name c-shape2) "Rect 2"))
(t/is (= (:name c-group) "Group-1"))
(thl/is-from-file group file))))
(thl/is-from-file group file))))
(rx/subs
done
#(do
(println (.-stack %))
(done)))))
(catch :default e
(println (.-stack e))
(done)))))
(rx/subs done #(throw %))))))
(t/deftest test-rename-component
(t/async done
(try
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"})
(thp/make-component :instance1
[(thp/id :shape1)]))
(t/async
done
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"})
(thp/make-component :instance1
[(thp/id :shape1)]))
instance1 (thp/get-shape state :instance1)]
instance1 (thp/get-shape state :instance1)]
(->> state
(the/do-watch-update (dwl/rename-component
(:component-id instance1)
"Renamed component"))
(rx/do
(fn [new-state]
(let [file (dwlh/get-local-file new-state)
component (cph/get-component
(:component-id instance1)
(:component-file instance1)
file
{})]
(->> state
(the/do-watch-update (dwl/rename-component
(:component-id instance1)
"Renamed component"))
(rx/do
(fn [new-state]
(let [file (dwlh/get-local-file new-state)
component (cph/get-component
(:component-id instance1)
(:component-file instance1)
file
{})]
(t/is (= (:name component)
"Renamed component")))))
(t/is (= (:name component)
"Renamed component")))))
(rx/subs
done
#(do
(println (.-stack %))
(done)))))
(catch :default e
(println (.-stack e))
(done)))))
(rx/subs done #(throw %))))))
(t/deftest test-duplicate-component
(t/async done
(try
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"})
(thp/make-component :instance1
[(thp/id :shape1)]))
(t/async
done
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"})
(thp/make-component :instance1
[(thp/id :shape1)]))
instance1 (thp/get-shape state :instance1)
component-id (:component-id instance1)]
instance1 (thp/get-shape state :instance1)
component-id (:component-id instance1)]
(->> state
(the/do-watch-update (dwl/duplicate-component
{:id component-id}))
(rx/do
(fn [new-state]
(let [new-component-id (->> (get-in new-state
[:workspace-data
:components])
(keys)
(filter #(not= % component-id))
(first))
(->> state
(the/do-watch-update (dwl/duplicate-component
{:id component-id}))
(rx/do
(fn [new-state]
(let [new-component-id (->> (get-in new-state
[:workspace-data
:components])
(keys)
(filter #(not= % component-id))
(first))
[[instance1 shape1]
[c-instance1 c-shape1]
component1]
(thl/resolve-instance-and-main
new-state
(:id instance1))
[[instance1 shape1]
[c-instance1 c-shape1]
component1]
(thl/resolve-instance-and-main
new-state
(:id instance1))
[[c-component2 c-shape2]
component2]
(thl/resolve-component
new-state
new-component-id)]
[[c-component2 c-shape2]
component2]
(thl/resolve-component
new-state
new-component-id)]
(t/is (= (:name component2)
"Component-2")))))
(t/is (= (:name component2)
"Component-2")))))
(rx/subs
done
#(do
(println (.-stack %))
(done)))))
(catch :default e
(println (.-stack e))
(done)))))
(rx/subs done #(throw %))))))
(t/deftest test-delete-component
(t/async done
(try
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"})
(thp/make-component :instance1
[(thp/id :shape1)]))
(t/async
done
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"})
(thp/make-component :instance1
[(thp/id :shape1)]))
instance1 (thp/get-shape state :instance1)
component-id (:component-id instance1)]
instance1 (thp/get-shape state :instance1)
component-id (:component-id instance1)]
(->> state
(the/do-watch-update (dwl/delete-component
{:id component-id}))
(rx/do
(fn [new-state]
(let [[instance1 shape1]
(thl/resolve-instance
new-state
(:id instance1))
(->> state
(the/do-watch-update (dwl/delete-component
{:id component-id}))
(rx/do
(fn [new-state]
(let [[instance1 shape1]
(thl/resolve-instance
new-state
(:id instance1))
file (dwlh/get-local-file new-state)
component (cph/get-component
(:component-id instance1)
(:component-file instance1)
file
{})]
file (dwlh/get-local-file new-state)
component (cph/get-component
(:component-id instance1)
(:component-file instance1)
file
{})]
(t/is (nil? component)))))
(t/is (nil? component)))))
(rx/subs
done
#(do
(println (.-stack %))
(done)))))
(catch :default e
(println (.-stack e))
(done)))))
(rx/subs done #(throw %))))))
(t/deftest test-instantiate-component
(t/async done
(try
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"})
(thp/make-component :instance1
[(thp/id :shape1)]))
(t/async
done
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"})
(thp/make-component :instance1
[(thp/id :shape1)]))
file (dwlh/get-local-file state)
instance1 (thp/get-shape state :instance1)
component-id (:component-id instance1)]
file (dwlh/get-local-file state)
instance1 (thp/get-shape state :instance1)
component-id (:component-id instance1)]
(->> state
(the/do-watch-update (dwl/instantiate-component
(:id file)
(:component-id instance1)
(gpt/point 100 100)))
(rx/do
(fn [new-state]
(let [new-instance-id (-> new-state
wsh/lookup-selected
first)
(->> state
(the/do-watch-update (dwl/instantiate-component
(:id file)
(:component-id instance1)
(gpt/point 100 100)))
(rx/do
(fn [new-state]
(let [new-instance-id (-> new-state
wsh/lookup-selected
first)
[[instance2 shape2]
[c-instance2 c-shape2]
component]
(thl/resolve-instance-and-main
new-state
new-instance-id)]
[[instance2 shape2]
[c-instance2 c-shape2]
component]
(thl/resolve-instance-and-main
new-state
new-instance-id)]
(t/is (not= (:id instance1) (:id instance2)))
(t/is (= (:id component) component-id))
(t/is (= (:name instance2) "Component-2"))
(t/is (= (:name shape2) "Rect 1"))
(t/is (= (:name c-instance2) "Component-1"))
(t/is (= (:name c-shape2) "Rect 1")))))
(t/is (not= (:id instance1) (:id instance2)))
(t/is (= (:id component) component-id))
(t/is (= (:name instance2) "Component-2"))
(t/is (= (:name shape2) "Rect 1"))
(t/is (= (:name c-instance2) "Component-1"))
(t/is (= (:name c-shape2) "Rect 1")))))
(rx/subs
done
#(do
(println (.-stack %))
(done)))))
(catch :default e
(println (.-stack e))
(done)))))
(rx/subs done #(throw %))))))
(t/deftest test-detach-component
(t/async done
(try
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"})
(thp/make-component :instance1
[(thp/id :shape1)]))
(t/async
done
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"})
(thp/make-component :instance1
[(thp/id :shape1)]))
instance1 (thp/get-shape state :instance1)
component-id (:component-id instance1)]
instance1 (thp/get-shape state :instance1)
component-id (:component-id instance1)]
(->> state
(the/do-watch-update (dwl/detach-component
(:id instance1)))
(rx/do
(fn [new-state]
(let [[instance1 shape1]
(thl/resolve-noninstance
new-state
(:id instance1))]
(->> state
(the/do-watch-update (dwl/detach-component
(:id instance1)))
(rx/do
(fn [new-state]
(let [[instance1 shape1]
(thl/resolve-noninstance
new-state
(:id instance1))]
(t/is (= (:name "Rect 1"))))))
(t/is (= (:name "Rect 1"))))))
(rx/subs
done
#(do
(println (.-stack %))
(done)))))
(catch :default e
(println (.-stack e))
(done)))))
(rx/subs done #(throw %))))))

File diff suppressed because it is too large Load diff

View file

@ -44,15 +44,32 @@ function pull-devenv-if-not-exists {
function start-devenv {
pull-devenv-if-not-exists $@;
docker-compose -p $DEVENV_PNAME -f docker/devenv/docker-compose.yaml up -d;
# Check if the "backend-only" container is running. If it is, we need tot stop it first
if [[ ! $(docker ps -f "name=penpot-backend" -q) ]]; then
docker-compose -p $DEVENV_PNAME --profile backend -f docker/devenv/docker-compose.yaml stop -t 2 backend;
fi
docker-compose -p $DEVENV_PNAME --profile full -f docker/devenv/docker-compose.yaml up -d;
}
function start-backend {
pull-devenv-if-not-exists $@;
# Check if the "devenv" container is running. If it is, we need tot stop it first because conflicts with the backend
if [[ ! $(docker ps -f "name=penpot-devenv-main" -q) ]]; then
docker-compose -p $DEVENV_PNAME --profile full -f docker/devenv/docker-compose.yaml stop -t 2 main;
fi
docker-compose -p $DEVENV_PNAME --profile backend -f docker/devenv/docker-compose.yaml up -d;
}
function stop-devenv {
docker-compose -p $DEVENV_PNAME -f docker/devenv/docker-compose.yaml stop -t 2;
docker-compose -p $DEVENV_PNAME --profile full --profile backend -f docker/devenv/docker-compose.yaml stop -t 2;
}
function drop-devenv {
docker-compose -p $DEVENV_PNAME -f docker/devenv/docker-compose.yaml down -t 2 -v;
docker-compose -p $DEVENV_PNAME --profile full --profile backend -f docker/devenv/docker-compose.yaml down -t 2 -v;
echo "Clean old development image $DEVENV_IMGNAME..."
docker images $DEVENV_IMGNAME -q | awk '{print $3}' | xargs --no-run-if-empty docker rmi
@ -70,6 +87,14 @@ function run-devenv {
docker exec -ti penpot-devenv-main sudo -EH -u penpot /home/start-tmux.sh
}
function run-backend {
if [[ ! $(docker ps -f "name=penpot-backend" -q) ]]; then
start-backend
fi
docker exec -ti penpot-backend sudo -EH -u penpot /home/start-tmux-back.sh
}
function build {
echo ">> build start: $1"
local version=$(print-current-version);
@ -175,6 +200,8 @@ function usage {
echo "- stop-devenv Stops the development oriented docker-compose service."
echo "- drop-devenv Remove the development oriented docker-compose containers, volumes and clean images."
echo "- run-devenv Attaches to the running devenv container and starts development environment"
echo "- start-backend Start the backend only service."
echo "- run-backend Starts a backend-only instance and attach tmux to it"
echo " based on tmux (frontend at localhost:3449, backend at localhost:6060)."
echo ""
}
@ -196,9 +223,15 @@ case $1 in
start-devenv)
start-devenv ${@:2}
;;
start-backend)
start-backend ${@:2}
;;
run-devenv)
run-devenv ${@:2}
;;
run-backend)
run-backend ${@:2}
;;
stop-devenv)
stop-devenv ${@:2}
;;