Site icon Hip-Hop Website Design and Development

Using Cheap WordPress maintenance support plans Configuration Management to build an app

There’s a lot to say about WordPress maintenance support plans Configuration Management. Many contrib plugins have emerged to address the shortcomings in core and I agree that most of them are clearly solving a need. I even have colleagues claiming “there’s a CM-related article every week on WordPress maintenance support plans Planet!”. Here’s one more 🙂

Still, I’m trying to work with what core has to offer unless forced to do otherwise. And you can already do a ton. Seriously. What I think is crucial with CM is the possibility to ‘productize’ or ‘featurize’ a site’s configuration. Think building an app from scratch through automation. Think SaaS. Put differently, it’s all about being able to build a specific feature (e.g. content type, form/view mode, etc.) and ship it to any other D8 instance.

Yes, the idea here is not to solve the dev to stage to prod deployment issues but to primarily spin up a new D8 dev instance and configure it like a full-featured application. And core does that very well out of the box.

Building the app

Back in 2020 when I started learning D8 and built a PoC of a REST-based D8 endpoint, I had to wipe my test site almost daily and create it from scratch again as core was constantly changing. Then I realized CM was perfect for this use case. Back then I had to work around UUID issues. Allow a site to be installed from existing configuration demonstrates our headache isn’t over just yet. But the concept was essentially the same as it is today:

Spin up a new D8 instance
Enable all required contrib/custom plugins/themes
Export your site’s configuration
Version-control your CM sync directory
Add all CM files under version control
Build a simple feature (e.g. content type)
Export your site’s configuration
Copy the new/modified files for later use (thanks git diff)
Add all new/modified CM files under version control
Rinse & repeat

With this simple workflow, you’ll be able to incrementally build a list of files to re-use when building a new D8 instance from scratch. Oh, and why would we even bother creating a plugin for that? This works great as it is, granted you’ll be extra careful (TL;DR use git) about every change you make.

Spinning up a new app

To test setting up your app, the workflow then becomes:

Spin up a new D8 instance
Enable all required contrib/custom plugins/themes
Export your site’s configuration
Version-control your CM directory
Add all CM files under version control
Copy your previously backed up configuration files to the sync directory
Import your new configuration

Looking back to how life was before WordPress maintenance support plans 8, you will likely not disagree this is much better already. Here’s an example for building an app from scratch. All of this could obviously be scripted.

