Problem/Motivation

Drupal 8.8.0 introduced native support for Composer directly in Drupal core. With the introduction of Composer also came the new top-level composer directory, which contains components that are not part of the drupal/core subtree split.

The following Composer-related components exist:

  • Generator: Modifies the metapackages when composer.lock is updated.
  • Metapackage: Contains exported dependency version constraints, e.g. drupal/core-recommended.
  • Plugin: Contains Composer plugins, e.g. drupal/core-composer-scaffold.
  • Template: Contains template projects used to create new Drupal sites, e.g. drupal/legacy-project.

These new components should each be considered for mention in the Drupal 8 backwards compatibility and internal API policy (backend) documentation page, so that this policy can better inform backporting decisions as changes are made to these new components.

Generator

The generators cannot be used or extended from a Drupal site. Their only function to to run during composer update operations to keep the metapackages in sync with Drupal's composer.lock file. Everything in the Generator directory should therefore be considered @internal.

Metapackage

The Metapackages only contain composer.json files. Each metapackage creates a subtree split with tagged versions that match the versions of drupal/core. No backwards-compatible-breaking change should be introduced in a minor update of a metapackage. Since the metapackages are generated from the composer.lock file, maintaining compatibility in the composer.lock file will also maintain compatibility in the metapackages. Therefore, no specific b/c policy documentation change is needed for the metapackages.

Plugins

The core Drupal Composer plugin classes are not intended to be called or used directly. All of the Composer plugins should therefore be treated as @internal. API changes that break compatibility with older versions of the plugin should therefore be permissible, provided that the behavior and data structures of the plugin remain compatible.

Note, however, that special care must be taken for plugins that run update hooks. It is possible that Composer may load some parts of a plugin before the update operation. If the plugin itself is updated, then the post-update hook and post-install hook will be called using the new version of the plugin. This can cause failures if classes loaded earlier (from the earlier version of the plugin) are incompatible.

For a fix for one example of this, see https://github.com/drupal-composer/drupal-scaffold/pull/82

Templates

The template projects are used to create new Drupal sites, and are also used to generate the downloadable tarballs on drupal.org. Everything in the templates directory should therefore be subject to the backwards compatibility policy.

Proposed resolution

The new headings should appear just before the existing section Minor releases vs. patch releases.

Proposed text for Composer plugins:

#Composer Plugins
Composer plugins in the Drupal repository are not included in Drupal download archives, but are instead packages separately. Subclassing code from Composer plugins is not supported. All classes and methods in Composer plugins are considered internal, and should explicitly marked as such for the avoidance of doubt. In the context of a Composer plugin, @internal means that the class or method is internal to that plugin only, and should not be used from other Composer plugins or from any other part Drupal core outside of that plugin. Commands provided by Composer plugins must follow the rules for command line tools and scripts.

Proposed text for Commandline tools:

The implementation for all command line tools and scripts packaged in Drupal are considered to be @internal. However, the parameters and options accepted by these tools are themselves considered to be a public API. Changes to parameters and options must follow the rules for backwards compatibility.

Proposed text for Configuration elements:

Configuration elements that Drupal sites and modules must provide in order to work correctly are also considered to be part of the API, and must remain compatible from one release of Drupal to another. Configuration elements that are to be removed or changed must also follow the rules for backwards compatibility.

#Composer Scaffold Configuration
Drupal sites that follow the Recommended project or Legacy project templates for Composer-managed sites may contain scaffold configuration elements in their top-level composer.json file. This configuration include specifications for file mappings, the location of the Drupal web root, and other settings. See the documentation on Using Drupal's Composer Scaffold for details on the available settings.
#Project Message Configuration
The Project Message plugin allows project templates to include follow-on instructions for sites created from the template. See the Project Message README for details.
#Vendor Hardening Configuration
The Vendor Hardening plugin allows Composer-managed sites to provide lists of directories to remove. See the Vendor Hardening README for details.

No part of any of the configuration enumerated above will be changed or removed in a breaking way.

Remaining tasks

Refine the proposed policy as needed and reach agreement on the policy. Update the Drupal 8 backwards compatibility and internal API policy (backend) documentation page with the approved text.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

greg.1.anderson created an issue. See original summary.

Mile23’s picture

Plugins:

In the context of a Composer plugin, one can't require the plugin package and then subclass or re-use code without inheriting the events of the original package. This means that, for instance, all the events registered in the plugin will be called in addition to any changes in the subclassing one.

Basically if you want to re-use code in a Composer plugin, you have to fork it or refactor so that all the behavior is in another package, external to the plugin definition.

