Overview

In doing some testing I noticed that code components are broken in the Haven site template, and at least one other "monolithic" template (Archimedes) even though they work as expected in Byte (which is not monolithic).

Turns out that the root of the issue is there is a new config for canvas.brand_kit.global.yml in Canvas that got added after we converted to a monolith. I think this is going to be an ongoing issue that is not really workable for site templates to keep updating their config when modules make changes.

Steps to reproduce:

  1. Install Drupal CMS with the Haven site template
  2. Create a code component
  3. See there is an error that results from the path /canvas/api/v0/config/brand_kit/global throwing a 404:
    An unexpected error has occurred.

    Error 404: false

Proposed resolution

User interface changes

Issue fork drupal_cms-3582753

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

pameeela created an issue. See original summary.

pameeela’s picture

pameeela’s picture

Issue summary: View changes
penyaskito’s picture

Status: Active » Postponed (maintainer needs more info)

Assets and Brandkits are config entities, so they need to be explicitly installed, and are required by Canvas.

Maybe Haven's recipe should define:

config:
    canvas: '*'

For installing the config entities.

Or as a minimal:

config:
    canvas: 
         - canvas.asset_library.global.yml
         - canvas.brand_kit.global.yml

for avoiding the code component editor problem.

But you would still miss

editor.editor.canvas_html_block.yml
editor.editor.canvas_html_inline.yml
filter.format.canvas_html_block.yml
filter.format.canvas_html_inline.yml
image.style.canvas_avatar.yml
image.style.canvas_parametrized_width.yml

which can cause other issues.

So I think it's unavoidable to define:

config:
    canvas: '*'
pameeela’s picture

Title: Unable to create code components with some site templates » Monolithic site templates can break when module's config requirements change upstream
Project: Drupal Canvas » Drupal CMS development repository
Version: 1.x-dev »
Component: … to be triaged » General
Issue summary: View changes

@penyaskito do you know when canvas.brand_kit.global.yml got added? I suspect it was after this was converted into a monolith so it's not included in the recipe's config (all of the other config you mentioned is included).

I have wondered about this specific issue where a module's config changes, the site template has no way of knowing this. I think the only way to prevent it is to do what you've suggested with:

config:
    import:
        canvas: '*'

But this is at odds with the monolithic approach. Going to repurpose the issue to be about that more broadly.

pameeela’s picture

Status: Postponed (maintainer needs more info) » Active
penyaskito’s picture

It got added in 1.3.0 (#3577631). We provide hook_install() and a post_upgrade.

The real problem seems to be that drupal_cms_site_template_base should include

config:
    import:
        canvas: '*'

As I assume all the monoliths started from that and used site:export, they all missed those config entities.

And if they were generated with a version < 1.3.0, they are skipping the install path.

I think that's a problem of the recipes system, not canvas itself. It's raised in canvas because of the peculiar case of requiring config entities to be installed, but can be triggered in many ways. e.g.

1. Module X 1.0.0 (or even core) provides config schema for block settings.
2. A recipe was built with X 1.0.0 and provides a block with that schema.
3. When installing the recipe, block with schema for X 1.0.0 is installed, but the code for X 1.2.0 is downloaded.
4. Any update_N hooks or post_updates WON'T run, so your block is never updated to the new schema.

The workaround is that recipes/site template devs need to be up-to-date. The problem is that this might be too much of a maintenance burden.

phenaproxima’s picture

I have fielded several requests to make it easier to condense monolithic site templates, so that they can bring in third-party bits without deviating from them. So I think now's the time to make it happen.

Rather than having site:export try to be "smart", I'd rather put another tool into creators' hands, which allows them to try merging a recipe into another recipe. Something like this:

drush recipe:merge ../recipes/my_site_template ../recipes/some_recipe

If the config shipped by some_recipe matches the subset that's in my_site_template, some_recipe gets merged into my_site_template as a dependency, and the duplicated config is removed.

If there are changes detected, the command exits, unless you pass a --force option, in which case the command will proceed anyhow.

We could make this even more convenient to use by adding a --merge option to site:export:

drush site:export --merge ../recipes/drupal_cms_content_type_base --merge ../recipes/drupal_cms_admin_ui
penyaskito’s picture

@phenaproxima That's great, but won't solve the main problem described here. The problem in the IS and described in #7 is not affecting monoliths, but any recipe.

phenaproxima’s picture

@penyaskito: My proposal in #8 is not meant to be a holistic solution to this problem (I should have clarified that; my bad).

What it is meant to do is make it easier to construct site templates which can stay up-to-date, by offloading the parts that you, as the site template author, don't really want to maintain.

  • phenaproxima committed 15f2f845 on 2.x
    chore: #3582753 Add canary tests that the config shipped by recipes is...

  • phenaproxima committed 0cccf0f4 on 2.1.x
    chore: #3582753 Add canary tests that the config shipped by recipes is...
wim leers’s picture

#7++ — great insight, @penyaskito!

It made me realize something.

Drupal 8+'s config management system is designed for config management of a given site. Config exports make sense for the site they were exported from, because they assume a certain DB schema/(post) update hooks. (Exported config declares dependencies, but only on extensions, not versions.) It's the responsibility of the site builder/developer to know what config can be imported on another environment (module versions — schemas really — must match). Failure to do so may lead to hard-to-debug errors, but at least is isolated and can be rolled back (when following config management best practices).

The Recipe system changes that, drastically. Recipes reuses the config management and "config export" functionality, but it is intended to be able to target any site. Meaning it is impossible for a site builder who applies a recipe to know which module versions were used while building the recipe: A) a different person built it, B) the site in which it was built is not publicly available, C) recipes do not specify such metadata, D) config entities do not declare which module version was installed at export time, D) they definitely do not declare which update paths had been applied.

So this is NOT about "an update path for the recipe" — it's about being able to apply update paths (that have already been applied to the site) to config being installed during recipe application (where that recipe's config entities were created for a prior version of the module that provides those config entities). That requires knowing which update paths have already been applied to each exported config entity.

IOW: I think config exported for recipes needs additional metadata (much like _core: { default_config_hash: … }) to be able to know which update paths have already been applied.

For example, a block.block.*.yml in a Recipe should contain:

_core:
  default_config_hash: fRKXNB91UxDvEMkzCR8ZBsawfC6Fqbme2gtobei3gu4
  applied_update_paths:
    - block_post_update_make_weight_integer

… which then means during Recipe application, the missing post-update path (block_post_update_set_menu_block_depth_to_null_if_zero) should still be applied, even though on the site that the Recipe is being applied to, block_post_update_set_menu_block_depth_to_null_if_zero already ran.

wim leers’s picture

I think the scenario I described in detail in #14 was missed in #3283827: Support multiple config update paths.