Support generating recipes

Created on 13 August 2022, almost 3 years ago
Updated 1 June 2023, about 2 years ago

Problem/Motivation

Currently we package configuration into modules. Per the Distributions and Recipes initiative β†’ , a new type of configuration provision is planned for Drupal core: recipes.

Proposed resolution

At the bundle level, allow selection of what type to create: modules or recipes.

Remaining tasks

User interface changes

API changes

Data model changes

✨ Feature request
Status

Active

Version

5.0

Component

Code

Created by

πŸ‡¨πŸ‡¦Canada nedjo

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • πŸ‡ΊπŸ‡ΈUnited States dave reid Nebraska USA
  • πŸ‡¨πŸ‡¦Canada mandclu

    I'd be interested in helping with this, but I'm not very familiar with how Features works under the hood. Could a maintainer point me in the right direction on where to start?

  • πŸ‡«πŸ‡·France andypost

    +1 to provide submodule to export as recipes

  • πŸ‡¨πŸ‡¦Canada mandclu

    Poking around in the code a little, it seems like the logical place to start would be in the addPackageFiles() method of the FeaturesManager class. I can see some potential approaches to making these changes:

    1. Overwrite the current method and assume that recipes are the way forward (probably only acceptable if this would be the basis of a new major version)
    2. Put code into a submodule that would decorate the current FeaturesManager class
    3. Add some kind of global configuration option to export recipes instead of standard modules
    4. Add a per-feature option to export a recipe instead of a standard module

    Do the maintainers (or anyone else) have a preference on the best path forward?

  • πŸ‡«πŸ‡·France andypost

    The module can just decorate FeaturesManager

  • πŸ‡ΊπŸ‡ΈUnited States thejimbirch Cape Cod, Massachusetts
  • πŸ‡¨πŸ‡¦Canada nedjo

    @mandclu If you happen to still be interested and available to start on this, I have some availability in the next few weeks to provide review and comments. While I'm not a current maintainer, I am a past one and have a good familiarity with the code.

    I'll start by commenting on some of what may be needed.

    At a high level, we have the advantage that we already model features fairly generically as a set of "packages", see the Package object and its properties. Currently, to generate feature modules, we parse these packages into modules. Our task here is to enable a second target format: recipes.

    Packages are associated with a Features bundle, see the relevant documentation β†’ and the associated FeaturesBundle config entity type.

    Each package can have a set of configuration items assigned to Package::config.

    In the current implementation, as you've noted, we parse the set of feature packages into extensions in FeaturesManager:: addPackageFiles(). We model most of the packages as modules, but optionally we special-case one package to be parsed as an install profile.

    Regarding your question about approaches:

    1. Overwrite the current method and assume that recipes are the way forward (probably only acceptable if this would be the basis of a new major version)
    2. Put code into a submodule that would decorate the current FeaturesManager class
    3. Add some kind of global configuration option to export recipes instead of standard modules
    4. Add a per-feature option to export a recipe instead of a standard module

    My personal take is: #3 is the way to go. Rationale:

    • Recipes appear to be here to stay. If feasible, our best approach is to introduce recipe support directly in Features rather than through a submodule.
    • The changes would be significant enough as to require a new minor release, but should be feasible within the existing major version. At first glance, it look like a lot of the work - possibly most - can be done within existing classes without deprecations. :: addPackageFiles(), for example, is a protected method that we're free to modify as needed.

    So, yes, concretely, we'd start by adding a new property to the FeaturesBundle object--perhaps ::targetRenderFormat or similar.. We'd add accompanying getter and setter methods to FeaturesBundleInterface. as is done in core per the relevant documentation β†’ . Values would be module and recipe. Perhaps we declare these in an Enum in FeaturesBundleInterface. We'd need:

    • At the UI level, a new radio element on the bundle configuration form to manage the new setting.
    • An update to set the value to modulein existing bundle configuration.

    From there, we can get a general sense of what's needed by looking look at our two different target formats - modules and recipes - and highlighting (a) how they differ and (b) how the parsing to modules is currently handled in Features (so we know where changes are needed). I'll give that some thought and follow up when I can with some further notes.

  • πŸ‡¨πŸ‡¦Canada nedjo

    Here are those further notes--certainly not an exhaustive list of what's needed, but a start ;)

    Scope

    We'll want to limit scope in an initial spec. That might basically mean: we replicate for recipes what's already supported for modules but leave for later any recipe-specific functionality. For example, recipes can provide config actions, while modules cannot, so for purposes of this issue feature-style recipes can't provide config actions.

    Parsing config into packages

    We parse site config and assign it to a set of packages. Most of that parsing and assigning are not specific to the module use case, but a few pieces need to be updated to support recipes.

    The packages assignment plugin, current description "Detect and add existing package modules", needs to be updated to conditionally handle recipes.

    Methods invoked there that need to be updated and/or renamed and/or deprecated and replaced to handle recipes as well as modules include:

    • FeaturesManagerInterface::getFeaturesModules()
    • FeaturesManager::initPackageFromExtension()

    (Is there a term in use that we can adopt to cover both modules and recipes?)

    The optional assignment plugin needs to be disabled for recipes.

    Render packages as recipes rather than modules

    Currently we render packages as modules. We need to optionally render instead as recipes.

    For our purposes, a key advantage of the current implementation is that the renderer plugins - responsible for generating features - basically just render a bunch of files based on data previously assigned to a nested array. So, to queue up generation of recipes instead of modules, we just need to change what's assigned to that array. The key method involved here is FeaturesManager::addPackageFiles(), which in turn calls FeaturesManager::addInfoFile() and Package::appendFile().

    Of course, the method name ::addInfoFile() is no longer exactly right either, since recipes are defined through a recipe.yml file rather than a modulename.info one. Maybe we rename this protected method to ::addDefinitionFile()or similar? In any case, it will need to conditionally generate a YML file data structure that's in the expected recipe format rather than the module .info format. So, for example, while the .info file has an array of dependencies, recipes need to separate those out into a set of required recipes and an additional install array. We also need to set the recipe config.import array to ensure expected module and theme config is installed.

    FeaturesManager::mergeInfoArray() will similarly need updating.

    Generation

    For the most part the generation plugins are agnostic as to render target, but a few changes will be needed. For example, FeaturesGenerationMethodBase::prepare() calls FeaturesManager::getExportInfo(), which currently has the hard-coded string 'modules' for the $path variable. This will need to be conditionally set to 'recipes'.

    Validation

    We don't validate the .info file we generate for modules. It would be a valuable addition, though, to validate our .recipe files. There's relevant code in the core recipe API, in which recipes are validated before being applied. We could add additional constraints, such as that the generated recipe:

    • doesn't have any config actions (not yet supported by Features)
    • doesn't provide any optional config (not supported by the core recipes API)
    • has config set to strict: false

    Tracking applied recipes

    Currently in the UI we distinguish between installed feature modules and those that have not been installed. The closest equivalent with recipes is whether they've been applied. Core doesn't track this info, but there are (at least!) two contrib modules that do: Project Browser β†’ and Recipe Tracker β†’ . We might introduce a soft dependency through a method that looks for each of these modules and uses the first one found or gracefully defaults.

  • πŸ‡ΊπŸ‡ΈUnited States thejimbirch Cape Cod, Massachusetts

    We have a core Sue for this also

    https://www.drupal.org/project/drupal/issues/3446561 ✨ Automatic recipe generation from config diff Active

    And some folks in contrib have created a sandbox module

    https://www.drupal.org/sandbox/jaro2801/3523096 β†’

Production build 0.71.5 2024