Since this is the case, plugins should be assumed to be @internal.

There's the edge case mentioned above, however, in that a Composer script could directly refer to a method in the plugin, much like drupal/drupal used to say this:

"post-package-update": "Drupal\\Core\\Composer\\Composer::vendorTestCodeCleanup",

So we should explicitly mark Composer plugin classes/methods as @internal in order to be able to mark a given method as "@notInternal" by not marking it @internal. That way we can avoid breaking people's scripts.

We also don't have a general API/BC policy on CLI-based commands, so let's begin to define that here, too. We should be careful about them in Composer plugins which give you something to type into the command line. Basically: No take-backsies. We should be able to add options and arguments to existing commands, but not remove them. Also implementations of commands are generally @internal while command names, aliases, arguments, argument order, and options are not.

greg.1.anderson’s picture

The other thing we need to be careful of is that if the Plugin class instantiates objects (or, more specifically, loads classes) in the `activate` method, and those classes are updated as part of a `composer update` operation, then the old version of the code loaded in `activate` will continue to be used in the post-update hooks, whereas in contrast, classes loaded after the update will be loaded from the new code.

We should investigate avoiding this problem by not loading any classes (other than the Plugin class) in the activate method. Once we have proven this technique, we should document it as part of the bc policy / strategy for Composer plugins.

Mile23’s picture

Re #3:

It seems like the best practice would be to:

  • Always use a Handler pattern (the Plugin object creates a Handler object to do the work).
  • Always put off instantiating the Handler object until the very last moment, in the event handler method of the Plugin object.
greg.1.anderson’s picture

Yeah, we already use a handler pattern; it was exactly my thought that we should lazy-instantiate the handler.

greg.1.anderson’s picture

Lazy-instantiation of the handler done for scaffold plugin in #3104922: Guard against changes to Scaffold Plugin that might cause upgrade problems and committed to 8.9.x and 9.0.x. Still needs backport to 8.8.x.

greg.1.anderson’s picture

Issue summary: View changes

I updated the issue summary to include proposed text for the new policies.

Perhaps we should go one step farther and propose that command line tools and scripts may include new features in patch releases. Do we know if the "no new features in patch releases" policy has been rigorously enforced for scripts in the past? If this policy change is at all controversial, we could discuss it in a follow-on issue (or simply drop the question if deprecated).

greg.1.anderson’s picture

Title: Decide on backwards compatibility policy for Composer plugins in Drupal 8 » [policy, no patch] Decide on backwards compatibility policy for Composer plugins in Drupal 8
Status: Active » Needs review

I guess this should be "needs review" now.

Mile23’s picture

General +1 on proposed resolution, but:

The implementation for all command line tools and scripts packaged in Drupal are considered to be @internal.

I'm re-thinking this a little bit despite what I said in #2, because we do have #2242947: Integrate Symfony Console component to natively support command line operations and others... One of the things we're trying to do there is allow, for instance, drush to be able to use core commands, and also have discoverable commands for contrib. So there will be an API.

But we don't have an API yet, so defining everything as @internal gives us flexibility now. When we do eventually start defining an API for CLI stuff, we can amend the rules and mark all the stuff @internal that we previously assumed was such.

greg.1.anderson’s picture

Just to be explicit, I agree with #9. Let's keep the proposal as presently written, and adjust the policy for a command line tool framework if/when such a thing exists.

greg.1.anderson’s picture

Issue summary: View changes
FileSize
12.47 KB

Updated the issue summary to include information about backwards compatibility policies for configuration elements.

Add a patch marking all Composer Plugin classes @internal.

greg.1.anderson’s picture

Title: [policy, no patch] Decide on backwards compatibility policy for Composer plugins in Drupal 8 » [policy + patch] Decide on backwards compatibility policy for Composer plugins in Drupal 8

There is a patch now, so adjust title to reflect that.

greg.1.anderson’s picture

Slack conversation from yesterday:

@alexpott
My gut feeling is that the config should be the “API” layer. And as long as existing config works as expected then we’re good.
I don't think these plugins are extension points

@larowlan
that seems reasonable

