-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Blocks: Versioning and migrations #2541
Comments
Thanks for opening this issue, this is something we need to figure out.
|
Migrations! Yay! Heavy +1 on doing 3, then 4. Happy to help on this one, data migrations are a thing I did lots. |
Possible implementation is to have registerBlock( 'myplugin/foo', {
version: 1,
attributes: {
// v1 attributes ...
},
} );
registerBlock( 'myplugin/foo', {
version: 2,
attributes: {
// v2 attributes ...
},
migrate( attributes ) {
// given v1 attributes, return v2 attributes
},
} );
registerBlock( 'myplugin/foo', {
version: 3,
attributes: {
// v3 attributes ...
},
migrate( attributes ) {
// given v2 attributes, return v3 attributes
},
edit() {
// current implementation
},
save() {
// current implementation
},
} ); |
Another option with single registration: registerBlock( 'myplugin/foo', {
versions: [
// v1
{
attributes: {
// v1 attributes ...
},
},
// v2
{
attributes: {
// v2 attributes ...
},
migrate( attributes ) {
// given v1 attributes, return v2 attributes
}
},
// v3 (latest)
{
attributes: {
// v3 attributes ...
},
migrate( attributes ) {
// given v2 attributes, return v3 attributes
},
},
],
edit() {
// current implementation
},
save() {
// current implementation
},
} ); |
I find the multiple registration version easier to read. It also allows the migrations to be moved into a 'migrations' module and keep the main file uncluttered. |
Also worth considering them as distinct blocks so the slug is myplugin/v1/foo and keep the version attribute along with that. Otherwise it will be hard to distinguish between slugs. |
I'm for the second option, the "old blocks" are not really blocks (any more) and shouldn't be registered as blocks. |
Good point. We can put the versions etc. into separate files as well, to address Nicola's point. I'm on board for option two. We still will want to make sure we add versioning into the attributes. That way we know what block is what etc. |
A couple considerations are:
Given the above, this is certainly appealing as a simple case. The block implementer, when changing a block, simply creates a block with a different unique slug (maybe just including the version number). We'd then want a way for them to flag their previous versions as disabled (maybe just a |
How about having |
(i.) This exposes three types of version bumps, then:
Type 3 is obviously harder to confidently deal with and could be assimilated into type 2. However, should the burden of decision be placed on the user for a bump of type 1? It may actually turn out to be confusing or frustrating, as a user, to be presented with the choice to upgrade and then see no resulting change. (ii.) This brings me, more generally, to the aspect of communication. Could a lower-tech and lower-commitment solution be to provide a mechanism for block authors to explain an upgrade to their users? For instance:
(iii.) If a user chooses not to upgrade — whatever the mechanism chosen, from the most sophisticated of those proposed here to the least — how and when can they change their mind? |
It may be simpler to start with the assumption that versioning should always be backwards-compatible, at least in the sense that there should be no content loss or regression in visual appearance, even if the underlying attributes have been remapped. If we did want to support breaking changes, maybe these ought to be implemented as separate blocks, where transforms are defined to allow a user to explicitly convert the block to the new canonical block type. I assume Undo behavior and revisions should be sufficient here to allow the user to revert back to the original copy should they regret their decision to transform. |
Another thing on my mind: whatever the solution we come up with, it would be nice that the same foundation could be used for a graceful downgrade mechanism for Gutenberg. I had an IRL chat about this — I'm not sure anyone has tracked it — and the gist is:
|
We have a case here that would need to support sourcing from I'd be interested in an approach that just handles variations of markup. Maybe: attributes: {
citation: {
type: 'array',
source: children( 'footer' ) || children( 'cite'),
},
}, Or... attributes: {
citation: {
type: 'array',
source: children( 'footer, cite' ),
},
}, or... attributes: {
citation: {
type: 'array',
source: children( 'cite' ),
legacy: children( 'footer' ),
},
}, |
While the second of these works as-is, we would need a mechanism that prevents blocks with the legacy markup from being flagged as invalid, since the behavior for block invalidation is to compare a reserialization of that same block against the saved markup, which is expected to now be different in the new version of the block. If the parser knows that the attribute value it receives is from a legacy format, we could potentially skip this validation step, though this has a few issues:
The first of these challenges is still an issue for the other proposals here where we merely define attributes of the different versions. The only alternative I could imagine to satisfy this need is to always keep the full serialization ( If we did decide that this is important, I feel that with a need to define most properties of a block anyways, we may be better off just establishing a convention of creating separate block registrations for new block versions (e.g. |
Is there any part of this which could be delegated to the upgrade process (core/theme/plugin) that generates the block? or CLI ? I can see authors being overwhelmed by prompts if every existing post prompts them to upgrade / convert a few blocks.
What would the expected impact on themes / headless systems be when a block changes it's type? Some other issues mention wp_query attributes for block type and it is all over #2649 |
It's not clear what you have in mind by impact; as I see it, there would be no fundamental difference between how a block is initially represented in post content before and after the change in type, except in the latter case the specific markup may be altered by the modified To your point, it's more about deciding on block APIs that allow for attributes to be transferred from one type/version to another, and the UX by which this is communicated to the user (automated, prompted, explicit action). |
I have a lot of concerns here, some from the user's perspective and some from the developer's perspective. We are dealing with a couple of related but different scenarios here: (a) a developer pushes a change to a block, and (b) a user deactivates a plugin that builds a block.
|
Closing as we have an initial implementation. |
The behavior of block validation works by comparing the output of what the editor would save as a block against what is actually saved in post content. Therefore, if the generated output of a block ever changes, all previously saved blocks would then be considered invalid.
The options here are:
The text was updated successfully, but these errors were encountered: