Problem/Motivation
This module will eventually be added to Drupal 10 core
Right now the core merge request is against 9.5.x, #3253158: Add Alpha level Experimental Update Manager module. We should determine if the module is Drupal 10 compatible and mark it as such if it is.
I ran Upgrade Status and it say we just need to change info.yml an composer.json file but we should test that out
Steps to reproduce
Proposed resolution
Remaining tasks
- ✅ Create a merge request here that updates all the need info.yml files and the composer.json to be compatible with Drupal 10
- In that branch update
scripts/setup_local_dev.shto check out d10 - Try running tests against Drupal 10
- Try manually updating a site from Drupal 10 pre-release to another
User interface changes
API changes
Data model changes
| Comment | File | Size | Author |
|---|---|---|---|
| #29 | Screen Shot 2022-11-09 at 5.54.25 PM.png | 749.69 KB | wim leers |
Issue fork automatic_updates-3314137
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:
Comments
Comment #2
tedbowComment #5
phenaproximaComment #6
traviscarden commentedComment #7
tim.plunketthttps://www.drupal.org/pift-ci-job/2504642
Comment #8
tedbowComment #9
wim leersThe fact that there is a merge request to make this work on Drupal 10 says that the answer to the current title is already known: "no" 🤓
I think the scope of this issue is to make it compatible with Drupal 10? If so, retitling.
In fact, Automatic Updates will never land as experimental in
9.x, so this is de facto a blocker to get it into core at all. I think the scope of this issue is to also update the automated testing config at https://www.drupal.org/node/2997874/qa to default to Drupal 10?Comment #10
wim leersComment #11
wim leersOne less failure if all went well 🤞 (My very first code contribution to AU 🤓)
Comment #12
wim leersOne less failure! I'll continue this on Thursday (I'm off Tue + Wed).
Comment #13
wim leersWTF.
phpcson 9.5:versus on 10.0:
🤯🤣💩
Comment #14
wim leersOn DrupalCI:
… but none of those deprecated calls ever occur from within the
automatic_updatescodebase! 🤯Running locally with PHP 7.4 I cannot reproduce that, but with PHP 8.1 I do:
but even after running
composer run-script drupal-phpunit-upgrade-checkto upgrade PHPUnit, I get:So … something is definitely wrong here. I can reproduce the same problem, but now with the PHPStorm-generated
ide-phpunit.php🤔But this reproduces it too:
or
Investigating further … 🕵️♀️
Comment #15
wim leersRoot cause for the
strpos()andversion_compare(): https://github.com/composer/semver/issues/134 hardened against a PHP 8.1 deprecation in an incomplete/poor way — we need to fix our calls to the underlying API too. Done.And … the same root cause for the
htmlspecialchars()deprecation, which gets triggered by… because
@target_versionisNULLfor a formattable string.This really just revealed a weakness/bug in
\Drupal\automatic_updates\Validator\VersionPolicy\ForbidDowngrade::validate(), caused by\Drupal\automatic_updates\Validator\VersionPolicyValidator::getTargetVersion().Comment #16
wim leersThis is … nonsensical.
What isn't:
\Drupal\Core\Update\UpdateRegistry::getPendingUpdateFunctions()returns[]on D10, but a list of 14 pending update functions on 9.5.0d672b0fixed that.Comment #17
wim leersOn
UpdatePathTest, I'm seeing an integrity violation constraint being triggered byautomatic_updates_update_9001()'s firstrename()call, which is why the second key does not get renamed. How is this possible?!Well … if you put a breakpoint in
automatic_updates_cron()you can see that it gets invoked while running updates, heck, even just on visitingupdate.phpfor the very first time. 🙈I did not dig deeper to figure out why this only happens on Drupal 10 (it could be as simple as "PHP 8.1"), but it's a fact that this is an upstream bug. Created #3318964: automated_cron should not run cron when visiting update.php.
Comment #18
tedbow@Wim Leers to get around this for now could we check in our implementation of
hook_cronwhether we our on update.php and return early?. I don't think we should do any of our task if on update.phpComment #19
wim leers… even though I can see that …
automatic_updates_update_9001()did in fact rename both keys, thestatus_check_last_runkey-value pair is obviously missing on D10 🤯Comment #20
wim leersFigured it out.
This is caused by
automatic_updates_post_update_create_status_check_mail_config()saving config, which triggers\Drupal\automatic_updates\EventSubscriber\ConfigSubscriber::onConfigSave(), which in turn triggers\Drupal\automatic_updates\Validation\StatusChecker::clearStoredResults().I bet this is passing in 9.5 then only through sheer execution order coincidence: this post-update hook erases
status_check_last_run, but then the cron execution recreates it, which allows the assertions in\Drupal\Tests\automatic_updates\Functional\UpdatePathTest::testUpdatePath()to pass. On D10, the execution order is different, which causes it to fail.This is happening since #3316611: If unattended updates are enabled, send an email when status checks start failing. That issue is technically not testing the update path correctly, because it requires all update functions to be tested by
\Drupal\Tests\automatic_updates\Functional\UpdatePathTest::testUpdatePath().This is wrong, we need one update path test per update.EDIT: no, only Drupal core requires that.Comment #21
wim leersFull sequence of events on 10.1:
update.php,automatic_updates_cron()gets called, which calls\Drupal\automatic_updates\Validation\StatusChecker::run(), which setsstatus_check_last_runandstatus_check_timestamp— ⚠️ WRONG! → this de facto means there is no point to run this update path at all… we could instead just have it be wiped and it'd auto-heal after cron…automatic_updates_update_9001()gets executed. Renaming works fine, but now everything is in there TWICE already: the original and the renamed. Which means that therename()calls both trigger an integrity constraint violation which gets eaten by\Drupal\Core\KeyValueStore\KeyValueStoreInterface::rename()— ⚠️ now the same data is in there twice, and our update path silently didn't do anything at all, leaving the system in an inconsistent stateautomatic_updates_post_update_create_status_check_mail_config()gets executed, triggeringConfigSubscriber::onConfigSave(), which in turn triggersStatusChecker::clearStoredResults(), meaningstatus_check_last_runis removed, hence causing the assertions to fail — ⚠️ this means that it's logically impossible for BOTH of the update path tests' results to be true! 🤯😱🤔fails
Full sequence of events on 9.5:
system_post_update_enable_provider_database_driver()gets invoked, which does\Drupal::service('module_installer')->install(array_keys($modules_to_install));, which triggersautomatic_updates_modules_installed(), which in turn triggersStatusChecker::run(), which overwritesstatus_check_last_runandstatus_check_timestampagainpasses!
IOW: this update path test was not at all testing the update path…
Comment #22
wim leersI've got this green locally, but d.o's GitLab instance is currently experiencing severe issues, so I can't push them… 😅
Comment #23
phenaproximaWell, #21 is pretty mind-boggling. Had I tried to debug this, without a doubt it would have melted by brain. I can only humbly tip my hat in your direction, @Wim Leers, and quietly aspire to someday reach even 1% of your skill level.
This is technically true; the ephemeral nature of status check caching means the update path was not strictly necessary. However, had we not done that, until status checks were run again people would have seen messages like "You haven't run readiness checks recently", which I figured would be confusing. Not to mention the fact that, since status checks could potentially be expensive operations, we don't want to run them more often than we need to. In retrospect, though, we could have just cleared the stored results and flagged a message saying "Please run readiness checks again", with a link.
I guess the lesson here is that, in most situations, it's not worth trying to seamlessly update what is essentially a cache.
What's also so frustrating about this bug is that it works, albeit by coincidence, in Drupal 9. Which, until this issue lands, is all we test with. So there was no real way for us to see this Lovecraftian betentacled horror show of a bug coming down the road.
When this is pushed, I will review it and likely RTBC.
Comment #24
wim leersComment #25
wim leersYay! That proves it:
Comment #26
wim leersGreen! On
10.0.0-beta2… 😬 It was green on10.0.xHEAD last Friday. I bet that it's the Symfony 6.2 beta update … 😔Comment #27
wim leersCommit history:
It's green on the bottom commit (the one before #3284422: [META] Symfony 6.2 compatibility) and red after.
😬
Comment #28
wim leershttps://github.com/symfony/symfony/commit/739789f384cca3b06a3b5f35793e11... introduced
in
FileLoader. Thatregister($class)call passes inPhpTuf\ComposerStager\Domain\Exception, which then triggers… which understandably triggers this exception.
Comment #29
wim leersTurns out Drupal's validation in
ContainerBuilderis just … not working and that's why #3021803: Document and add tests for service autowiring didn't hit it …Comment #30
wim leersAsked @longwave and @alexpott at https://drupal.slack.com/archives/C1BMUQ9U6/p1668013246481129, also because of #3049525: Enable service autowiring by adding interface aliases to core service definitions, which is introducing boatloads of non-lowercase service IDs AFAICT!
Comment #31
wim leers@longwave in Drupal Slack:
Comment #32
wim leersClosed #2503567 at #2503567-51: Only allow lowercase service and parameter names [part 2].
Pushed #3049525 forward at #3049525-53: Enable service autowiring by adding interface aliases to core service definitions.
But … we'll still need a work-around until the time that #3049525 lands. On it.
Comment #33
wim leersWow, #3320315: Allow uppercase service IDs/names — necessary for autowiring support landed, so I can revert all the shenanigans of the last 4 commits!
Comment #34
phenaproximaCore brings in
symfony/polyfill-php80, so it's safe for us to use the new str_* functions in PHP 8.Comment #35
phenaproximaApart from the one nit about using
??=, I think this is ready. I know I wrote a bunch of it, but we gotta get it done. I assume @tedbow will give it a once-over before committing it. Besides, @Wim Leers did the incredibly impressive heavy lifting in debugging this one, particularly the craziness around the update path test. I can't thank you enough for tracing through that thicket of madness.I discovered while trying to test this that setup_local_dev.sh has a flaw which prevents the set-up from completing on D10: it configures Composer's PHP platform as 7.4.0, but D10 requires PHP 8.1.0. After discussion with @tedbow, I don't think it needs to block commit. Instead, I leave it to @TravisCarden to sort that out in another issue, which should probably be opened before this is committed since it blocks local development on Drupal 10. (IMHO, the best solution would be to run
composer config platform.phpagainst the core checkout, to find out what it wants to use as the PHP version, and then propagate that into the local project.) Additionally, Automatic Updates can remove the wholeconfigsection from composer.json, because it serves no purpose.Comment #36
wim leersYep, that's literally the first thing I ran into after
git cloneing AU 😅Comment #37
traviscarden commentedI have argued in other contexts that setting
platform.phpin the first place is usually a bad idea, and this problem illustrates why. The only reasonsetup_local_dev.shsets it to 7.4 is thatdrupal/drupalhas it at 7.3.We should probably change the constraint to
>=7.4or remove it altogether. I've created #3320486: Make setup/install scripts remove "platform.php" Composer config override altogether to remove it. It's ready for review.If we're explicitly using an indirect dependency, it's really a direct dependency. And we should
composer require symfony/polyfill-php80it in our own module. Of course, I assume it will go away when we commit the module to core, but apparently its absence causes confusion (exhibit one: this thread), and it's technically "correct".Comment #38
phenaproximaEDIT: Redacted
Comment #39
tedbowAssigning to @Wim Leers mostly for clarification on few points from MR
Comment #40
wim leersComment #41
wim leersComment #42
tedbowHad to merge in 8.x-2.x locally. Will merge if green.
Thanks @Wim Leers & @phenaproxima!
Comment #44
tedbow🎉I will change the default test to d10 on issues