@webchick
It's hard for me to parse out "impact" from that issue summary...
"BC" in the context of a Drupal release means that if you wrote code that was compatible with Drupal 9 for the 9.0.0 release, it can sit there stale and untouched until 9.40.2 and you're fine.
(we have historically had mixed results with that promise, but that's the idea behind it :P)
So if these are internal changes only that only impact how Drupal itself does Composer-y things under the hood, and no contrib/custom code would be affected, :+1:
If we suddenly introduce new requirements, like all modules now need a composer.json in their root, or all sites must now use a completely different directory structure, we'd need to jack the major release number for that, IMO.

@greg.1.anderson
The b/c policy for Composer plugins are fairly removed from any requirements for modules. The scaffold plugin introduces config that install profiles might use to place files, but that's config, not APIs in the usual sense.
It seems there is general agreement about this (the "API" definition for Composer plugins). How do we get the policy issue "merged" (moved to "fixed" with documentation updated as suggested on the issue)?

@alexpott
I think we should clearly state that a composer.json file that uses these plugins in 8.8.0 is expected to work in Drupal 9.0.0
And if we introduce any changes to configuration then we will do in BC compatible manor ie. by deprecating and providing a path to move to an undeprecated config

@greg.1.anderson
@alexpott Do you think that policy should be stated on https://www.drupal.org/core/d8-bc-policy, or somewhere else?

@alexpott
@greg.1.anderson I think this issue should propose an edit to that page. And then it is up to the committers - espically the release managers to approve the changes to that page.

@greg.1.anderson
Yes, it currently does suggest an edit to the page. I can enhance it to also talk about composer.json configuration as suggested above, although the page does not currently discuss things like that, so I'd probably have to propose a new section heading.

@catch
So +1 to making sure 8.8 composer.json is supported.
i think I'm +1 to making the actual plugin code @internal bug why not explicitly mark it all @internal when we add it?

@alexpott
We should definitely get in the habit of adding new things like this as @internal

@greg.1.anderson
Should I make a new issue that adds @internal to all of the Composer plugin classes? Is it okay to just put @internal at the top of the class file, or does every public method need to be @internal? Protected methods too?

@alexpott
I think if the class is @internal then all the methods are
Also I think the patch could be in this issue because if we sign off on this approach then all the classes are @internal

@greg.1.anderson
OK sounds good, I will just add the code patch to the documentation issue then.

@Mile23
+1 on this conversation.

@catch
Also final?

greg.1.anderson’s picture

Issue summary: View changes
FileSize
8.74 KB
1.13 MB

Here is an updated patch with 'final' attached to each internal class.

Exceptions:

  • I skipped FileSecurity.php in VendorHardening, because that is a duplicate of a file in core, and I did not want to change the core location in this patch.
  • I skipped Config and VendorHardeningPlugin in VendorHardening, and Message in ProjectMessage because marking any of those classes final produces warnings in tests that mock them.

I figure that mocking warnings trump 'final', because classes that are already marked '@internal' should not be used or extended. Today making those classes final only produces a warning, but that warning could become an error in future versions of PHP, introducing technical debt.

alexpott’s picture

Title: [policy + patch] Decide on backwards compatibility policy for Composer plugins in Drupal 8 » [policy, no patch] Decide on backwards compatibility policy for Composer plugins in Drupal 8
Issue summary: View changes

+1 to marking all the code in our composer plugins as @internal. I like the proposed policy update in that it makes clear that the API we make BC promises is the configuration in composer.json. The important thing is that a Drupal 8.8.0 composer.json using our new plugins and meta packages continues to work in Drupal 9 and if we do change something we trigger deprecation errors and provide an upgrade path which is what we're promising to do.

alexpott’s picture

I reverted the node back to #14 - sorry about that - it was an xpos - had the tab open too long.

Mile23’s picture

Marking a Composer plugin's classes as final is a bit of overkill, and really only makes it that much harder to fork for your own needs.

greg.1.anderson’s picture

Patch does not apply; I'll reroll later.

I feel like final is belts and suspenders. Not terribly opposed to it, but as mentioned in #17, we might need to remove some final labels as time goes on. Most of the classes here are not intended to be extended anyway, so I don't think that concern is to severe. Happy to revert to simply "@internal" though.

greg.1.anderson’s picture

FileSize
14.38 KB

Here's a re-roll of #14, which was actually a bad patch. For the interdiff, see #14, as the interdiff was fine.

This one still has the final markers, except where stipulated in #14.

greg.1.anderson’s picture

Here is an updated version of the patch in #19, with the final keyword removed. There is no guidance on the use of final in the Drupal Object-Oriented code guidelines; it therefore seems out of scope to stipulate the way that final should be used in plugins vis-a-vis the backwards compatibility policy.

Let's keep the question of whether and how the Drupal coding standards should advise on the use of final a separate issue. If anyone feels strongly about that topic, they may create a follow-on issue against the coding standards.

greg.1.anderson’s picture

Here are the patch files that should have been in #20.

greg.1.anderson’s picture

#20 / #21 had garbage in it. Also, it just reverts back to #11. So, please ignore all patches after #11, and just review that one.

Mile23’s picture

+1 on just @internal as per #11. Maybe re-up the patch so it's the last one in the queue.

greg.1.anderson’s picture

Re-uploading #11 as #24 for the avoidance of doubt.

Mixologic’s picture

Status: Needs review » Needs work

So from what I understand, @internal should mean "Nobody outside of us should use this" but for composer plugins its slightly more different than that.

I.e. Core itself should not attempt to instantiate classes provided by composer plugins, and vice versa. The 'Api boundary' for internal in this case means "Internal to the plugin itself", not just "internal to core"

I agree with the rest of the ideas/ideals.

greg.1.anderson’s picture

Issue summary: View changes
Status: Needs work » Needs review

I updated the Issue Summary to cover #25:

In the context of a Composer plugin, @internal means that the class or method is internal to that plugin only, and should not be used from other Composer plugins or from any other part Drupal core outside of that plugin.

Mixologic’s picture

Status: Needs review » Reviewed & tested by the community

^Thats solid. And what I was thinking.

The rest of the policy is sensible, and the api boundaries seem clear to me. CLI interfaces, composer.json schemas etc.

Gábor Hojtsy’s picture

Assigning credits. I agree making these internal is better than not :)

  • Gábor Hojtsy committed 151825e on 9.0.x
    Issue #3103918 by greg.1.anderson, Mile23, alexpott, Mixologic, catch: [...

  • Gábor Hojtsy committed ab5ca5e on 8.9.x
    Issue #3103918 by greg.1.anderson, Mile23, alexpott, Mixologic, catch: [...

  • Gábor Hojtsy committed 86fd930 on 8.8.x
    Issue #3103918 by greg.1.anderson, Mile23, alexpott, Mixologic, catch: [...