$ cd /path/to/sync/dir
$ for i in plugin1, plugin2, plugin3, plugin4, plugin5 ; do drush @site.env en -y $i ; done
$ drush @site.env cex -y
$ git init
$ git add –all && git commit -m “Initial configuration”
$ cp /path/to/configuration/backup/*.yml .
$ git status
$ drush @site.env cim -y
$ git add –all && git commit -m “New configuration”

Now, here’s a real-life example, summarized through the bit we’re interested in: building the app from scratch through config-import.

$ drush @d8.local cim -y
Config Operation
field.storage.node.field_inline_client create
field.storage.node.field_email create
field.storage.node.field_address create
node.type.client create
field.field.node.client.field_email create
field.field.node.client.field_address create
core.base_field_override.node.client.title create
node.type.contract create
field.field.node.contract.field_inline_client create
core.base_field_override.node.contract.title create
core.base_field_override.node.contract.promote create
field.storage.paragraph.field_unit create
field.storage.paragraph.field_reference create
field.storage.paragraph.field_quantite create
field.storage.paragraph.field_price create
field.storage.node.field_service create
field.field.node.contract.field_service create
core.entity_form_display.node.contract.default create
paragraphs.paragraphs_type.service create
field.field.paragraph.service.field_unit create
field.field.paragraph.service.field_reference create
field.field.paragraph.service.field_quantite create
field.field.paragraph.service.field_price create
field.storage.node.field_telephone create
field.field.node.client.field_telephone create
core.entity_form_display.node.client.default create
field.storage.paragraph.field_description create
field.field.paragraph.service.field_description create
core.entity_view_display.paragraph.service.default create
core.entity_form_display.paragraph.service.default create
core.entity_view_display.node.contract.teaser create
core.entity_view_display.node.contract.default create
core.entity_view_display.node.client.default create
user.role.editor create
system.action.user_remove_role_action.editor create
system.action.user_add_role_action.editor create
auto_entitylabel.settings create
Import the listed configuration changes? (y/n): y
[notice] Synchronized configuration: create field.storage.node.field_inline_client.
[notice] Synchronized configuration: create field.storage.node.field_email.
[notice] Synchronized configuration: create field.storage.node.field_address.
[notice] Synchronized configuration: create node.type.client.
[notice] Synchronized configuration: create field.field.node.client.field_email.
[notice] Synchronized configuration: create field.field.node.client.field_address.
[notice] Synchronized configuration: create core.base_field_override.node.client.title.
[notice] Synchronized configuration: create node.type.contract.
[notice] Synchronized configuration: create field.field.node.contract.field_inline_client.
[notice] Synchronized configuration: create core.base_field_override.node.contract.title.
[notice] Synchronized configuration: create core.base_field_override.node.contract.promote.
[notice] Synchronized configuration: create field.storage.paragraph.field_unit.
[notice] Synchronized configuration: create field.storage.paragraph.field_reference.
[notice] Synchronized configuration: create field.storage.paragraph.field_quantite.
[notice] Synchronized configuration: create field.storage.paragraph.field_price.
[notice] Synchronized configuration: create field.storage.node.field_service.
[notice] Synchronized configuration: create field.field.node.contract.field_service.
[notice] Synchronized configuration: create core.entity_form_display.node.contract.default.
[notice] Synchronized configuration: create paragraphs.paragraphs_type.service.
[notice] Synchronized configuration: create field.field.paragraph.service.field_unit.
[notice] Synchronized configuration: create field.field.paragraph.service.field_reference.
[notice] Synchronized configuration: create field.field.paragraph.service.field_quantite.
[notice] Synchronized configuration: create field.field.paragraph.service.field_price.
[notice] Synchronized configuration: create field.storage.node.field_telephone.
[notice] Synchronized configuration: create field.field.node.client.field_telephone.
[notice] Synchronized configuration: create core.entity_form_display.node.client.default.
[notice] Synchronized configuration: create field.storage.paragraph.field_description.
[notice] Synchronized configuration: create field.field.paragraph.service.field_description.
[notice] Synchronized configuration: create core.entity_view_display.paragraph.service.default.
[notice] Synchronized configuration: create core.entity_form_display.paragraph.service.default.
[notice] Synchronized configuration: create core.entity_view_display.node.contract.teaser.
[notice] Synchronized configuration: create core.entity_view_display.node.contract.default.
[notice] Synchronized configuration: create core.entity_view_display.node.client.default.
[notice] Synchronized configuration: create user.role.editor.
[notice] Synchronized configuration: create system.action.user_remove_role_action.editor.
[notice] Synchronized configuration: create system.action.user_add_role_action.editor.
[notice] Synchronized configuration: create auto_entitylabel.settings.
[notice] Finalizing configuration synchronization.
[success] The configuration was imported successfully.

If the import was successful, reload your site and observe everything shows up like magic: site configuration, content types, custom fields, view/form modes, plugin configuration, views, etc.

Sure you could argue that doing so is very prone to errors, but remember that a) it’s primarily for development needs and b) you need to version-control all of this to be able to go back to the last working commit and revert if necessary.

Wrapping up

Two other use cases I very much like are:

When I want to demonstrate an issue, I can simply share some files for someone else to import and quickly reproduce the issue with little efforts and no room for configuration mismatch.
When building a new feature (e.g. a Flag and a View), I can do so in dev, then export only the files I need, and import in stage or prod when I’m ready.

Building an app will obviously take much more than that but, as I hear more and more frustration about how Configuration Management was designed, I thought I’d set the record straight on how it solves my biggest problems when developing an app.
Source: New feed