Merge remote-tracking branch 'origin/staging' into develop

This commit is contained in:
Andrey Antukh 2025-05-21 09:23:15 +02:00
commit a63fa2944d
19 changed files with 127 additions and 98 deletions

View file

@ -134,6 +134,8 @@ A non exhaustive list of changes:
- Fix problem with path edition of shapes [Taiga #9496](https://tree.taiga.io/project/penpot/issue/9496)
- Fix exception on paste invalid html [Taiga #11047](https://tree.taiga.io/project/penpot/issue/11047)
- Fix share button being displayed with no permissions [Taiga #11086](https://tree.taiga.io/project/penpot/issue/11086)
- Fix inline styles in code tab [Taiga Issue #7583](https://tree.taiga.io/project/penpot/issue/7583)
- Fix exception on returning openapi.json
## 2.6.2

View file

@ -9,7 +9,6 @@
(:refer-clojure :exclude [tap])
(:require
[app.common.data :as d]
[app.common.exceptions :as ex]
[app.common.logging :as l]
[app.common.transit :as t]
[app.http.errors :as errors]
@ -54,18 +53,20 @@
::yres/status 200
::yres/body (yres/stream-body
(fn [_ output]
(binding [events/*channel* (sp/chan :buf buf :xf (keep encode))]
(let [listener (events/start-listener
(partial write! output)
(partial pu/close! output))]
(try
(let [channel (sp/chan :buf buf :xf (keep encode))
listener (events/start-listener
channel
(partial write! output)
(partial pu/close! output))]
(try
(binding [events/*channel* channel]
(let [result (handler)]
(events/tap :end result))
(catch Throwable cause
(events/tap :error (errors/handle' cause request))
(when-not (ex/instance? java.io.EOFException cause)
(binding [l/*context* (errors/request->context request)]
(l/err :hint "unexpected error on processing sse response" :cause cause))))
(finally
(sp/close! events/*channel*)
(px/await! listener)))))))}))
(events/tap :end result)))
(catch Throwable cause
(let [result (errors/handle' cause request)]
(events/tap channel :error result)))
(finally
(sp/close! channel)
(px/await! listener))))))}))

View file

@ -92,9 +92,9 @@
[:string {:max 250}]
[::sm/one-of {:format "string"} valid-event-types]]]
[:props
[:map-of :keyword :any]]
[:map-of :keyword ::sm/any]]
[:context {:optional true}
[:map-of :keyword :any]]])
[:map-of :keyword ::sm/any]]])
(def schema:push-audit-events
[:map {:title "push-audit-events"}

View file

@ -115,7 +115,8 @@
(db/update! pool :project
{:modified-at (dt/now)}
{:id project-id})
{:id project-id}
{::db/return-keys false})
result))

View file

@ -189,7 +189,7 @@
[:is-shared ::sm/boolean]
[:project-id ::sm/uuid]
[:created-at ::dt/instant]
[:data {:optional true} :any]])
[:data {:optional true} ::sm/any]])
(def schema:permissions-mixin
[:map {:title "PermissionsMixin"}

View file

@ -80,9 +80,9 @@
(def ^:private schema:create-font-variant
[:map {:title "create-font-variant"}
[:team-id ::sm/uuid]
[:data [:map-of :string :any]]
[:data [:map-of ::sm/text ::sm/any]]
[:font-id ::sm/uuid]
[:font-family :string]
[:font-family ::sm/text]
[:font-weight [::sm/one-of {:format "number"} valid-weight]]
[:font-style [::sm/one-of {:format "string"} valid-style]]])

View file

@ -9,6 +9,7 @@
(:require
[app.common.data :as d]
[app.common.exceptions :as ex]
[app.common.json :as json]
[app.common.pprint :as pp]
[app.common.schema :as sm]
[app.common.schema.desc-js-like :as smdj]
@ -19,7 +20,6 @@
[app.http.sse :as-alias sse]
[app.loggers.webhooks :as-alias webhooks]
[app.rpc :as-alias rpc]
[app.util.json :as json]
[app.util.services :as sv]
[app.util.template :as tmpl]
[clojure.java.io :as io]
@ -86,7 +86,7 @@
(fn [request]
(let [params (:query-params request)
pstyle (:type params "js")
context (assoc context :param-style pstyle)]
context (assoc @context :param-style pstyle)]
{::yres/status 200
::yres/body (-> (io/resource "app/templates/api-doc.tmpl")
@ -178,8 +178,7 @@
(fn [_]
{::yres/status 200
::yres/headers {"content-type" "application/json; charset=utf-8"}
::yres/body (json/encode context)})
::yres/body (json/encode @context)})
(fn [_]
{::yres/status 404})))
@ -209,7 +208,7 @@
(defmethod ig/init-key ::routes
[_ {:keys [::rpc/methods] :as cfg}]
[(let [context (prepare-doc-context methods)]
[(let [context (delay (prepare-doc-context methods))]
[["/_doc"
{:handler (doc-handler context)
:allowed-methods #{:get}}]
@ -217,7 +216,7 @@
{:handler (doc-handler context)
:allowed-methods #{:get}}]])
(let [context (prepare-openapi-context methods)]
(let [context (delay (prepare-openapi-context methods))]
[["/openapi"
{:handler (openapi-handler)
:allowed-methods #{:get}}]

View file

@ -10,7 +10,6 @@
to them. Mainly used in http.sse for progress reporting."
(:refer-clojure :exclude [tap run!])
(:require
[app.common.data.macros :as dm]
[app.common.exceptions :as ex]
[app.common.logging :as l]
[promesa.exec :as px]
@ -18,33 +17,30 @@
(def ^:dynamic *channel* nil)
(defn channel
[]
(sp/chan :buf 32))
(defn tap
[type data]
(when-let [channel *channel*]
(sp/put! channel [type data])
nil))
([type data]
(when-let [channel *channel*]
(sp/put! channel [type data])
nil))
([channel type data]
(when channel
(sp/put! channel [type data])
nil)))
(defn start-listener
[on-event on-close]
(dm/assert!
"expected active events channel"
(sp/chan? *channel*))
[channel on-event on-close]
(assert (sp/chan? channel) "expected active events channel")
(px/thread
{:virtual true}
(try
(loop []
(when-let [event (sp/take! *channel*)]
(when-let [event (sp/take! channel)]
(let [result (ex/try! (on-event event))]
(if (ex/exception? result)
(do
(l/wrn :hint "unexpected exception" :cause result)
(sp/close! *channel*))
(sp/close! channel))
(recur)))))
(finally
(on-close)))))
@ -55,7 +51,7 @@
[f on-event]
(binding [*channel* (sp/chan :buf 32)]
(let [listener (start-listener on-event (constantly nil))]
(let [listener (start-listener *channel* on-event (constantly nil))]
(try
(f)
(finally

View file

@ -47,14 +47,14 @@
[:type [:= :assign]]
;; NOTE: the full decoding is happening on the handler because it
;; needs a proper context of the current shape and its type
[:value [:map-of :keyword :any]]
[:value [:map-of :keyword ::sm/any]]
[:ignore-touched {:optional true} :boolean]
[:ignore-geometry {:optional true} :boolean]]]
[:set
[:map {:title "SetOperation"}
[:type [:= :set]]
[:attr :keyword]
[:val :any]
[:val ::sm/any]
[:ignore-touched {:optional true} :boolean]
[:ignore-geometry {:optional true} :boolean]]]
[:set-touched
@ -238,9 +238,9 @@
[:component-id {:optional true} ::sm/uuid]
[:ignore-touched {:optional true} :boolean]
[:parent-id ::sm/uuid]
[:shapes :any]
[:shapes ::sm/any]
[:index {:optional true} [:maybe :int]]
[:after-shape {:optional true} :any]
[:after-shape {:optional true} ::sm/any]
[:component-swap {:optional true} :boolean]]]
[:reorder-children
@ -250,14 +250,14 @@
[:component-id {:optional true} ::sm/uuid]
[:ignore-touched {:optional true} :boolean]
[:parent-id ::sm/uuid]
[:shapes :any]]]
[:shapes ::sm/any]]]
[:add-page
[:map {:title "AddPageChange"}
[:type [:= :add-page]]
[:id {:optional true} ::sm/uuid]
[:name {:optional true} :string]
[:page {:optional true} :any]]]
[:page {:optional true} ::sm/any]]]
[:mod-page
[:map {:title "ModPageChange"}
@ -327,14 +327,14 @@
[:type [:= :add-component]]
[:id ::sm/uuid]
[:name :string]
[:shapes {:optional true} [:vector {:gen/max 3} :any]]
[:shapes {:optional true} [:vector {:gen/max 3} ::sm/any]]
[:path {:optional true} :string]]]
[:mod-component
[:map {:title "ModCompoenentChange"}
[:type [:= :mod-component]]
[:id ::sm/uuid]
[:shapes {:optional true} [:vector {:gen/max 3} :any]]
[:shapes {:optional true} [:vector {:gen/max 3} ::sm/any]]
[:name {:optional true} :string]
[:variant-id {:optional true} ::sm/uuid]
[:variant-properties {:optional true} [:vector ::ctv/variant-property]]]]
@ -411,7 +411,7 @@
[:set-tokens-lib
[:map {:title "SetTokensLib"}
[:type [:= :set-tokens-lib]]
[:tokens-lib :any]]]
[:tokens-lib ::sm/any]]]
[:set-token-set
[:map {:title "SetTokenSetChange"}

View file

@ -24,18 +24,19 @@
;; Auxiliary functions to help create a set of changes (undo + redo)
(sm/register!
^{::sm/type ::changes}
[:map {:title "changes"}
[:redo-changes vector?]
[:undo-changes seq?]
[:origin {:optional true} any?]
[:save-undo? {:optional true} boolean?]
[:stack-undo? {:optional true} boolean?]
[:undo-group {:optional true} any?]])
(def schema:changes
(sm/register!
^{::sm/type ::changes}
[:map {:title "changes"}
[:redo-changes vector?]
[:undo-changes seq?]
[:origin {:optional true} ::sm/any]
[:save-undo? {:optional true} boolean?]
[:stack-undo? {:optional true} boolean?]
[:undo-group {:optional true} ::sm/any]]))
(def check-changes!
(sm/check-fn ::changes))
(sm/check-fn schema:changes))
(defn empty-changes
([origin page-id]

View file

@ -997,6 +997,8 @@
{:title "agent"
:description "instance of clojure agent"}}))
(register! ::any (mu/update-properties :any assoc :gen/gen sg/any))
;; ---- PREDICATES
(def valid-safe-number?

View file

@ -7,6 +7,7 @@
(ns app.common.schema.desc-js-like
(:require
[app.common.data :as d]
[app.common.schema :as-alias sm]
[cuerdas.core :as str]
[malli.core :as m]
[malli.util :as mu]))
@ -90,7 +91,7 @@
(defmethod visit :int [_ schema _ _] (str "integer" (-titled schema) (-min-max-suffix-number schema)))
(defmethod visit :double [_ schema _ _] (str "double" (-titled schema) (-min-max-suffix-number schema)))
(defmethod visit :select-keys [_ schema _ options] (describe* (m/deref schema) options))
(defmethod visit :and [_ s children _] (str (str/join ", and " children) (-titled s)))
(defmethod visit :and [_ s children _] (str (str/join " && " children) (-titled s)))
(defmethod visit :enum [_ s children _options] (str "enum" (-titled s) " of " (str/join ", " children)))
(defmethod visit :maybe [_ _ children _] (str (first children) " nullable"))
(defmethod visit :tuple [_ _ children _] (str "(" (str/join ", " children) ")"))
@ -106,7 +107,8 @@
(defmethod visit :qualified-symbol [_ _ _ _] "qualified symbol")
(defmethod visit :uuid [_ _ _ _] "uuid")
(defmethod visit :boolean [_ _ _ _] "boolean")
(defmethod visit :keyword [_ _ _ _] "keyword")
(defmethod visit :keyword [_ _ _ _] "string")
(defmethod visit :fn [_ _ _ _] "FN")
(defmethod visit :vector [_ _ children _]
(str "[" (last children) "]"))
@ -123,10 +125,12 @@
(defmethod visit :repeat [_ schema children _]
(str "repeat " (-diamond (first children)) (-repeat-suffix schema)))
(defmethod visit :set [_ schema children _]
(str "set[" (first children) "]" (minmax-suffix schema)))
(defmethod visit ::sm/set [_ schema children _]
(str "set[" (first children) "]" (minmax-suffix schema)))
(defmethod visit ::m/val [_ schema children _]
(let [suffix (minmax-suffix schema)]
(cond-> (first children)
@ -152,7 +156,6 @@
(or (:title props)
"*")))
(defmethod visit :map
[_ schema children {:keys [::level ::max-level] :as options}]
(let [props (m/properties schema)
@ -172,13 +175,11 @@
": " s)))
(str/join ",\n"))
header (cond-> (if (zero? level)
(str "type " title)
(str title))
header (cond-> (str "type " title)
closed? (str "!")
(some? title) (str " "))]
(str header "{\n" entries "\n" (pad "}" level))))))
(str (pad header level) "{\n" entries "\n" (pad "}\n" level))))))
(defmethod visit :multi
[_ s children {:keys [::level ::max-level] :as options}]
@ -205,18 +206,18 @@
(defmethod visit :merge
[_ schema children _]
(let [entries (str/join " , " children)
(let [entries (str/join ",\n" children)
props (m/properties schema)
title (or (some-> (:title props) str/camel str/capital)
"<untitled>")]
(str "merge object " title " { " entries " }")))
(str "merge type " title " { \n" entries "\n}\n")))
(defmethod visit :app.common.schema/one-of
[_ _ children _]
(defmethod visit ::sm/one-of
[_ _ children _]
(let [elems (last children)]
(str "OneOf[" (->> elems
(map d/name)
(str/join ",")) "]")))
(str "string oneOf (" (->> elems
(map d/name)
(str/join "|")) ")")))
(defmethod visit :schema [_ schema children options]
(visit ::m/schema schema children options))

View file

@ -5,7 +5,7 @@
;; Copyright (c) KALEIDOS INC
(ns app.common.schema.generators
(:refer-clojure :exclude [set subseq uuid filter map let boolean vector])
(:refer-clojure :exclude [set subseq uuid filter map let boolean vector keyword int double])
#?(:cljs (:require-macros [app.common.schema.generators]))
(:require
[app.common.schema.registry :as sr]
@ -38,10 +38,6 @@
([s opts]
(mg/generator s (assoc opts :registry sr/default-registry))))
(defn filter
[pred gen]
(tg/such-that pred gen 100))
(defn small-double
[& {:keys [min max] :or {min -100 max 100}}]
(tg/double* {:min min, :max max, :infinite? false, :NaN? false}))
@ -61,7 +57,7 @@
(defn word-keyword
[]
(->> (word-string)
(tg/fmap keyword)))
(tg/fmap c/keyword)))
(defn email
[]
@ -100,12 +96,11 @@
(c/map second))
(c/map list bools elements)))))))
(def any tg/any)
(def boolean tg/boolean)
(defn set
[g]
(tg/set g))
(defn map-of
([kg vg]
(tg/map kg vg {:min-elements 1 :max-elements 3}))
([kg vg opts]
(tg/map kg vg opts)))
(defn elements
[s]
@ -119,6 +114,10 @@
[f g]
(tg/fmap f g))
(defn filter
[pred gen]
(tg/such-that pred gen 100))
(defn mcat
[f g]
(tg/bind g f))
@ -130,3 +129,18 @@
(defn vector
[& opts]
(apply tg/vector opts))
(defn set
[g]
(tg/set g))
;; Static Generators
(def boolean tg/boolean)
(def text (word-string))
(def double (small-double))
(def int (small-int))
(def keyword (word-keyword))
(def any
(tg/one-of [text boolean double int keyword]))

View file

@ -97,7 +97,8 @@
(defmethod visit :enum [_ _ children options] (merge (some-> (m/-infer children) (transform* options)) {:enum children}))
(defmethod visit :maybe [_ _ children _] {:oneOf (conj children {:type "null"})})
(defmethod visit :tuple [_ _ children _] {:type "array", :items children, :additionalItems false})
(defmethod visit :re [_ schema _ options] {:type "string", :pattern (first (m/children schema options))})
(defmethod visit :re [_ schema _ options]
{:type "string", :pattern (str (first (m/children schema options)))})
(defmethod visit :nil [_ _ _ _] {:type "null"})
(defmethod visit :string [_ schema _ _]

View file

@ -35,7 +35,7 @@
(.. r (toString 16) (padStart 2 "0"))
(.. g (toString 16) (padStart 2 "0"))
(.. b (toString 16) (padStart 2 "0"))))))
sg/any))
sg/int))
(defn rgb-color-string?
[o]

View file

@ -118,7 +118,7 @@
[:map {:title "Token"}
[:name cto/token-name-ref]
[:type [::sm/one-of cto/token-types]]
[:value :any]
[:value ::sm/any]
[:description {:optional true} :string]
[:modified-at {:optional true} ::sm/inst]])
@ -389,7 +389,8 @@
[:description {:optional true} :string]
[:modified-at {:optional true} ::sm/inst]
[:tokens {:optional true
:gen/gen (->> (sg/generator [:map-of ::sm/text schema:token])
:gen/gen (->> (sg/map-of (sg/generator ::sm/text)
(sg/generator schema:token))
(sg/fmap #(into (d/ordered-map) %)))}
[:and
[:map-of {:gen/max 5

View file

@ -5,6 +5,7 @@
set -ex
export INCLUDE_STORYBOOK=${BUILD_STORYBOOK:-no};
export INCLUDE_WASM=${BUILD_WASM:-yes};
export CURRENT_VERSION=$1;
export BUILD_DATE=$(date -R);
@ -17,14 +18,18 @@ export TS=$(date +%s);
export NODE_ENV=production;
corepack enable;
corepack up || exit 1;
corepack install || exit 1;
yarn install || exit 1;
rm -rf resources/public;
rm -rf target/dist;
yarn run build:app:main --config-merge "{:release-version \"${CURRENT_HASH}-${TS}\"}" $EXTRA_PARAMS || exit 1
yarn run build:wasm || exit 1;
if [ "$INCLUDE_WASM" = "yes" ]; then
yarn run build:wasm || exit 1;
fi
yarn run build:app:libs || exit 1;
yarn run build:app:assets || exit 1;
@ -36,7 +41,10 @@ sed -i -re "s/\%version\%/$CURRENT_VERSION/g" ./target/dist/render.html;
sed -i -re "s/\%version\%/$CURRENT_VERSION/g" ./target/dist/rasterizer.html;
sed -i -re "s/\%buildDate\%/$BUILD_DATE/g" ./target/dist/index.html;
sed -i -re "s/\%buildDate\%/$BUILD_DATE/g" ./target/dist/rasterizer.html;
sed -i "s/version=develop/version=$CURRENT_VERSION/g" ./target/dist/js/render_wasm.js;
if [ "$INCLUDE_WASM" = "yes" ]; then
sed -i "s/version=develop/version=$CURRENT_VERSION/g" ./target/dist/js/render_wasm.js;
fi
if [ "$INCLUDE_STORYBOOK" = "yes" ]; then
# build storybook

View file

@ -38,7 +38,8 @@
indent))
(cfh/text-shape? shape)
(let [text-shape-html (rds/renderToStaticMarkup (mf/element text/text-shape #js {:shape shape :code? true}))]
(let [text-shape-html (rds/renderToStaticMarkup (mf/element text/text-shape #js {:shape shape :code? true}))
text-shape-html (str/replace text-shape-html #"style\s*=\s*[\"'][^\"']*[\"']" "")]
(dm/fmt "%<div class=\"%\">\n%\n%</div>"
indent
(dm/str "shape " (d/name (:type shape)) " "

View file

@ -115,6 +115,7 @@ function build {
--mount source=`pwd`,type=bind,target=/home/penpot/penpot \
-e EXTERNAL_UID=$CURRENT_USER_ID \
-e BUILD_STORYBOOK=$BUILD_STORYBOOK \
-e BUILD_WASM=$BUILD_WASM \
-e SHADOWCLJS_EXTRA_PARAMS=$SHADOWCLJS_EXTRA_PARAMS \
-e JAVA_OPTS="$JAVA_OPTS" \
-w /home/penpot/penpot/$1 \