Gábor Hojtsy’s picture

Issue summary: View changes
Status: Reviewed & tested by the community » Needs work
Issue tags: +needs documentation updates

Committed, thanks all! Leaving open for finishing off the docs updates as scoped in the issue summary.

greg.1.anderson’s picture

Status: Needs work » Needs review
Issue tags: -needs documentation updates +Needs documentation updates

I updated the Drupal 8 backwards compatibility and internal API policy (backend) page per the issue summary.

Leaving in "needs review" so that a second party can confirm that the updates conformed to the agreement and there were no mistakes made in the transcription. (Please fix mistakes if any are found.) I don't think that we need to ask a core committer to do this verification.

mradcliffe’s picture

https://www.drupal.org/node/2562903/revisions/view/11328169/11757531

but are instead packages separately

Should be "but are instead packaged separately".

and should explicitly marked as such for the avoidance of doubt

Should be "and should be explicitly marked as such for the avoidance of doubt"

I'm not sure if "for the avoidance of doubt" is necessary in the full sentence. Maybe "All classes and methods in Composer plugins are internal, and should be explicitly annotated as such."?

greg.1.anderson’s picture

@mradcliffe Thanks for the improvements. Please make minor grammar corrections directly to the documentation page.

andypost’s picture

greg.1.anderson’s picture

Was #37 intended for a different issue? I don't see how #3116405 affects the backwards compatibility policy. If it does, could you please explain? Is the fix for Composer 1.10 a "breaking" change under this policy?

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.2.x-dev

Drupal 8 is end-of-life as of November 17, 2021. There will not be further changes made to Drupal 8. Bugfixes are now made to the 9.3.x and higher branches only. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.2.x-dev » 9.3.x-dev
quietone’s picture

I have updated the doc per #35.

That was the last thing to do here. Someone what to check?

Spokje’s picture

Status: Needs review » Reviewed & tested by the community

Looked at the changes made by @quietone here and they are exactly the same as requested in #35.

Therefore: RTBC. (Unsure if this could be status Fixed straight away, since there's nothing to commit any more, this is only about updating documentation)

Mile23’s picture

What remains here? Do we get sign-off from maintainers first and then add the docs, or do we add the docs and then mark it closed?

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.15 was released on June 1st, 2022 and is the final full bugfix release for the Drupal 9.3.x series. Drupal 9.3.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.4.x-dev branch from now on, and new development or disruptive changes should be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

catch’s picture

Title: [policy + patch] Decide on backwards compatibility policy for Composer plugins in Drupal 8 » [policy + docs] Decide on backwards compatibility policy for Composer plugins in Drupal 8
Status: Reviewed & tested by the community » Fixed

I think the docs update looks great, going to go ahead and close this.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.