Problem/Motivation
Backporting #3126566: Allow Drupal to work with Composer 2 to the 8.9.x branch exposed a Composer bug that caused composer update commands that should have upgraded to version 8.8.5 to instead upgrade to the unstable dev release 8.9.x-dev. The full details of this bug are described in composer/composer #8882.
Briefly, Composer confuses 8.9.x-dev as a stable release when updating drupal/core-recommended and composer/installers in the same composer update command. Composer becomes confused because Drupal 8.9.x-dev requires composer/installers 1.9.0, whereas Drupal 8.8.5 (and similar) requires composer/installers 1.7.0. Updating to 8.9.x-dev should be prevented by the prefer-stable flag, but somehow the fact that composer/installers 1.9.0 is stable, and upgrading to that version requires Drupal 8.9.x-dev, Composer apparently
forgives the non-stable status of Drupal 8.9.x and allows that upgrade in order to also allow composer/installers to upgrade to 1.9.0.
This bug can be avoided by setting "minimum stability" to "alpha" or better, or by pinning composer/installers to version 1.7.0 in the top-level composer.json file.
Proposed resolution
We presume that this Composer bug could be avoided if we remove composer/installers from drupal/core-recommended. composer/installers is not in the page-serving path, so it should be safe to allow it to float in Drupal projects that use drupal/core-recommended.
Additionally, we will make a new stable 8.8.6 release that does not have composer/installers pinned in drupal/core-recommended. This will make it more likely that Composer will find a stable 8.8.x release and not try to upgrade to 8.9.x-dev.
Remaining tasks
Removecomposer/installersfromdrupal/core-recommendedAdd a unit test- Release 8.8.6
User interface changes
None
API changes
None
Data model changes
None
Release notes snippet
The project composer/installers is no longer pinned in drupal/core-recommended. It may now be upgraded whenever a new version is available, and a Composer-managed site runs composer update.
| Comment | File | Size | Author |
|---|---|---|---|
| #62 | 3134648-8.9.x-41.patch | 4.39 KB | alexpott |
| #62 | 3134648-2--8.8.x-35.patch | 4.39 KB | alexpott |
Comments
Comment #2
catchComment #3
abaier commentedSo … what to do if this already happened on a production site? Yesterday we only pushed two CSS files to the server without running composer update locally before (shame on me). The rollout did its job, though, leaving us with core 8.9.x-dev on the live site.
Because there were db updates, I think I could probably not come back to 8.8.5 without a db backup, right?
system module : 8901 - Update the stored schema data for entity identifier fields.
action module : Removes action settings.
views module : Update field names for multi-value base fields
What do you suggest? Thanks in advance.
Comment #4
catchYou shouldn't try to downgrade a database that's already updated, so you have a choice of restoring a backup or staying on 8.9. The release candidate for 8.9.x will be released next week so if you're not able to restore a backup, you're at least on something that's about-to-be-stable.
In general I would not recommend running composer update as part of a deployment process at all - this should be part of the development rather than deployment process.
Comment #5
abaier commentedSounds good, thanks for the quick answer. That was indeed what I intended to do, since the last db backup was written a few hours before the rollout … Will stay with 8.9 for now.
Regarding the rollout process itself – because I read a lot of different opinions about this:
Do you say I should better run composer update only locally and therefore also commit the "/vendor" directory and "composer.lock" to git?
Thanks again, Anton
Comment #6
alexpott@ABaier on a project you don't have to commit vendor. Committing the lock file should be enough.
Comment #7
catchI've re-opened #3126566: Allow Drupal to work with Composer 2. Assuming we're able to recommit with a 1.7 pinned dependency, we can repurpose this to tracking the upstream composer bug and/or finding another way to allow 1.9
Comment #8
ressa@ABaier: Check out the Update core via Composer page for more details.
Comment #9
abaier commentedThanks for clarifying @alexpott & @ressa and sorry for disturbing the context! As I understood correctly I should only run 'composer install --no-dev' on production and commit composer.lock to our git … instead of running 'composer update' on production.
Will change our rollout to this:
Comment #10
ressaThanks for sharing your new rollout @ABaier, that looks like a better and safer process! Yesterday I watched Composer 101 by Michael Miles. It was a great refresher for me, about the basics of Composer. This is my process:
Update Drupal and modules locally
https://www.drupal.org/docs/8/update/update-core-via-composer
https://www.drupal.org/docs/8/update/update-modules
Put in production on server
Note: Always run DB updates before importing configuration.
Comment #11
catchComment #12
alexpottRe-titling to be more specific. Drupal 8.9.x can work with composer installers v1.9.0 but the root composer.lock file is at v.1.7.0 and therefore thats the version that's used in composer/Metapackage/CoreRecommended/composer.json.
Comment #13
greg.1.anderson commentedComment from 3122112#59:
If we did that, then we could update composer/installers to 1.9.0 again in 8.9.x.
Comment #14
hussainwebSince there is a buy-in for the decision to remove composer/installers (and it makes sense to me too), here it goes.
Comment #15
greg.1.anderson commentedLet's move the composer/installer update back to #3122112: Update dependencies for Drupal 8.9
Comment #16
greg.1.anderson commentedI didn't RTBC this earlier today because I thought it might be a good idea to add tests to try and guard against this sort of failure happening again. I didn't "needs work" it either, though, because I wasn't sure how feasible it would be to do that.
I thought about it some more today. If we set up a packages.json file, we could conceivably have some fixtures that would align with a Drupal 8.8.0, and an 8.8.5, and and we could then build an 8.9.x-dev from the SUT, and try to upgrade from 8.8.0 to 8.8.5. However, it would be difficult to set up those fixtures without duplicating too much from the Drupal sources and/or relying on Packagist for dependencies. If we use Packagist, then we're relying too much on the internet, like #3123933: Determine whether ComposerProjectTemplatesTest is testing the internet, and if it is, avoid that. If we don't use Packagist, it's unlikely that we could build an accurate enough mock to make the test worthwhile.
Therefore, I think it's best to just RTBC this as it is. We have some existing tests that exercise this code.
Comment #17
alexpottHere are patches for 9.x.x. branches.
Comment #18
hussainwebThere's something wrong with the 9.0.x patch. There are a lot of unrelated changes in it. Possibly other changes got in there?
Comment #19
alexpottForgot to update my 9.0.x HEAD whoops...
Here's a fixed patch - also we can unit test this.
Comment #21
alexpottRerolled 8.9.x now that #3122112: Update dependencies for Drupal 8.9 landed.
Comment #22
alexpottI have a concern. I think that the patches here will solve the problem but I think we need to do this in 8.8.x too and make sure that it's committed there first and that 8.8.6 is released before 8.9.0. The reason we need to make this change is so that when composer tries to solve for a project built off the drupal/recommended-project project the composer.json looks like this:
"composer/installers": "^1.2",
"drupal/core-composer-scaffold": "^8.8",
"drupal/core-project-message": "^8.8",
"drupal/core-recommended": "^8.8"
The most recent composer/installers version is 1.9. The bug in composer meant that when we upgrade 8.9.x to support 1.9 composer determined that therefore we need to upgrade to 8.9.x-dev for drupal/core-recommended. Therefore if we commit the above patch the same is going to happen because the 8.9.x-dev version of drupal/core-recommended will allow 1.9 again but the 8.8.5 version does not. I think this means we need an 8.8.6 version that does.
Here's a patch for 8.8.x.
Comment #23
greg.1.anderson commentedI think that the Composer bug only happened because composer/installers was pinned to a specific version in drupal/core-recommended. We could not reproduce the Composer bug when only drupal/core as in use in the project.
Still, the above supposition is unproven; we won't know for sure that we have not exposed the bug until we ship 8.9x-dev again. #22 increases the odds that Composer will be able to find a stable release to upgrade to, and will not erroneously select 8.9.x-dev, so I'm +1 on releasing 8.8.6 before shipping this.
Updated the issue summary to reflect this strategy.
Comment #24
alexpott@greg.1.anderson re #23. I'm not sure about that. I think the moment we commit this to 8.8.x then the 8.8.x-dev release will become compatible with composer/installers 1.9.0 and therefore update to it. Therefore, I think we need to commit this to 8.8.x and tag a release asap. And also not commit this to 8.9.x until there is a tagged 8.8 release with this commit in.
It can be committed to 9.x whenever because the drupal/core constraints in core-recommended don't allow for update to 9.
Comment #25
greg.1.anderson commentedSorry I confuzled what I was trying to say in #23. I meant that I am +1 on committing this issue and releasing 8.8.6 before #3134648: [backport, needs scheduling] Don't pin the composer/installers version in drupal/core-recommended.
Comment #26
greg.1.anderson commentedHere's another test that we can add to this patch. The test in #19 only ensures that composer/installers is being pinned at the expected sha. This does not help us determine if it is safe to commit a patch that updates composer/installers. It also adds an additional burden that this sha has to be updated in the test every time composer/installers is updated, and there is little additional value for this burden.
We have determined that a key characteristic of this Composer bug is that a top-level requirement of a template project is updated in drupal/core-recommended. Projects that are not in core-recommended, or projects that are not top-level dependencies do not seem to cause this problem.
This failing test demonstrates a check for the condition that a top-level dependency is also pinned in core-recommended. While this test does not directly detect the situation where Composer will upgrade you to the next "dev" version prematurely, we have strong indications that the situation that this test checks for has a causal relationship with that bug, and is therefore dangerous.
If we add this test to #22 & c. it should pass. I'll do that in the morning.
Comment #28
dwwThanks for the additional test in #26, that coverage looks helpful.
There are a handful of CS issues and doc fixes needed. Instead of a nit-picky review, I'm attaching a cleaned up version of it.
Also attaching just #22 and #26 applied, and an interdiff between that and my version of #26.
Comment #29
dwwSorry, hadn't seen the unused use results when I uploaded #28. This is better.
Comment #30
dwwWhoops, missed one more:
s/dependnecy/dependency/
Comment #32
xjmAdding a bit about #24 to the title because otherwise we might forget and commit it the normal way. It's not clear to me whether #25 is validating that suggestion or not since it references this issue as if it were a different issue? Confirmation would be good. Thanks!
Also, we shouldn't assume people are on their 8.8.5 now or that they'll update to 8.8.6 when it comes out. I guess the idea is that we're trying to change the behavior of Packagist by ensuring the 8.8.6 tag exists with this change, which should benefit all sites.
What about 8.7? Does the fact that 8.8.0 - 8.8.5 exist prevent 8.7 sites from being affected by thisw?
Comment #33
alexpottRe 8.7.x drupal/core-recommended does not exist there so we don't have this problem fortunately - otherwise we would have hit when we updated 8.8.x to use composer/installers 1.7 (8.7.x is on 1.6).
I disagree that this test in #19
It's testing that composer installers is excluded from the core-recommended build which is exactly what we want to test. The sha and everything doesn't actually need to be updated. This is a unit test of the \Drupal\Composer\Generator\Builder\DrupalCoreRecommendedBuilder class which imo is exactly what we want to do.
Comment #34
alexpottTIL run-tests.sh runs unit tests from inside the core directory.
The test added by #26 is nice though because it prevents us making the same mistake with another dependency so +1
Comment #35
alexpottReworked the new test to loop round the smaller loop (tiny optimisation but why not) and fixed test comments to match and for correctness.
Comment #36
greg.1.anderson commentedSorry, I was distracted by other things yesterday, and some of my posts came out wrong.
#32: In #24 my main point was supposed to be a strong +1 to commit this to 8.8.x and release 8.8.6 before committing it to 8.9.x. I am also in favor of doing this before re-committing #3126566: Allow Drupal to work with Composer 2, although as I and others previously mentioned, doing it in this order is not strictly required.
#33: Hm, I will have to look later at where that sha is coming from. In any event, I am +1 on keeping #19.
I can't RTBC here but I'm +1 on #35. Thanks for the fixes and improvements.
Comment #39
greg.1.anderson commentedPlease also see #3135247: Composer's "prefer-stable" setting cannot be relied on to produce a stable release.
Comment #40
dwwRe: #34 - Ugh, yeah. The test passed locally when I ran it via phpunit. When I saw the failure, I figured it was some
run-tests.shweirdness. Thanks for sorting it out!Comment #41
alexpottHere's patches for all the other branches with the latest changes.
Comment #42
alexpottI've only added tests and test fixes to this patch. The fix was made by @hussainweb in #14. Both @greg.1.anderson (who has also added tests) think this rtbc - see #36. Therefore this is rtbc.
I think we need to commit this to 8.8.x first and wait for the github mirror / packagist to get a new 8.8.x-dev and the test running composer update on a core-remmended project. Once we confirm that you're not updated to 8.8.x-dev (which not as bad as been updated to 8.9.x-dev) then we can commit this to the other branches.
Comment #43
abaier commentedWell, today I ran
composer updateon my 8.9.x-dev environment (#3) today ... where I wanted to stay for now. But it was downgraded back to 8.8.5. Is this on purpose?Comment #44
greg.1.anderson commented@abaier Perhaps your Composer configuration was depending on a bug in dependency resolution, and was downgraded now that that bug was fixed?
If your top-level composer.json has `prefer-stable: true`, then Composer will in general prefer 8.8.5 over 8.9.x-dev, even if you are already on the newer release. If you want to stay on 8.9.x-dev, then change your version constraint for Core to:
Instead of "^8.8".
Comment #45
greg.1.anderson commented#42: That commit order is safe so long as we get this patch committed to all branches before going up to composer/installers ^1.9 on 8.9.x-dev.
Comment #46
abaier commentedThanks for your help, Greg! I will
try it like that.
Comment #48
catchCommitted/pushed #35 to 8.8.x, let's see what happens with packagist. Leaving RTBC for the other branches. We should also make sure to explicitly test what happens after the 8.9.x commit too prior to tagging a new release.
Comment #49
alexpottSo step 1 looks like this...
So we need to revert and push harder on the composer issue.
Comment #51
alexpottConfirmed that the revert fixes #49.
Comment #52
alexpottSo one thing we can do here is commit #41 to Drupal 9.x because at least we can avoid some of this mess there. Moving to 9.x for that whilst we work out how to deal with Drupal 8
Comment #53
alexpottChanging the issue title to reflect #52. The important thing is that we don't commit this (on any other patch that allows anything other than composer/installers 1.7.0 for the core-recommended project to Drupal 8.x.x. Otherwise we'll end with people being updated to dev releases.
This is good for Drupal 9 because we have no stable Drupal 9 releases yet and core-recommended in Drupal 8 does at this minute result in updating to Drupal 9 (if it did then everyone would have been updating to Drupal 9 the moment we pinned composer/installers to 1.9.0 :D). I think this is more than "good" for Drupal 9 because this will stop us getting into the same trouble once composer/installers 1.10.0 comes out.
Comment #56
catchOK. Committed/pushed to 9.1.x and 9.0.x
Comment #57
xjmComment #58
xjmme:
greg.1.anderson:
So should we plan to commit this to 8.8.x... now? Then tag 8.8.7 Wednesday, then only after 8.8.7, commit it to 8.9.x and tag 8.9.0.
Comment #59
alexpottWe need to commit this to 8.8.x and tag 8.8.7 as close as possible. Whilst this is committed to 8.8.x and we don't have a tag then people on 8.8.6 will be updated to 8.8.x-dev.
And once we have 8.8.7 I think (not 100% sure) that we need to commit this to 8.9.x asap because anyone on 8.9.x might have a chance of being downgraded to 8.8.x-dev. Because 8.8.x-dev will be compatible with composer/installers:1.9.0 but 8.9.x-dev will still only allow 1.7.0.
I think in an ideal world we'd push the 8.8.x and 8.9.x commits at the same time and then tag and push 8.8.7 in the next moment.
Comment #60
greg.1.anderson commentedYeah I concur with #59.
Comment #61
MixologicWe have seen a similar issue with a user updating both drupal/core and drupal/core-dev at the same time, which had the same effect of updating to 8.9.x-dev. So I think the long term fix is to ensure that we no longer have minimum stability:dev and advise people to use stability flags in their requirements.
Considering that composer/installers is a top level dependency, this one is at a much higher risk of causing this sort of snafu, so the plan outlined in 58/59 looks like a reasonable way to shield us from composer selecting different versions if we upgraded the installers requirement for composer 2.
Given that, Im RTBC for this to go back to 8.8.x/Tag 8.8.7, then go into 8.9.x.
Comment #62
alexpottUploading the correct patches for 8.8.x and 8.9.x so they can have another run at the tests and so it's obvious what the correct patches are.
Comment #63
xjmComment #64
xjmComment #65
gábor hojtsyWould be great to have a change record so we can link it from the release notes :) Thanks!
Comment #66
alexpottBeen trying to work out what to put on the CR. But it’s hard to write one and know what to say. Like there is nothing a site, module developer or core developer to do different in the future.
For me this is more of a bugfix. If the release team’s plan works then no one should notice anything. Fingers-crossed.
If we did add a CR it would say exactly what the release notes say ie.
Which kinda boils down to: when you run
composer updatesomething is updated.Comment #67
gábor hojtsyDo we believe there could be side effects or conflicts? What to do then? Why would they happen?
Comment #68
greg.1.anderson commentedThis change will result in fewer conflicts.
Composer/installers does not run at page-request time, so any effects are limited to Composer operations.
Changes to composer/installers mostly comprise of updates to project types not used by Drupal, so most updates have no effect at all. Those that do affect Drupal, e.g. Composer 2 support, added in composer/installers 1.9.0, are unlikely to cause side effects. Drupal for the most parts re-declares the installer paths we use, but if composer/installers changed a default path, and some project was using said path, then that site would find modules of a certain type installed in a new location. However, composer/installer rarely changes paths once they are defined. Not sure if they consider doing so a "breaking" / major-version-update change.
Comment #69
xjmSaving issue credits.
Comment #71
alexpottVery glad to see this issue fixed.
I tested this by doing
-> went to 8.9.0 as expected
And then
-> went to 8.8.7 as expected
And the root composer.json has
as expected.