transform-in-variant (Ctrl+K on a component) runs a chain of events,
each one committing its own changes. It set the variant-id on the main
instance before setting it on the component, so the shape validation
of that step saw them out of sync and rejected the change. The next
steps then failed too, as the main instance was never a variant.
Set the variant-id on the component first, and give the main instance
an empty variant-name, which is what the validator expects while the
variant has no properties yet.
AI-assisted-by: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The page index worker applies the same changes to its own copy of the
pages, which has no file id and no components. :validate-shapes changes
then always failed there, so the worker logged "error updating page
index" and stopped updating the snap and selection indexes of that page
after any validated change. Drop :validate-shapes changes before
applying them in the worker.
AI-assisted-by: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cover the callers of the variant generators that had no tests with the
new validation: restoring a deleted variant into its container, restoring
it after its container was deleted, and adding a property and then
setting its value, as the workspace properties panel does. Each case
runs with valid data and expects no validation error on JS.
AI-assisted-by: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When generate-relocate moves a variant inside a component, the variant
is no longer a root, so relocate left it out and validated nothing in
its place. Validate the root of the enclosing component instead. The
workspace does not allow this move, but relocate can be called
directly.
Document that the default call of generate-make-shapes-no-variant
requires the shapes to be outside their variant container.
Add tests for moving a variant into another container, and into a
component with a corrupted root.
AI-assisted-by: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Validating a variant container already checks each of its main
instances, so listing the mains next to it repeated the work. Each id
costs a pass over the whole page, so a variant with N components cost
N+1 passes per call, and each error was reported twice.
The generators in variant_properties now validate only the container.
generate-update-property-value and generate-set-variant-error fall back
to the main instance when the component is not a variant. Before, they
emitted a nil id, which fails the :validate-shapes schema.
Add a test that generate-set-variant-error on a component that is not a
variant produces valid changes.
AI-assisted-by: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
duplicate-variant calls generate-make-shapes-variant before it moves the
new shape into the target container. When a variant is pasted into a
different variant container, the new shape already has the target
container's data but still sits in the old one, so the validation in
generate-make-shapes-variant reported errors on valid data.
Pass :skip-validation? to generate-make-shapes-variant, and validate the
target container once the shape is inside it. That also covers the
pasted shape, which the closing validation of
generate-duplicate-changes missed.
Add tests for pasting a variant into another container and for
duplicating it inside the same container.
AI-assisted-by: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
generate-make-shapes-variant now validates the new variants and their
variant container, and accepts a :skip-validation? option.
generate-relocate calls it before the shapes are inside the container,
so it passes :skip-validation?. Relocate already validates the new
container once the shapes are moved.
Add tests that corrupt the new variant or the container and expect a
validation error, and a test for :skip-validation?.
AI-assisted-by: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
generate-make-shapes-no-variant now validates the shapes and their old
variant containers, and accepts a :skip-validation? option.
generate-relocate calls it before moving the shapes, while they are
still inside the container. Validating the container at that point
reported :main-instance-not-a-variant on valid data. Relocate now passes
:skip-validation? and validates at the end: the old and new variant
containers, and the moved variants when they end up outside any
component. Inside a component they are no longer roots, and the
validator can't check them on their own.
Add tests for the function and for moving a variant out of its
container, with valid and corrupted data.
AI-assisted-by: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
generate-add-new-property now validates the main instances and the
variant container. Add a :skip-validation? option to it.
generate-make-shapes-variant calls it several times before the new
shapes become variants. Validating the container at that point reported
:main-instance-not-a-variant on valid data, so it now passes
:skip-validation? and will validate once the shapes are variants.
Add tests that corrupt a main instance or the container and expect a
validation error. Add a test that moves into a variant a component whose
name needs more properties, which runs that intermediate path. Register
variants-test in the runner so that it also runs on JS.
AI-assisted-by: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
generate-reorder-variant-poperties now validates the main instances and
the variant container, but only when the reorder changed something, so
a no-op still returns the same changes. Add tests that corrupt a main
instance or the container and expect a validation error.
AI-assisted-by: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
generate-set-variant-error now validates the main instance of the
component and its variant container. The validator has no rule for
:variant-error itself, so the new tests corrupt the main instance or the
container and expect a validation error.
AI-assisted-by: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
generate-update-property-value now validates the main instance of the
component and its variant container once the new value is set. Add tests
that corrupt the main instance or the container and expect a
validation error.
AI-assisted-by: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
generate-instantiate-component validated the new instance on its own.
When the parent is inside another component, the new instance is a
nested copy. The validator starts with a :not-component context, so it
reported :nested-copy-not-allowed. Validate the root of the enclosing
component instead, which walks the new copy with the right context.
During a component swap, generate-new-shape-for-swap sets the swap slot
after the instance is created. Validating at instantiation time then
reported :missing-slot. Add a :skip-validation? option to
generate-instantiate-component. The swap sets it and validates the
enclosing root once the swap slot is in place.
These false errors broke 34 common tests on JS.
AI-assisted-by: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
apply-changes-local replays the pending redo changes on a local copy of
the data, so that later builder calls can read up-to-date values. It
replayed :validate-shapes too, so the validator ran on that copy, which
has no real file id and no libraries. Any chain of two generators that
apply local changes then failed with false errors, such as
:component-not-found. Skip :validate-shapes when applying changes
locally.
Add the variant container to the shapes that
generate-update-property-name and generate-remove-property validate.
Validating only the main instances misses errors that only the container
check finds, such as siblings with different property names.
Register variant-properties-test in the runner so that it runs on JS,
where validation takes place. Add tests that corrupt a main instance or
the container and expect a validation error. Remove two debug prn calls
from the validator.
AI-assisted-by: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Parallel deploy jobs on the shared self-hosted host raced on
/home/runner/setup-pnpm (ENOTEMPTY / missing tarball errors).
Use penpotapp/devenv, which already ships the Node and pnpm versions
pinned in develop, and mount a persistent pnpm store from the host
instead of pnpm/setup + actions/cache.
Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
Values coming from `${{ }}` expressions were interpolated directly into
`run:` scripts, so GitHub substituted them into the shell source before
bash parsed it. A commit title containing a double quote broke the
"Write step summary" step of the bundle build with a syntax error, and
the same pattern allowed arbitrary command execution on the
self-hosted runners.
Pass every expression used inside `run:` through step/job `env:` and
reference it as a quoted shell variable instead. Use the runner's
default variables (GITHUB_RUN_ID, GITHUB_REPOSITORY, ...) where the
value comes from the `github` context.
Also validate `plugin_name` in plugins-deploy-package.yml against
`^[a-z0-9][a-z0-9-]*$`, since it is free-form and reaches paths,
worker names, GITHUB_ENV and action inputs.
Affected workflows: build-bundle, build-docker,
build-docker-admin-console, plugins-deploy-package,
plugins-deploy-api-doc, plugins-deploy-styles-doc, release, tests-e2e.
Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
Firefox draws its own resize grip for a box with `resize: both` and,
unlike Chrome and Safari, gives no way to hide it: `::-webkit-resizer`
is ignored. The plugin modal therefore showed two grips in its
bottom-right corner, its own icon and the one from the browser.
Show the custom icon only where the browser grip can be hidden. In
Firefox the native grip is now the only one.
Closes#11795
* ✨ Show color tokens toggle on canvas background picker
* 🐛 Apply token click on canvas background with no shape selected
* ✨ Apply and persist color tokens on canvas background
* 🌐 Translate canvas background section label