Problem/Motivation

This has been discussed on #2684081: Modules need a way to keep their configuration up to date and other issues, can't find a dedicated issue for it, so opening at the risk of creating a duplicate.

Sometimes we want to update configuration, but we can't be 100% confident whether the site owner wants that configuration change or not. In these cases we tend towards not updating anything, and maybe putting a notes on the status report if the preferred configuration is not set.

For linux distribution updates, it's a fairly common pattern to flag modified files, and give the user the option to keep their own changes, accept the maintainer's version, or manually review a diff. I think we could do a similar thing for a subset of core updates.

This might need to be a new update type, alongside hook_update_N() and hook_post_update_NAME().

#3632720: Add site data-sharing choices to Update Status: share the site domain (opt-out) and site email address (opt-in) with Drupal.org is an example of an issue that could use this, e.g. update.php, or drush, could present a form saying 'Would you like your domain name to be sent to Drupal.org alongside module install data' with a checkbox or y/n prompt. Then different code paths for yes/no/skip.

But I think once we had the capability, there would be quite a few cases - e.g. whether to update a view to add a translation filter or not would be one example.

Steps to reproduce

Proposed resolution

Generated with the help of an LLM.

Add a third update type, hook_interactive_update_NAME(), in EXTENSION.interactive_update.php. Discovery and run-tracking reuse \Drupal\Core\Update\UpdateRegistry, which is already parameterized by update type (update.post_update_registry is the post_update instance), with its own key/value collection.

Each function asks one yes/no question, declared with a function attribute in the style of #[MarkFutureUpdateEquivalent], for example:

#[InteractiveUpdate(
  question: new TranslatableMarkup('Share this site\'s domain name with Drupal.org?'),
  description: new TranslatableMarkup('Sent with the update status check. You can change this later on the Update Status settings page.'),
)]
function update_interactive_update_share_domain(bool $answer): ?TranslatableMarkup {
  \Drupal::configFactory()->getEditable('update.settings')->set('data_sharing.domain', $answer)->save();
  return NULL;
}

Answers are yes, no, or decide later. Yes and no run the function with TRUE or FALSE and record it as run. Decide later records nothing, so the update stays pending and is asked again next time.

An optional applies callback on the attribute lets an update mark itself as run without asking when the decision has already been made, for example because the config was set locally and deployed. Without it, a site that manages config would be asked again on every environment.

Collecting answers reuses the recipe input pattern (\Drupal\Core\Recipe\InputCollectorInterface):

  • update.php: the selection page lists pending interactive updates with yes / no / decide later radios, defaulting to decide later. They run after post updates, in the same batch.
  • Drush: prompts per question, as ConsoleInputCollector does for recipes, plus a way to pass answers on the command line.
  • Non-interactive runs (drush updb -y, automated updates through Package Manager, tests) default to decide later, never yes.

Pending interactive updates get a status report entry linking to update.php, so a deferred question isn't lost.

Interactive updates should only make changes the site owner can reverse through the normal UI, and say where in the description.

Remaining tasks

  • Agree the shape: a new update type, or an attribute on post updates.
  • Questions beyond yes/no (choose one of several, or a diff of shipped versus customised config, as in #2684081): leave for a follow-up, or support typed data definitions now as recipe inputs do?
  • Removed interactive updates: the hook_removed_post_updates() equivalent.
  • Drush support, which lives in Drush.
  • First consumer: #3632720.

API changes

New hook_interactive_update_NAME(), a new #[InteractiveUpdate] attribute, and a new update.interactive_update_registry service.

User interface changes

The update.php selection page gains a question list. The status report gains an entry while interactive updates are pending.

Introduced terminology

Data model changes

Release notes snippet

Comments

catch created an issue. See original summary.

hestenet’s picture

@catch - I'm a blind man in the dark on the architecture here, but I've used some llm help to try and figure out a possible implementation path that follows according to your suggestions and existing precedent in core.

This may be a Cunningham's Law situation (posting the wrong answer to prompt the right one) - so please suggest any feedback on the idea.

When this is more firm, I'll go back to update: #3632720: Add site data-sharing choices to Update Status: share the site domain (opt-out) and site email address (opt-in) with Drupal.org accordingly