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
- Add tests to confirm that Composer plugins do not change in ways that will cause upgrade problems. See #3104922: Guard against changes to Scaffold Plugin that might cause upgrade problems and #3104985: Increase scope of Composer plugin update tests to cover other plugins
- Update Drupal 8 backwards compatibility and internal API policy (backend) with new policies:
- Add a new heading Composer plugins under the heading @internal
- Add a new section on Command line tools and scripts
- Add a new section on Configuration elements
- Add @internal markers to all Composer plugin classes explicitly marking them all internal.
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.
Comment | File | Size | Author |
---|---|---|---|
#24 | 3103918-24.patch | 12.47 KB | greg.1.anderson |
#11 | 3103918-11.patch | 12.47 KB | greg.1.anderson |
Comments
Comment #2
Mile23Plugins:
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:
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.
Comment #3
greg.1.anderson CreditAttribution: greg.1.anderson at Pantheon commentedThe 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.
Comment #4
Mile23Re #3:
It seems like the best practice would be to:
Comment #5
greg.1.anderson CreditAttribution: greg.1.anderson at Pantheon commentedYeah, we already use a handler pattern; it was exactly my thought that we should lazy-instantiate the handler.
Comment #6
greg.1.anderson CreditAttribution: greg.1.anderson at Pantheon commentedLazy-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.
Comment #7
greg.1.anderson CreditAttribution: greg.1.anderson at Pantheon commentedI 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).
Comment #8
greg.1.anderson CreditAttribution: greg.1.anderson at Pantheon commentedI guess this should be "needs review" now.
Comment #9
Mile23General +1 on proposed resolution, but:
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.
Comment #10
greg.1.anderson CreditAttribution: greg.1.anderson at Pantheon commentedJust 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.
Comment #11
greg.1.anderson CreditAttribution: greg.1.anderson at Pantheon commentedUpdated the issue summary to include information about backwards compatibility policies for configuration elements.
Add a patch marking all Composer Plugin classes
@internal
.Comment #12
greg.1.anderson CreditAttribution: greg.1.anderson at Pantheon commentedThere is a patch now, so adjust title to reflect that.
Comment #13
greg.1.anderson CreditAttribution: greg.1.anderson at Pantheon commentedSlack 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?
Comment #14
greg.1.anderson CreditAttribution: greg.1.anderson at Pantheon commentedHere is an updated patch with 'final' attached to each internal class.
Exceptions:
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.
Comment #15
alexpott+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.
Comment #16
alexpottI reverted the node back to #14 - sorry about that - it was an xpos - had the tab open too long.
Comment #17
Mile23Marking 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.Comment #18
greg.1.anderson CreditAttribution: greg.1.anderson at Pantheon commentedPatch 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.
Comment #19
greg.1.anderson CreditAttribution: greg.1.anderson at Pantheon commentedHere'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.Comment #20
greg.1.anderson CreditAttribution: greg.1.anderson at Pantheon commentedHere 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.
Comment #21
greg.1.anderson CreditAttribution: greg.1.anderson at Pantheon commentedHere are the patch files that should have been in #20.
Comment #22
greg.1.anderson CreditAttribution: greg.1.anderson at Pantheon commented#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.
Comment #23
Mile23+1 on just @internal as per #11. Maybe re-up the patch so it's the last one in the queue.
Comment #24
greg.1.anderson CreditAttribution: greg.1.anderson at Pantheon commentedRe-uploading #11 as #24 for the avoidance of doubt.
Comment #25
MixologicSo 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.
Comment #26
greg.1.anderson CreditAttribution: greg.1.anderson at Pantheon commentedI updated the Issue Summary to cover #25:
Comment #27
Mixologic^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.
Comment #29
Gábor HojtsyAssigning credits. I agree making these internal is better than not :)
Comment #33
Gábor HojtsyCommitted, thanks all! Leaving open for finishing off the docs updates as scoped in the issue summary.
Comment #34
greg.1.anderson CreditAttribution: greg.1.anderson at Pantheon commentedI 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.
Comment #35
mradcliffehttps://www.drupal.org/node/2562903/revisions/view/11328169/11757531
Should be "but are instead packaged separately".
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."?
Comment #36
greg.1.anderson CreditAttribution: greg.1.anderson at Pantheon commented@mradcliffe Thanks for the improvements. Please make minor grammar corrections directly to the documentation page.
Comment #37
andypostLooks it needs changes for #3116405: Warnings generated when using an optimized autoload file with Composer 1.10 / Composer 2
Comment #38
greg.1.anderson CreditAttribution: greg.1.anderson at Pantheon commentedWas #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?
Comment #42
quietone CreditAttribution: quietone at PreviousNext commentedI have updated the doc per #35.
That was the last thing to do here. Someone what to check?
Comment #43
SpokjeLooked 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)
Comment #44
Mile23What 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?
Comment #46
catchI think the docs update looks great, going to go ahead and close this.