Problem/Motivation
I ran into a issue today testing using a recipe in place of an install profile. Everything appears to have gone fine, except I cannot update any modules that were required by the recipes composer.json file.
Steps to reproduce
Create site with core/recommended-project template via composer.
Require my test recipe via Composer.
Now that that recipe is required and listed in the root composer.json dependency, composer is now adhering to the version constraints of that recipes composer.json.
That means if an update comes along and Pathauto gets a 2.0 version, I cannot run 'composer update drupal/pathauto' or 'composer require drupal/pathauto:^2.0' because it will error with a conflict. Using -W does not get around this either. This will make mixing/matching recipes tough and or 'breaking free' of an install profile style recipe when you need to perform module updates.
Proposed resolution
Following whats been done here: http://fabien.potencier.org/symfony4-unpack-the-packs.html
It would be nice to see a similar behavior of these 'unpacking' to the root composer.json so it behaves closer to a traditional composer managed Drupal project. Otherwise, site owners will be locked into whatever recipes like these had at the time they were downloaded and applied. If the recipe is abandoned, won't this make updating those modules not possible?
Similarly, if you want to remove a recipe from a project to prevent any accidents in a mature project, composer should not try to remove the dependencies of that project from you. That should be an explicit action by the site owner / developer.
Remaining tasks
Add test coverage of the new composer commandcomposer drupal:unpack "recipe-package-name"- Add test coverage of unpacking a recipe with package manager
Check existing test coverage to make sure it is goodWrite a change record
User interface changes
None
API changes
New composer plugin. It's not really API.
Data model changes
None
Release notes snippet
A new Composer plugin has been added that manages "drupal-recipe" packages. Recipes are special Composer packages designed to bootstrap Drupal projects with necessary dependencies. When a recipe is required, this plugin "unpacks" it by moving the recipe's dependencies directly into your project's root composer.json, and removes the recipe as a project dependency.
| Comment | File | Size | Author |
|---|
Issue fork drupal-3355485
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
- 3355485-dependencies-should-be
changes, plain diff MR !11071
1 hidden branch
Issue fork distributions_recipes-3355485
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
- 3355485-recipes-unpack
changes, plain diff MR !149
Comments
Comment #2
kevinquillen commentedComment #3
kevinquillen commentedComment #4
yonas.legesse commentedI have created a WIP composer plugin for this functionality and available under https://gitlab.ewdev.ca/yonas.legesse/drupal-recipe-unpack. I'll be adding documentation and further features soon.
Steps:
1. Add the git repos under the projects repositories list as follows:
2. Run the install of the package and the recipe.
composer require ewcomposer/unpack:dev-master
In my case, for the purpose of testing, i created a custom recipe and used it.
composer require drupal_recipe/startup
Then you just need to run the unpack command to update the projects package list:
composer unpack drupal_recipe/startup
It will update the composer and lock file with the dependencies available in the recipe.
Comment #5
kevinquillen commentedThat is interesting. I will have to give that a try.
Once unpacked, does it complain about version constraints between the main and recipe composer.jsons?
Comment #6
yonas.legesse commentedNot yet, but will work towards that. It will be a matter of adding some functions to check the constraints and maybe (if needed) provide a interactive session. Right now, the feature available is the ability to unpack the recipes packages into the projects package list and allow you to update them individually. Once the process is done, it removes the package dependency of the recipe from the composer and lock file.
Comment #7
kevinquillen commentedThis was the result of unpack:
From this recipe:
to:
I wasn't sure what to do here necessarily - for example, Pathauto has no 2.0 release. That was just a theoretical I posed in the original question. Most of these do not have a major release above the ones listed in the recipe.
A few questions:
[>= 1.0.0.0-dev < 2.0.0.0-dev]? Couldn't that be^1.0||^2.0?Comment #8
yonas.legesse commentedFor the purpose of testing, i had hardcoded the version to *, which might be why you're seeing these issue. As i'm currently working on a startup recipe, i've noticed it as well. Let me update the plugin to account for the provided versions.
Comment #9
yonas.legesse commentedI've added pretty constraint reference and should extract the correct version. I've used the the sample recipe composer you provided and getting the following:
Comment #10
thejimbirch commentedAdding the DrupalCon tag as discussing today.
Comment #11
sonfdI've been testing this and I've seen some success. One high-level thing that I'm wondering: should we postpone removing the recipe as a composer dependency and just focus on the first part, pulling the recipe's dependencies into the root composer.json, at least for now?
Comment #12
sonfdFollowing up from #11 - it feels odd to
composer installa recipe, then upack it. You end up with the recipe in your codebase, installed by composer, but it's no longer managed by composer.Comment #13
sonfdCurrently working on this.
Comment #14
yonas.legesse commentedI understand the issue and completely agree. As one of the aims with the recipe initiative is to allow "application of a recipe at any point within the project cycle" as well as "allowing for updates", i like your suggestion. In that case, we can just update the plugins code to avoid removing the recipe once it unpacks it.
Comment #15
yonas.legesse commentedHowever, maybe we have to re-define the scope of the composer plugin as to what it's entire functionality should be.
Comment #16
sonfd@yonas.legesse - as I played with this more, a couple things I've been thinking about are:
Also worth noting, I'm in the middle of some work building upon your initial share above, e.g. the recursing piece. I wonder if we can create a project on d.o so that we can collaborate more easily and review changes, etc.
Comment #17
yonas.legesse commented@sonfd, i really appreciate your suggestions. I was especially thinking about the recursive unpacking so would love to clone the project and collaborate. Two suggestion i have:
1. Lets define a features list for this plugin to server as a map
2. Do you think this should be a d.o project or fits more into a packagist library?
Comment #18
sonfd@yonas.legesse -
Agreed. I think this is a packagist library, ultimately, and is entirely decoupled from Drupal. At the end of the day, we're really just targeting a package type, which happens to be
drupal-recipe, but it should work for any other package type too.To me, MVP looks something like this:
1. A command
composer unpack [packages]that copies all requirements of a package into composer.json.2. A configuration option, set in
composer.json, that specifies which package types should be auto-unpacked, i.e. unpacked when they're required or updated.3. A configuration option, also set in
composer.json, that specifies which package types should be recursively unpacked, i.e. unpacked when encountered during an unpack action. (Perhaps this should be the same setting as auto-unpacking. The case that I think about is if someone doesn't want to auto-unpack on require, but definitely would want packages to be unpacked recursively when they callcomposer unpackmanually. For example, if manually unpacking a recipe that requires other recipes.)Intentionally left out of the list is removing the unpacked package from
composer.json. Though, I think that's probably another configuration option incomposer.json.Comment #19
yonas.legesse commentedI've added the suggestion as a features roadmap within the README. I've created the project on Github https://github.com/woredeyonas/Drupal-Recipe-Unpack. Lets create the features as milestones and continue.
Comment #20
sonfd@yonas.legesse - Thanks for sharing! I opened one issue, recommending that you rename to
composer-unpackto keep it a generic composer plugin.Additionally, I have my own repository where I took your original code and started working on it. I'll fork your repo and open some PRs sometime this week.
Thanks again.
Comment #21
sonfdComment #22
thejimbirch commentedComment #23
mallezieReading up a bit on the symfony unpack feature, they even made it the default (auto) behavior now to unpack composer packages when installing symfony packs (which are really similar to what recipes are).
Was wondering if we could just pull in symfony/flex and then use the symfony unpack command.
https://github.com/symfony/flex/tree/2.x/src/Unpack
Comment #24
yonas.legesse commented@mallezie the symfony/flex package is a huge pack and done for starting up a boilerplate symfony project with minimal configuration and that might be a jargon for our use case. I've tried that and didn't seem to work out of the box and might need some workaround. So, here's what we can do:
1. Try requiring it in a default project and see if it works. If not, see what work around is required and whether it will be acceptable
2. Think about scalability and how much we want to add features to this plugin pertinent to Drupal recipe.
Comment #25
thejimbirch commentedComment #26
thejimbirch commentedI used https://gitlab.ewdev.ca/yonas.legesse/drupal-recipe-unpack today.
Worked as expected, although there was no success message or anything upon completion. That would be good to add to give a user the assurance that it worked.
I believe when I had packages in my site's composer.json file with specificity, it did not update when my recipe specified drupal/package: '*'. I don't know if that is expected behavior, I just wanted to call it out.
Thanks for a cool tool!
Comment #27
alexpott@yonas.legesse is it okay if we add you code to the recipes fork and make it a part of the initiative?
Comment #28
yonas.legesse commented@alexpott Absolutely. I would also volunteer to work on it to make it a complete tool.
Comment #29
thejimbirch commentedIf a recipe requires another recipe, are the dependencies from the dependent recipe unpacked also?
Comment #30
thejimbirch commentedThe answer is no. If I require a recipe package that requires other recipe packages, when I unpack, I get dependent recipes unpacked to my site's composer.json file.
This works probably exactly as expected. However, if I only depend on the recipes, they could update/change the recipes and dependencies, and since I don't re-apply the recipe, I am tied to a stack of dependencies that could get out of sync.
New question is, should all the dependencies of recipes be unpacked?
This could also be documented to developers. When you apply recipe A, just unpack sub-recipe-B, sub recipe-C, etc..
Comment #31
thejimbirch commentedComment #34
robert-arias commentedPer the new requirements (see Slack thread):
The recipe unpack plugin should unpack a recipe upon running
composer require drupal/a-recipecomposer requirewould only unpack a recipe's dependencies and it will up to the user whether they apply the recipe or not. What we're trying to avoid is adding a recipe as a dependency, as that's the whole premise of recipes.About how we want to handle a recipe removal, that's another discussion. With this approach, the recipe package reference wouldn't exist in composer.json so
composer removewould not find it. The remove functionality could be either added into the composer plugin, as a new drush command, or part of the drupal script.Comment #35
robert-arias commentedI worked on adding an unpack composer plugin into core.
This plugin will unpack a recipe's dependencies into the root
composer.jsonand update thecomposer.lockas well. If a recipe has other recipes as dependencies, those will also be unpacked.I created the code working around the idea proposed by @thejimbirch of adding unpacking support for other types of projects (i.e, modules or themes). This way, creating other unpackers should be easy.
Pending tasks:
Add support to also unpack patchesI will working on the unpack patches during next week and work as feedback is provided, then start adding tests to core for this plugin.
How to test manually
composer config allow-plugins.drupal/core-composer-unpack truecomposer require drupal/core-composer-unpackUpon installing it, its dependencies must be unpacked into your root composer.json. If your recipe depends on other recipes, those recipes should be unpacked as well.
Shout out to @yonas.legesse - I used part of its plugin to create this, thanks!
Comment #36
robert-arias commentedComment #37
b_sharpe commentedI tried the above, and it appears to be working as expected with multiple tests and different recipes/dependencies, though I'm not convinced removing the recipe itself from composer.json/lock is the correct approach here.
Given we've now unpacked the dependencies, what's the concern of having the recipe here?
If the recipe was still in composer, removing it there would keep all the unpacked dependencies, and literally just delete the recipe.
Also, because recipes allow default content, how would you deploy a recipe to a remote environment without then re-requiring the recipe on that environment's composer? Is the expectation that the recipes folder is a committed folder rather than package managed? I wouldn't be entirely opposed to that, but I am slightly scarred from npm/bower-asset vs commit libraries folder :)
If the approach is to commit the recipes folder, and then delete them when no longer needed then this likely needs some docs around DX.
Marking NW for discussion as well as "Pending tasks" above.
Comment #38
alexpottRecipes should not become a content deployment mechanism. We need to improve default content tooling in the module and core so that this can happen.
Leaving recipes in the root composer.json then subjects them to being updated via composer update. Recipes are not intended to be updated and reapplied like this.
Comment #39
b_sharpe commentedAgree, but recipes currently do support default content, so how is a developer expected to deal with this?
With Recipes, I'd expect you require the recipe locally (which unpacks it's dependencies but not the recipe itself), apply it, export your config, commit and you're good to go.
With default content however, if the recipe requires the content to be functional (let's use the example of creating/setting the frontpage), then unless you're porting the DB to the next env, you're back to the same chicken/egg of content blocks in config. So how does this work?
Makes sense, and agree.
Comment #40
thejimbirch commentedWith the current functionality of default content, you cannot definitively create and set the home page. The node you create could be nid 14 on your local, but NID 20139 on the site the recipe is applied to.
I agree with you that functionality would be super awesome, but we haven't started to understand the default content module issues yet to be able to get core to do what we assume it should.
Comment #41
robert-arias commentedAdded support for patches in recipes composer.json. If the project's root composer JSON has the patches plugin and the recipes has patches in its composer.json, these patches will be unpacked into the root composer.json, as long as you have patching unpacking enable in your project's composer.json, like this:
This config should be within the "extra" section of your composer.json.
Pending task: tests!
Comment #42
robert-arias commentedComment #43
prashant.cI tested by following the steps from #35
and recipe dependencies are getting added to the root
composer.jsonAll the steps to test are already mentioned in #35 but it is unclear where to add the the following:
to the root
composer.jsonAdd it to the"repositories": [section.The repositories section will look something like this:
composer require kanopi/gin-admin-experienceHowever, when I tried to remove the recipe with composer
composer remove kanopi/gin-admin-experience, the dependencies did not get removed from the composer.json file.Comment #44
drubbWhere is this package hosted? Can't find it on packagist.org?
Comment #45
drubbAh, I see: once the MR gets merged, there will be a local instance of this plugin in composer/Plugin, which can be added to the composer repositories, allowing to require the plugin.
Ok, perspectively it might make sense to host it on packagist.org, just like the other core-xxx plugins.
Comment #46
phenaproximaA few things needed to get this moving again:
Comment #47
phenaproximaComment #48
phenaproximaLeft an initial review - I think my questions largely concern the overall complexity of the MR here. It feels a like there are more moving parts than necessary; I think we might want to try to simplify and streamline it as much as we can.
To be honest, I felt the same way about the scaffold plugin, but I was not involved in developing that. On the other hand, the scaffold plugin is powerful and has many options, so it might have been designed with that in mind. For my money, we should not follow in the scaffold plugin's footsteps unless we really need to. Supporting fewer options and less configurability at the outset is going to make this much easier to land, and maintain, and test. Simplicity is our friend!
Comment #49
robert-arias commentedFor #3355485-46: Dependencies should be 'unpacked' to the root composer.json and merged/resolved: yeah, I'll try to add some tests.
#3355485-48: Dependencies should be 'unpacked' to the root composer.json and merged/resolved:
I guess the complexity of it comes from two things:
1) I did base the code around the scaffold plugin, as I was not sure how these plugins are developed in core, so I agree that it's complex and I believe simplicity should be our approach.
2) I was thinking about extensibility when creating the plugin. The idea came from the conversation on the recipes channel, about maybe other unpackers in the future, if needed. So, that's why there's some configurability around it. If we don't think we'll need more unpackers in the future, I think we can simplify the code, but I do like the idea of it being ready in case of some other feature or requirement in the future.
Comment #50
robert-arias commentedAdded some base tests.
Comment #51
catchThis seems like a blocking issue for project browser/automatic updates to work with recipes so tagging with 'Drupal CMS stable release blocker'. I'm basing this on the understanding that if a recipe is installed, it will be impossible to safely remove it without unpacking, because doing so could also remove the dependencies of the recipe (which will usually be installed on the Drupal site but not in the root composer.json). This means that if we add a way to remove a recipe without this, sites will get broken, but also if that's not implemented, sites installed before this issue is done could end up with crufty recipes that are impossible for the site owner (using project browser) to remove.
Also concerned about the version constraints when unpacking. If a recipe has a
< 1.3constraint for any reason and we unpack that constraint to the root composer.json, it will prevent updates of the module with that constraint, even though there is no reason to prevent that (assuming the module has an upgrade path for its own config/content from 1.2 to 1.3). So feels like unpacking needs to essentially composer require ~$current_installed_version rather than copy constraints over - to allow later upgrade paths as if the site owner had just run composer require themselves in the first place (or had the module already installed etc.).Not sure if that makes it a CMS 1.0 blocker or not but it'll need to block the version that enables project browser installs/removals of recipes and not sure what the consequences are for the recipes that ship with Drupal CMS itself.
Comment #52
robert-arias commentedComment #53
phenaproximaThis is neither a Drupal CMS release target nor a stable blocker. It'd be nice to have, but it doesn't block anything.
If someone spins up Drupal CMS with dependencies on recipes, that's...fine. They can continue operating their site with the recipes keeping their dependencies in place. When unpacking becomes possible, they'll be able to remove that intermediate layer of dependencies...but it's not a big deal for those to sit there while we work on this.
Comment #54
thejimbirch commentedComment #55
catchDiscussed with @thejimbirch in slack and decided to move this to core. If there's a really good reason to keep it against the recipes and distributions project that's fine but seems more historical that it was posted there - now that recipes is in core and we've made several changes directly.
Comment #56
phenaproximaThis is very much on my list to review when I'm back next week. I'd like Drupal CMS to adopt it as soon as humanly possible.
Comment #57
thejimbirch commentedRemoving Needs tests tag as tests were added.
The issue was moved to Drupal core, but the MR needs to be moved also.
Comment #60
b_sharpe commentedMR moved over to core, looks like some cspell and phpstan failures need addressing.
Comment #61
thejimbirch commented4 Failing PHPstan tests remain.
Comment #62
robert-arias commentedFixed PHPStan error on
UnpackCollection::getIterator().I'm not quite sure about the errors on
core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Fixtures.phpthough. They are void functions—no need for return type.Comment #63
penyaskito@robert-arias These are not about actual errors, but known errors to be ignored. As they don't happen anymore they need to be removed from core/.phpstan-baseline.php
Comment #64
robert-arias commentedComment #65
thejimbirch commentedComment #66
a.dmitriiev commentedI have tested the unpacking with nested recipe (more than 4 levels) and it worked correctly. Unfortunately the patches unpacker was moved to follow-up issue, so this is still not completely covering my use case, as some recipes Do have patches, and transferring them manually is quite a big task.
I have a silly question: how to unpack already installed recipe? The recipe that was added before the core-composer-update package. Is there a command to manually trigger the unpacking? I believe all users who were waiting for unpacker in core and had their recipes in composer.json would like to get rid of them when it is possible. So it would be nice to know how to trigger the command for certain package manually, if it exists, if not - I think that would be a good addition to this feature.
I would love to move it to RTBC, but there is some non-addressed feedback in the MR.
Comment #67
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #68
alexpottI've rebased the MR on top of 11.x to make it mergeable.
Comment #69
alexpottI'm working through the comments on the MR.
I've added a new drupal:unpack RECIPE/PACKAGE command to help existing sites.
Still to do:
Comment #70
alexpottComment #71
alexpottNote that now we have the composer command linked up for a core checkout this code can easily be tested. On a core checkout on this MR branch you can do:
If you want to test automatic unpacking you can update the root composer.json add set
on-install-and-updatetotruein the extras section.Comment #72
sonfd@phenaproxima @thejimbirch and I spoke about unpacking in the contrib room at DrupalCon Atlanta. (Some initial discussion is in this slack thread.) Our discussion was about whether recipes should be auto-unpacked and when should they be auto-unpacked. I'm documenting here while it's fresh.
Our starting point was that recipes will be (unless the drupal-unpack plugin is disabled) auto-unpacked when the recipe is required with
composer require. This creates two scenarios that we're not sure about:For #1, I think our consensus is that this scenario is not ideal, but also maybe not that big of a problem. In many situations, e.g. while using Project Browser to apply a recipe, users will have no awareness of their composer.json and having extra dependencies won't cause them any problems. And there's been talk of a composer plugin to remove orphaned dependencies that could further reduce the relevance of this problem.
For #2, one idea is that unpacking recipes should be a game-time decision. Maybe by default recipes should be auto-unpacked on require, but something like
composer require drupal/some-recipe --no-unpackwould prevent the recipe from being unpacked. (But according to some, it may not be possible to add this flag to composer require.)Comment #73
phenaproxima@thejimbirch and I discussed this issue with @alexpott.
One thing we agreed on is that this should be for recipes only. We can remove UnpackerInterface and UnpackerFactory; they won't be needed. We're not going to need extensibility for the foreseeable future. To make it even clearer that this plugin is only intended for use with recipes, we should rename it to
drupal/core-recipe-unpack, under theDrupal\Composer\Plugin\RecipeUnpacknamespace. Any configuration flags should be underextra.drupal-recipe-unpackincomposer.json.I think we also agreed that a reasonable default workflow is that the recipes are unpacked as soon as they are required into your site, and are not kept as Composer-managed packages.
Will that leave orphaned dependencies in your composer.json if you ultimately don't apply the recipe you just required? Quite possibly, yes. Having an additional plugin to remove dependencies that you're not actually using would be helpful, but is not in scope here; we could add that in a separate issue. Or maybe it could be implemented by Package Manager. Either way, it's not our problem right now.
I think that this "auto-unpack" behavior should be configurable, and enabled by default. If a site builder chooses to disable it, they can still call
composer drupal:recipe-unpack foo/barat the command line to unpack it later.So, kicking this back for those changes.
Comment #74
thejimbirch commentedComment #75
phenaproximaI ended up removing the unit test here because:
Comment #76
phenaproximaAlso, one thing we have not yet handled here, as far as I can see: What if a recipe has a dependency that is already required in the top-level composer.json?
IMHO, the existing requirement and constraint should "win", and be kept as-is. This will definitely need functional test coverage.
Comment #77
catch#3500346: [meta] composer require / install module discrepancy issues is open against project browser for a UI to be able to remove unused dependencies. We should possibly open a core issue for the detection and removal bits themselves which could maybe live in package_manager?
Agreed it's fine to ignore that here, lots of ways that sites can end up with unused composer dependencies already.
Comment #78
sonfd@phenaproxima - I think handling for a recipe requirement already being in composer.json is accounted for ~ line 116 of RecipeUnpacker and works as you want, though I didn't check on tests.
Comment #79
phenaproximaAh, you're totally right! Glad it's there. I do think that will need test coverage, then.
Comment #80
alexpott@phenaproxima I added test coverage of an existing dependency. I've also got test coverage of moving a dep from require-dev to require and not moving a dep from require to require-dev :)
Comment #81
thejimbirch commentedComment #82
alexpottI've got unpacking of require and require-dev recipes working. We've got some decisions about behaviour to decide.
We need to decide how the following situations work...
We have recipeA and recipeB. recipeA depends on recipeB.
What should happen when recipeA is in require-dev and recipeB is in require and you call unpack recipeA?
What should happen when recipeA is in require-dev and recipeB is in require and you call unpack recipeB?
What should happen when recipeA is in require and recipeB is in require-dev and you call unpack recipeA?
What should happen when recipeA is in require and recipeB is in require-dev and you call unpack recipeB?
For more information on this problem space if you do the following on an empty directory:
The second require will offer to move the dependency. If you answer no then it will not do the include.
Comment #83
phenaproxima@alexpott and I discussed this extensively in a Slack DM.
Here was the long thought process I had, for those interested:
My summation, your honor
Recipes introduce dependencies on modules and themes that Drupal will expect to be present, or it will WSOD. If you have any of those dependencies in
require-dev, you are onecomposer install --no-devaway from breaking your site completely. (Omitting dev dependencies from production is a best practice.)We cannot safely assume that site builders, who may have zero understanding of or experience with Composer, will know that installed modules and themes effectively must be in composer.json's
requiresection.It is also much less important for unpacking to result in "everything in its right place", than for it to not break your site.
The proposed solution
@alexpott came up with what I think is an excellent solution, that sidesteps the problems outlined in #82:
Yes. YES!!! A thousand times yes to this.
Comment #84
phenaproximaI should note that the solution in #83, while great, doesn't actually remove all risk to site operators. Consider:
This will still bring in modules and themes, as indirect dependencies of the recipe. Therefore, you could still break your site with
composer install --no-dev.But, @alexpott and I feel that this is an acceptable trade-off, for a few reasons:
require-dev. It's just that the recipe adds a layer of indirection. So it's a tad worse, but not much.composer require RECIPE --devat the command line. It would have been an explicit choice made by a developer (or another command-line jockey).composer require's default behavior is to add the recipe torequire, notrequire-dev.If, despite these mitigating factors, you do somehow manage to get a recipe into
require-dev, the unpack plugin will try to nudge you in the right direction, as #83 states:If, after all this, you still have a recipe in
require-devwhich has introduced runtime dependencies, and you have explicitly refused to unpack it intorequire, then that's on you, o mighty power user. :)Comment #85
alexpottI've implemented #83/#84
Comment #86
alexpottNext thing to do is to not do anything when
--dry-runis passed into therequirecommand. Working on that.Comment #87
alexpottSo --dry-run works but it reveals another issue. Here's a useful composer.json for testing unpacking.
Replace /PATH/TO/YOUR/DRUPAL/MR_CHECKOUT with the correct value for your set up...
If you do this then run the following commands on a directory with this in:
You'll see it is unpacked...
Then do...
You'll see it is not unpacked.
This is because we find recipes being added during the post-package-install event but this is not triggered the second time around because the packages are there. I think we need to do cleverer stuff in post-update-cmd as this is triggered both times. And then we might need to deal with --dry-run :)
Comment #88
alexpottOkay I've fixed #87 and will still work with a --dry-run ... I'm going to add test coverage for both things and then I think we're ready for another round of review and rtbc :)
Comment #89
alexpottOkay I think we're 99% done. We just need to add tests for two things:
Comment #90
alexpottOh and I think we should add a follow-up to implement the ability to unpack multiple or even all the recipes in a root composer.json. Atm the unpack command accepts a single package name.
Comment #91
alexpottI've addressed #89 but in doing that I've revealed the two of the tests are resulting in invalid composer files so we need to fix that.
Comment #92
alexpottWe have a problem...
If you have a composer.json as follows
and then do the following:
1. composer install
2. composer require drupal/events
3. composer require drupal/jwt
Step 2 will successfully unpack the recipe but when you do Step 3 it'll detect the recipe has been removed and uninstall it. Which is something we want to avoid...
Oh dear.
Comment #93
mallezieRe #92
Don't we just need to keep the recipe in the composer.json? As in Drupal/events (i assume that's a recipe).
Unpacking should make it possible to remove a recipe after it was applied, but not perse, remove it?
I think in #71 we should not do this.
I think we also agreed that a reasonable default workflow is that the recipes are unpacked as soon as they are required into your site, and are not kept as Composer-managed packages.
They should be kept, else indeed we have a problem. Just keep them as composer managed packages. What the unpacking does is allowing to remove them (after applying) so that dependencies are not removed, if you want it to remove it you should explicitly composer remove Drupal/recipe after applying, and the you are free from the recipes dependencies to allow to solve version of dep module updates.
Comment #94
catch#93 sounds reasonable to me. Project browser currently doesn't support removing composer dependencies, but #3500346: [meta] composer require / install module discrepancy issues is open to look at that, so eventually some kind of UI-based clean-up of composer dependencies could happen.
Comment #95
alexpottAdded a change record.
I'm not sure why we need to add test coverage of the package manager here. I think the package manager needs to obey composer plugins or we have a problem.
Comment #96
alexpottFWIW this MR is ready for extensive testing. I have pinged the Symfony and Composer maintainers to see if I can get some feedback on the gnarlier bits of futzing with Composer internals as I'm not sure how easy it is to review this part of the MR.
Comment #97
phenaproximaTagging for a follow-up per https://git.drupalcode.org/project/drupal/-/merge_requests/11071#note_50....
Comment #98
phenaproximaTook a bit of doing but I gave this a manual test with Drupal CMS.
All of this appeared at the end of the
composer create-projectoutput:...and they were all in
recipes:composer.jsonhad significantly more dependencies than it normally would with Drupal CMS, including some recipes. For example,"drupal/drupal_cms_content_type_base": "~1.1.0"and"drupal/drupal_cms_image": "~1.1.0"were inrequire. This makes sense -- those packages are recipes, but they are strictly a dependencies of other recipes.Did we decide, ultimately, that we were not going to unpack recursively? Or is this a bug? Both of these dependencies are at least two levels down in the tree from the top-level dependencies. I would guess that this is a bug, because some of the recipes that got unpacked are secondary dependencies -- so we appear to be recursing, just not far enough.
I was not, unfortunately, able to install Drupal CMS with all optional recipes via the UI. I ran into this:
The recipe is physically present, but this is a bit problematic -- the installer needs to be able to ask
InstalledVersionswhere a recipe is. I think this means that we'll have to adddrupal/drupal_cms_starterto the ignore list in our project template, or make the installer code fall back to some kind of dumb "guess the recipe path" logic, based on theinstaller-pathsconfigured incomposer.json. I don't love that, but it's not the end of the world. That's not a problem with the unpacker itself, but with Recipe Installer Kit.Comment #99
thejimbirch commentedI believe we decided that recipes would be unpacked recursively. That is the only way we'd ensure the functionality of those recipes can be maintained moving forward.
Comment #100
alexpottJust pushed some changes that will fix #98... pretty sure... added test coverage after reproducing the issue with a minimal set.
Comment #101
phenaproximaDrupal CMS needs this for long-term viability, so tagging it as Drupal CMS-critical.
Comment #102
phenaproximaI've reviewed this extensively and I feel pretty confident about it. I think Alex had found another bug in here that still needs to be fixed, so leaving at "needs work" for now, but this is close to RTBC. Adding at least partial credit.
Comment #103
alexpottComment #104
phenaproximaManually tested the latest against Drupal CMS and ran into a failure during
composer create-project:Comment #105
phenaproximaManually tested this with Drupal CMS 1.1.1.
I modified its project template to require the unpacker (from a local checkout) and allow it:
Then I used that modified template to create a local project:
It worked fine -- everything unpacked:
The
requiresection of composer.json is extensive, but contains no recipes:(
drupal_cms_analyticsis a metapackage, anddrupal_cms_oliverois a theme).composer validatewas happy; no errors were found.The real test, of course, was being able to actually install Drupal CMS. I had to make a small change to Recipe Installer Kit, since it expects all recipes to be Composer-managed (I will permanently fix that in Recipe Installer Kit once this is committed and available for use). But installation succeeded, with all optional recipes selected!
This truly has been reviewed and tested by the community. Let's get it into 11.2 so Drupal CMS can rightfully adopt it and blaze the trail of an update and maintenance path for sites built upon a foundation of recipes.
Comment #106
longwaveAdded some questions/nits from reading the code only, haven't done any manual testing yet. Leaving at RTBC to encourage other reviewers :)
Comment #107
larowlanNeeds a reroll after PHPUnit 11 MR went in but other than that this looks great, just a couple of MR comments
Comment #108
alexpottGiven this was set to needs work for a reroll - setting back to RTBC as the reroll is done.
Comment #110
larowlanCommitted this to 11.x so we can get more eyes on it. It's internal non production code so the risk is low in doing it this late in the alpha window.
Thanks all
Published the change record
Added a release note snippet and the corresponding tag.
Comment #112
grimreaperHi,
I have created a child issue #3525727: Recipe Composer plugin: support wikimedia/composer-merge-plugin because the new Composer plugin is not usable on projects using wikimedia/composer-merge-plugin.
Needing feedback on what a long term solution would be.