Problem/Motivation
There are some cases where we would like people to be able to use Package Manager in a more permissive way than it currently allows. For the purposes of this issue, consider the use case of a simple local development setup (i.e., php -S) of Drupal CMS, being used by a marketer who is not technical and cannot be expected to ever look at a command line.
Package Manager requires Composer and rsync. Currently, those must be detectable in the PATH environment variable, or anything built on Package Manager will crap out with a nasty error. You can configure the paths to these executables in config, but you have to know how to do that (spoiler: it involves the command line, so we've already lost our theoretical non-technical user). That's being improved over in #3463662: When it is installed, Package Manager should try to detect the paths of Composer and rsync.
What if you don't have rsync, though? What if you don't even know what that is? You are completely prevented from installing modules into your site in a local environment, in which you are the only user, with Project Browser. You are, effectively, shut out of the Drupal ecosystem.
This is a mission-critical problem for Drupal CMS.
Proposed resolution
Let me begin by saying that Package Manager's default behavior will continue to be as it is now: sandbox everything.
But we will introduce two things here:
- A new setting,
package_manager_allow_direct_write. FALSE by default. This needs to be true for Package Manager to allow any SandboxManagerBase subclass to operate on the live site directly. - A new attribute,
#[Drupal\package_manager\Attribute\AllowDirectWrite](no arguments), which a SandboxManagerBase implementer can add to their SandboxManagerBase subclass to signal Package Manager that, if the global setting is enabled, this sandbox manager is allowed to operate directly on the live code base.
This means that Project Browser's SandboxManagerBase subclass could declare that it is willing to operate on the un-sandboxed code base -- which makes sense for Project Browser, given its purpose and design -- but Automatic Updates could continue to be stringent, which makes more sense for it given its purposes and design.
API changes
A new setting and attribute. See above, or change record: https://www.drupal.org/node/3506770
A few other things will also happen:
- If a status check is done on a sandbox manager that is operating in direct-write mode, a warning will be flagged stating that direct-write is not recommended in production environments, since it's inherently riskier than sandboxing.
- The PreApply and PostApply events won't be dispatched in direct-write mode, since they don't really make sense.
- In direct-write mode, the site will be put into maintenance mode during PreRequire, and reverted to its previous maintenance mode status in PostRequire. This is for safety's sake - you probably want to reduce the amount of "stuff" that the code base is doing while it's being physically changed.
Data model changes
None.
Release notes snippet
TBD, but not sure if needed since Package Manager is experimental.
Issue fork drupal-3503699
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:
- 3503699-make-package-manager
changes, plain diff MR !11076
Comments
Comment #2
phenaproximaComment #3
phenaproximaComment #4
phenaproximaComment #5
catchComment #6
catchSo if I'm reading correctly this would be adding a direct-write mode when a flag is set, but only for project browser. The idea being you can composer require new dependencies and the simple act of doing so won't blow up your site, whereas update might.
Overall that sounds great I think.
Is there any situation where indirect dependencies might be updated as part of a composer require or does composer always just complain in that case?
Comment #7
phenaproximaYes, Composer might update indirect dependencies as part of a require, depending on what the dependency solver spits out. So there is an element of risk to this, but I think there are mitigating factors:
--minimal-changesoption, which tries to minimize changes to indirect dependencies; Package Manager could be changed to ensure that Project Browser can pass this option, or maybe Package Manager could just always pass the option regardless).About the
--minimal-changesoption, from https://getcomposer.org/doc/03-cli.md:Comment #9
catchAt least two fairly high usage contrib modules, maintained by different people, incremented hook_update_last_removed() in patch or minor releases in the past month or so, preventing updates from e.g. 1.1.1 to 1.1.3 or 1.1.1 to 1.2.0.).
--mininal sounds great to avoid this though.
Would it be possible to run --dry-run and detect if it includes updates? Would be annoyingly slow though, but maybe only when this mode is on?
Edit: one example https://www.drupal.org/project/honeypot/issues/3468450
Comment #10
phenaproximaUnfortunately,
composer updatedoesn't give us any machine-readable output, with or without the--dry-runflag. The only way to detect what will happen, is to use the sandbox.So as I said, there's some risk involved here, which is why direct-write wouldn't be enabled by default. I think it also makes sense for Package Manager to always pass the
--minimal-changesflag to Composer to minimize the possibility of breaking changes being introduced (regardless of whether direct-write is enabled), but we should probably do that in a separate, blocking issue.The reason Package Manager doesn't pass
--minimal-changesright now is because I don't think it even existed when we were writing Package Manager. Indeed, it was only introduced in Composer 2.7.0 (see https://getcomposer.org/changelog/2.7.0).Comment #11
phenaproximaComment #12
gábor hojtsy@catch wrote this:
I agree. There is already the
package_manager_change_logentries that document what package manager requested and then separately logged what actually happened in composer, which helps providing a paper trail of what it did.I also think we can trust composer itself probably better to not leave your
composer.json/lockand vendor files in a half applied state than our own code that copies that over from the composer staging, so I don't see added risk there.Comment #13
phenaproximaComment #14
catchThe MR looks good to me I think,
--minimal-changesshould reduce the amount of things that can go wrong.Comment #15
phenaproximaComment #16
phenaproximaComment #17
phenaproximaDraft change record written: https://www.drupal.org/node/3506770
Comment #18
smustgrave commentedAppears to have a number of open threads, if those could be answered please.
Thanks all
Comment #19
phenaproxima@tedbow and I discussed this at DrupalCon. We agreed that we should just not dispatch PreApplyEvent and PostApplyEvent at all, since they don't make any sense if you have effectively already "applied" changes to the site by requiring some dependencies.
So we'll just document that, and test that those events are never dispatched in direct-write. But generally this is ready for review.
Comment #20
tedbowSome suggestions but think it looks pretty good!
Comment #21
catchI'm not sure how to test this without committing it, but how sure are you that this will successfully work with core updates where the code of package_manager itself can change?
Comment #22
phenaproxima@tedbow and I had a nice, long Zoom chat about #21 and the implications of it.
It is in the very nature of Package Manager to change the running code base. Right now, that only happens during the apply phase, and during that phase, Automatic Updates kicks the site into maintenance mode and tries to finish the current request as quickly as possible. This is precisely to mitigate the possibility of weird things happening while the running code base is changing itself.
The crux of the problem is that, conceptually, direct-write moves that risk from the apply phase, to the require phase. In direct-write mode, the apply phase effectively doesn't exist.
Ted and I felt that the right course of action, then, is for Package Manager to kick the site into maintenance mode when direct-write is happening. In other words -- when something is required in direct-write mode, go into maintenance mode, and emerge from it when the require has completed. The idea is to reduce the risk of weird things happening if the running code base is changed while, say, 1000 people are using the site. It can't completely eliminate the risk, but it would greatly reduce it.
Additionally, direct-write mode should result in a warning being flagged during status checks. That warning should state that direct-write mode can be risky because it changes the running code base. That is always true, and site owners should be aware of it. Direct-write mode is meant to make local development easier (i.e., a site with only one person using it), and it's inherently riskier in a production environment. Flagging a warning seems like a completely reasonable thing to do.
We think we could implement that change in a separate issue, after this one (which adds the direct-write API) is committed. That issue would be a stable blocker for Package Manager.
The second tricky thing that came up is the validators which are trying to compare the live site with the sandbox, by subscribing to PreApplyEvent. Most of these validators are just useless in direct-write mode, and their validation -- which may be valuable -- is just skipped. (For example, SupportedReleaseValidator checks to ensure you didn't just install some insecure or unsupported project into the sandbox.) How can we ensure that this validation is not lost? Even if a problematic change has already happened to your live site, you as the site owner should still be aware of it as a problem that you need to address.
We're still not entirely sure what to do here, but Ted's idea is to shift a lot of that validation to StatusCheckEvent. Since there's no sandbox to compare the live site against, we'd have to record the state of the live site before anything else happened -- i.e., on or before PreCreateEvent is fired. Subscribers to StatusCheckEvent could then compare that recorded state to the current state of the site, to determine if anything untoward had happened during the require phase, regardless of whether it happened in a sandbox, or in the live site.
We could probably also do that in a stable-blocking follow-up issue.
Comment #23
phenaproximaNeeds some additional test coverage for the maintenance mode and warning stuff I just added (based on what Ted and I talked about in #22), and I think this should definitely have manual testing before being committed.
Comment #24
phenaproximaOK - I've written tests for what was discussed in #22.
Comment #25
phenaproximaComment #26
phenaproximaComment #27
phenaproximaComment #28
catchA few comments on the MR, I don't think there is anything too major in there.
Comment #29
phenaproximaTried to rephrase some comments for added clarity.
Comment #30
tedbowJust went over this again with @phenaproxima, looks good!
Comment #31
phenaproximaOpened #3525345: Move some Package Manager validation into the pre-require and status check event listeners to move some validation around as needed to support direct-write mode.
Comment #32
larowlanComment #33
phenaproximaI feel pretty confident about this. Let's get the API in, then I'll test it against Drupal CMS and we can fix any glaring bugs. The addition of the API is harmless by itself, since it doesn't do anything unless sandbox managers actually opt into the capability. So the real test will come after contrib (in particular, Project Browser and Automatic Updates) adopts this feature.
Comment #34
quietone commentedI read the MR, CR and updated credit. I re-worked the change record so that the improvement is the first thing mentioned. And I changed wording to be plain English.
Comment #35
quietone commentedComment #36
phenaproximaComment #37
quietone commentedYes, that make sense. The wee change is an improvement.
Comment #38
catchDiscussed #3525345: Move some Package Manager validation into the pre-require and status check event listeners a bit with @phenaproxima in slack.
I had a quick look for validators that would be affected by this change (e.g. not run at all if this is used). The three I could find were:
EnabledExtensionsValidatorDuplicateInfoFileValidator.phpSandboxDatabaseUpdatesValidatorEnabledExtensionsValidator seems like the main problem.
https://www.drupal.org/project/pathauto/releases/8.x-1.11 removes a ctools dependency, and will remove it as a composer dependency in a major release. This means that when sites eventually update to pathauto 2.x, they'll need to either composer require drupal/ctools or uninstall it (unless they happen to have another module installed that depends on it).
Automatic updates can't do any of that itself, so would need to rely on this validator to handle it (and then the site owner would be alerted they need to either install ctool explicitly or uninstall it before trying to update pathauto again, after which it would work), but that won't run in direct write mode. @phenaproxima pointed out that composer update --dry-run doesn't support machine readable output, so this might need a composer update --dry-run:json custom composer plugin in order to replace.
However, neither that, nor
SandboxDatabaseUpdatesValidatorare relevant to project browser. After writing that I realised it might be relevant, because if you install a project that depends on pathauto 2.x, and you have pathauto 1.x installed, then composer might update you to pathauto 2.x even though the command in composer require which could also result in ctools removal.I think we need a follow-up to discuss this more, and maybe postpone implementing the API in project browser and especially automatic updates on that follow-up.
Comment #41
catchBumped #3525345: Move some Package Manager validation into the pre-require and status check event listeners to critical so it doesn't get forgotten, also added to package manager stable blockers. I think the scope is too big to do in a single issue here.
Committed/pushed to 11.x and cherry-picked to 11.2.x, thanks!
Comment #43
phenaproximaThanks @catch!
For whatever it's worth, I am in complete agreement that implementing this API needs to be done very carefully and thoughtfully. Project Browser should have a dedicated issue to add support for this, to fully think through the implications.
Automatic Updates implements at least three different sandbox managers, and each of them should get their own issue to discuss whether to support direct-write. The potential benefits are significant, but so are the risks.
Comment #44
catchThe build test change here made it the slowest core test at over 3m20s, opened #3526397: Split PackageInstallTest into two.