This is a child issue of #2982674: [meta] Composer Initiative Phase 1: Add composer build support to core

Problem/Motivation

Given a normalization to Composer-based site building, we need a tool to convert non-Composer-based sites into Composer-based ones.

We're targeting 'tarball' style installs, which have downloaded Drupal from the download page of drupal.org, and have then installed in place.

We want a core-native tool which can be easily used to reliably make a composer.json file, ideally without much user intervention.

Proposed resolution

Since Drupal tarball downloads will be Composer-ready out of the box, and updating a Drupal site via drush pm-update essentially involves swapping out (parts of) your current code with the latest tarball download, so for folks who do not have any contrib modules, converting to Composer-managed will be a simple matter of running "composer update". Folks who do have contrib modules will need their composer.json adjusted post-update.

There are three stages:

  1. Validate: The site must be using drupal/core-composer-scaffold, and must have contrib modules on disk that are not listed in the composer.json file. (If either of these things is not true, then the tool does nothing.)
  2. Prompt: In a Composer pre-update script (or perhaps post-update), conspicuously warn the user that: a) they cannot go back and use `drush pm-update` any longer, and b) their existing contrib modules will be added to their composer.json file (if available on Packagist).
  3. Convert: If the user confirms, run a version of composerize-drupal ported into drupal/drupal project. If they decline, print a warning that `composer update` is not updating their contrib modules.

composerize-drupal is already a Composer plugin, so it probably will not need a lot of work to convert it to this workflow. If we add it to the composer/Plugins directory, then it will be subtree split into a separate component, and can be added to drupal/legacy-project, where it will also become part of the tarball downloads. (Alternately, we could consider adding it during the tarball bundling process, since this tool is not necessary for sites created via composer create-project drupal/legacy-project.)

Remaining tasks

  • [ ] Port grasmash/composerize-drupal to composer/Plugins directory.
  • [ ] Remove functionality of composerize-drupal' that is no longer necessary (replacement of root composer.json with a template version, relocation of files to "web" directory, etc., where such things have already been provided by the Composer-ready download).
  • [ ] Warn the user at `composer update` time that they've started out on a one-way trip.
  • [ ] List contrib modules converted, add them to composer.json, remove them from repository in their old location,
  • [ ] Optional. Remove the conversion plugin after it has converted the contrib modules.

Follow-on tasks

There are other things that folks might wish to do that could be handled in follow-on work.

  • Convert from older Composer-managed template projects (e.g. drupal-composer/drupal-project sites still using drupal-composer/drupal-scaffold).
  • Convert from drupal/legacy-project layout to drupal/recommended-project layout.
  • Add a rational .gitignore file, perhaps assuming vendor is committed, or perhaps assuming it is not. Remove any ignored files that are currently committed to the repository.
  • Convert a non-Composer-managed site to a Composer-managed site without updating the version of Drupal Core or contrib modules used (perhaps even for pre-8.8.0 versions, although most likely only Composer-ready sites will be supported at first).

User interface changes

None.

API changes

None.

Data model changes

None.

Release notes snippet

Comments

Mile23 created an issue. See original summary.

tormi’s picture

My current workflow is `drush generate-makefile example.make` > `drush make-convert example.make --format=composer > composer.json` > copy component's from generated composer.json to list into drupal-project's composer.json + adjustments.

imclean’s picture

mile23’s picture

Issue summary: View changes
Status: Active » Postponed

Thanks for the links.

This issue is postponed on #2982680: Add composer-ready project templates to Drupal core, which is where we'll make the target template this tool will convert to.

aaronmchale’s picture

Agree with @Mike23 in #4

grasmash’s picture

Of course, I'm partial to https://github.com/grasmash/composerize-drupal. I believe that it works for most use cases. It has 94% test coverage and I consider it stable.

Mixologic’s picture

Component: other » composer
Issue tags: -Composer +Composer initiative
greg.1.anderson’s picture

Issue summary: View changes

Added a proposed resolution and a list of remaining tasks to the issue summary.

As follow-on work, we could consider dropping a Drush 8 policy file into the site to protect it from future pm-update shenanigans. If the user has not converted their contrib modules, they could continue to update them with Drush. In this scenario, after the user has run `composer update` at least once, the policy file could protect the site by forcing the `--no-core` option to be set. Once the user has converted their contrib modules, the policy file could simply prevent `pm-update` and related commands from running on the site at all.

The policy file could either be added by the conversion tool, or we could make it a persistent part of Drupal, and have it decide what action to take at runtime by inspecting the site and seeing if it has been converted.

greg.1.anderson’s picture

Issue summary: View changes

Note that there are a few features of Composerize Drupal that will no longer be necessary when working with Comoser-ready Drupal sites.

Mixologic’s picture

Ah. I had forgotten about the situation where somebody starts with the composer ready tarball, and then installs modules manually, and then needs to become a composer based site.

We'll still need a composerize mechanism to 'convert' those sites for people.

naheemsays’s picture

Will the plugin be able to "fix" the situation where a composer based drupal has had a module manually updated?

If that is the case, the plugin will remain useful after initial use.

greg.1.anderson’s picture

I am wary as to the safety/reliability of fixing a manually updated module. Are we sure the module was manually updated, or was it patched with cweagans/composer-patches?

I think the "repair" operation is to have the user manually remove the tainted modules, and then run composer install. I don't think we should do this automatically.

mile23’s picture

Status: Postponed » Active
grasmash’s picture

Currently, https://github.com/grasmash/composerize-drupal will scan for contrib modules and populate the composer.json require array. It will also scan for patches and populate the patches array.

To answer your question:

Will the plugin be able to "fix" the situation where a composer based drupal has had a module manually updated?

We could easily add an additional command to https://github.com/grasmash/composerize-drupal that *only* scans for contrib modules and updates the require array. The command could be manually run even after an application has been "composerized."

naheemsays’s picture

That would be a useful option because I expect us non experts (or sites with multiple levels of contributors) to make such mistakes, so a way to repair the damage would be useful.

mile23’s picture

Just want to draw attention to this use case: #3082958-36: Add gitignore(s) to Composer-ready project templates

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

mile23’s picture

Version: 8.9.x-dev » 8.8.x-dev

Based on Slack chat, some specs:

We def want to warn people that they're using drupal/drupal.

We also want to warn people who are using drupal-composer/drupal-scaffold, webflo/drupal-core-strict, webflo/drupal-core-require-dev. They should switch over to drupal/core-composer-scaffold and drupal/core-recommended.

aaronmchale’s picture

We also want to warn people who are using drupal-composer/drupal-scaffold, webflo/drupal-core-strict, webflo/drupal-core-require-dev. They should switch over to drupal/core-composer-scaffold and drupal/core-recommended.

That sounds like it might covers something I was wondering, because many people (myself included) typically setup sites using drupal-composer/drupal-project, so it sounds like this tool would make it simple to convert a site which started with that to using drupal/core-recommended, or at the very least provide documentation to accomplish that?

greg.1.anderson’s picture

Yes, switching to the new Composer plugins should be pretty easy. See https://g1a.io/cic, which contains an overview.

We could warn users on old components by submitting a PR to drupal-composer/drupal-scaffold, and just start printing a big warning message every time it runs.

aaronmchale’s picture

Yes, switching to the new Composer plugins should be pretty easy. See https://g1a.io/cic, which contains an overview.

That's a handy presentation and overview, thanks.

We could warn users on old components by submitting a PR to drupal-composer/drupal-scaffold, and just start printing a big warning message every time it runs.

Sounds like a good idea.

mile23’s picture

Version: 8.8.x-dev » 8.9.x-dev
Status: Active » Needs review
StatusFileSize
new17.48 KB

This is *not* a tool to convert your composer.json file.

This is a status tool which ends up saying things like this:

$ composer drupal-status

Drupal Composer Status
======================

Things to do:
 * Rename the project so it is not drupal/drupal.
 * Change drupal/core to drupal/core-recommended.
 * Add drupal/core-dev to the require-dev section.
 * Determine how to require these extensions: unmanaged_module
 * Project specifies patches in extra (patches, patches-file, enable-patching), but does not require the cweagans/composer-patches plugin.

The reason this patch is being offered is so that you, whoever you are, can review the code and develop an opinion about which things it checks for and what it tells you to do about them.

You could try modifying the composer.json file in your root to various edge cases you think I didn't consider, and then running the tool, and then having an opinion about the result.

Once you have that opinion, your review can say what should be added, or what's wrong here, and then we can update the list.

Then, eventually, we can transform the list of to-dos into things the machine does.

Yes, I know this inadvertently promotes symfony/finder out of the dev requirements. This is a prototype. :-)

Important other specs to contemplate:

  • You should be able to say composer global require drupal/core-composerizer-tool-whatever-we-end-up-naming-it and then say composer drupal-composerize --working-dir /arbitrary/directory
  • You should also be able to require it locally and run it there. This means we have to target lower PHP versions, depending on the lowest allowed core version.
  • We have to figure out the lower boundary of Drupal core versions we can convert. Currently we only have a drupal/core-recommended for 8.7+. We might be able to generate this for earlier versions, but that should be a decision rather than a demand.
grasmash’s picture

This seems so similar to composer validate that I wonder if we can extend that command with Drupal specific inspections.

mile23’s picture

For a status type readout yah, it's very similar to validate.

Asking for edge cases, please. :-)

webchick’s picture

I am probably a subset of the target audience for this feature since I've been using tarballs since the 90s :P ... for me, I would need more information on each of these points, for it to be really useful to me and for me not to feel blocked/stuck.

$ composer drupal-status

Drupal Composer Status
======================

Things to do:
 * Rename the project so it is not drupal/drupal.

# give me a command I can copy/paste, please. And please give me a recommendation so I don't need to get stuck in analysis paralysis trying to figure out what the new name should be.

 * Change drupal/core to drupal/core-recommended.

# Where? How?

 * Add drupal/core-dev to the require-dev section.

# Can you point me to docs on this?

 * Determine how to require these extensions: unmanaged_module

# I would love to but... how do I determine how? :)

 * Project specifies patches in extra (patches, patches-file, enable-patching), but does not require the cweagans/composer-patches plugin.

# Uh. What..?! :)

greg.1.anderson’s picture

I think that #22 is useful as a step on the road toward implementing the use-case described in the issue summary. As such, I think that its target audience is developers working on this issue.

I think that the best way to address the questions in #25 is to continue the implementation to automate the conversion process.

webchick’s picture

Ah, ok, fair enough. :) Carry on, then!

mile23’s picture

StatusFileSize
new51.82 KB

Here's another patch.

This one *does* add the tool to core. :-)

It is highly development-centric, and has opinions about how to dev this plugin. IOW: Do not RTBC. :-) I'm not even wasting the resources to run DrupalCI on it.

If you want to see what it does, then you can use it globally from the mirror I made: https://github.com/paul-m/core-composer-converter

The way you do that is as follows:

$ composer global require mile23/core-composer-converter @dev

Now you can navigate to a Drupal installation somewhere and perform the conversion. As always: Make backups and/or use git.

$ cd drupal
$ git checkout 8.9.x
$ composer drupal-legacy-convert
The following actions will be performed:

 * Make a backup of your composer.json file.
 * Replace composer.json with one named drupal/converted-project.
 * Copy config for: Repositories, patches, config for drupal/core-* plugins.
 * Add requires for extensions on the file system.
 * Configure drupal/core-composer-scaffold based on drupal-composer/drupal-scaffold config.

Continue? y
 - Storing backup file...
 - Creating new composer.json file...
 - Checking repositories...
 - Checking extra configuration...
 - Moving existing Drupal extensions to new composer.json file...
 - Determining whether project needs patches...
 - Scanning the filesystem for extensions not in the composer.json file...

Finished!

Note that it will discover extensions you add.

$ drush dl examples
[...]
$ composer drupal-legacy-convert --no-interaction
 - Storing backup file...
 - Creating new composer.json file...
 - Checking repositories...
 - Checking extra configuration...
 - Moving existing Drupal extensions to new composer.json file...
 - Determining whether project needs patches...
 - Scanning the filesystem for extensions not in the composer.json file...
Using version ^1.0 for drupal/node_type_example
Using version ^1.0 for drupal/tablesort_example
Using version ^1.0 for drupal/plugin_type_example
Using version ^1.0 for drupal/field_permission_example
Using version ^1.0 for drupal/cache_example
Using version ^1.0 for drupal/events_example
Using version ^1.0 for drupal/cron_example
Using version ^1.0 for drupal/js_example
Using version ^1.0 for drupal/block_example
Using version ^1.0 for drupal/email_example
Using version ^1.0 for drupal/config_entity_example
Using version ^1.0 for drupal/file_example
Using version ^1.0 for drupal/queue_example
Using version ^1.0 for drupal/content_entity_example
Using version ^1.0 for drupal/form_api_example
Using version ^1.0 for drupal/examples
Using version ^1.0 for drupal/ajax_example
Using version ^1.0 for drupal/testing_example
Using version ^1.0 for drupal/tabledrag_example
Using version ^1.0 for drupal/phpunit_example
Using version ^1.0 for drupal/pager_example
Using version ^1.0 for drupal/hooks_example
Using version ^1.0 for drupal/dbtng_example
Using version ^1.0 for drupal/page_example
Using version ^1.0 for drupal/menu_example
Using version ^1.0 for drupal/session_example
Using version ^1.0 for drupal/batch_example
Using version ^1.0 for drupal/tour_example
Using version ^1.0 for drupal/render_example
Using version ^1.0 for drupal/field_example
Using version ^1.0 for drupal/stream_wrapper_example
Finished!

Don't want all those modules listed individually? Let's require them by d.o project (so that we'll require drupal/examples instead of all the individual modules):

$ git checkout -- composer.json
$ composer drupal-legacy-convert --no-interaction --prefer-projects
 - Storing backup file...
 - Creating new composer.json file...
 - Checking repositories...
 - Checking extra configuration...
 - Moving existing Drupal extensions to new composer.json file...
 - Determining whether project needs patches...
 - Scanning the filesystem for extensions not in the composer.json file...
Using version ^1.0 for drupal/examples
Finished!

And finally, let's reconcile extensions which don't have a d.o project.

$ mkdir modules/foo
$ touch modules/foo/foo.info.yml
$ composer drupal-legacy-convert --no-interaction --prefer-projects
 - Storing backup file...
 - Creating new composer.json file...
 - Checking repositories...
 - Checking extra configuration...
 - Moving existing Drupal extensions to new composer.json file...
 - Determining whether project needs patches...
 - Scanning the filesystem for extensions not in the composer.json file...
 - Discovered extensions which are not in the original composer.json, and which do not have drupal.org projects. These extensions will need to be added manually if you wish to manage them through Composer:

 * foo

Finished!

Much remains to be done, but please try it out and see if it breaks or doesn't, and whether it irks you in some way.

mile23’s picture

StatusFileSize
new85.81 KB

This is a little more together than #28. It still does things like remove the vendor hardening plugin from drupal/drupal, so that I could set up an automatic build script that relies on ./vendor/bin/composer running.

As in #28, it's also mirrored through packagist, so you can do this:

$ composer global require mile23/core-composer-converter @dev

It will behave mostly the same as in #28, but you'll also see this when you do composer install or composer update:

$ drush dl examples
Project examples (8.x-1.0) downloaded to                             [success]
/Users/paul/projects/drupal//modules/examples.
Project examples contains 31 modules: stream_wrapper_example, field_example, render_example, tour_example, batch_example,
[..]
$ composer install
[..]
This project has extensions on the file system which are not reflected in the composer.json file. Run 'composer drupal:reconcile-extensions --help' to fix this.

And then you can run that command:

$ composer drupal:reconcile-extensions --prefer-projects
The following actions will be performed:
 * Determine if there are any extension on the file system which are not represented in composer.json.
 * Declare these extensions within composer.json so that you can use Composer to manage them.
 * Remove the existing extensions from the file system.

Continue? y
 - Scanning the filesystem for extensions not in the composer.json file...
Using version ^1.0 for drupal/examples
Finished!

Currently it does not actually remove the existing extensions from the file system.

Any code or conceptual review would be helpful.

mile23’s picture

greg.1.anderson’s picture

so that I could set up an automatic build script that relies on ./vendor/bin/composer running.

Why do you have Composer as a dependency of the site in this scenario? This is unusual.

I'll try to give this a spin pretty soon.

Status: Needs review » Needs work

The last submitted patch, 29: 3047468_29.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

mile23’s picture

Re #31: In netbeans you have to have a full path to a script in order to run it when you click on 'run,' and that was the easiest way. I mentioned it because it's unusual and is one reason this is still WIP.

mile23’s picture

Status: Needs work » Needs review
StatusFileSize
new104.92 KB

Updates, refinements, cleanups, etc.

You can test drive with:

composer global require mile23/core-composer-converter @dev

Navigate to your Drupal site with a bunch of non-Composer-managed extensions and do this:

composer drupal:reconcile-extensions --dry-run --prefer-projects

The current issue I'm finding is that my reference site uses the date module. The date module has an .info file instead of an .info.yml file. So therefore, I have to add that as a way to discover extensions.

mile23’s picture

Priority: Normal » Major

Elevating to major based on critical status for #3101968: composer update removes core

mile23’s picture

StatusFileSize
new307 KB
new228.47 KB

The latest updates and stuff. :-)

mile23’s picture

StatusFileSize
new113.46 KB

Bad patch in #36.

mile23’s picture

StatusFileSize
new107.41 KB
new11.44 KB

Reverts composer.lock file to 8.9.x from #37.

Marking everything @internal because Composer plugins should not fall under the Drupal core API rules.

If you try to use a Composer plugin package as a base class for your changes, you'll discover that you automatically inherit their events and behaviors before your subclass can do anything. Basically: Composer plugins must be forked if you want to change them.

Status: Needs review » Needs work

The last submitted patch, 38: 3047468_38.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

mile23’s picture

Status: Needs work » Needs review
StatusFileSize
new105.29 KB
new5.28 KB

Oh yeah, I added a src/ directory for... reasons. And that should go away for consistency with other plugins and stuff. Also some metapackage cleanup and so forth.

Status: Needs review » Needs work

The last submitted patch, 40: 3047468_40.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

dww’s picture

Haven't tested or closely reviewed anything in here. Just flagging that #40 needs work not just for the failing test, but also for CS warnings:

8 coding standards messages
✗ 8 more than branch result

/var/lib/drupalci/workspace/jenkins-drupal_patches-23736/source/composer/Plugin/ComposerConverter/Command/ExtensionReconcileCommand.php ✗ 8 more
line 180	Line indented incorrectly; expected 4 spaces, found 6
181	Line indented incorrectly; expected 4 spaces, found 6
182	Line indented incorrectly; expected 4 spaces, found 6
183	Line indented incorrectly; expected 4 spaces, found 6
184	Line indented incorrectly; expected 4 spaces, found 6
185	Line indented incorrectly; expected 4 spaces, found 6
186	Line indented incorrectly; expected 4 spaces, found 6
187	Line indented incorrectly; expected 4 spaces, found 6
mile23’s picture

Status: Needs work » Needs review
StatusFileSize
new107.73 KB
new9.61 KB

Thanks, @dww.

Fixed failing tests, and a good thing too. Also fixed CS.

Build tests are the best. :-)

dww’s picture

Status: Needs review » Needs work
Issue tags: +Needs followup

Re: #43: sweet, thanks. I see in the interdiff that the "CS" violations were actually commented-out code. Good move removing it entirely. However, I think we want a follow-up issue for the @todo comment, and add a @see pointing to it.

Hope to get a chance to give this a spin and a more careful review in the coming days...

Cheers,
-Derek

hotwebmatter’s picture

+1 for mile23/core-composer-converter, which just helped me to convert a client site from a tarball into a standalone git repo, with all its dependencies managed by Composer and all its tooling encapsulated by Lando.

There was a lot more to that than just running this tool (and the documentation out there offers many different options!) but this tool provided a valuable service.

I just pulled the repo on a different laptop, unzipped a tarball with the contents of web/sites/default/files from production, ran lando start and lando db-import, and practically jumped for joy when the local copy started up.

Now that I’ve beaten the lando and composer level bosses, the next step is figuring out a Continuous Integration strategy that does not require subscription fees for private repos. (I'm leaning toward GitLab-CI, but open to suggestions.)

Note: once you've got your composer.json the way you want it, you can probably remove this from core since it serves no other function.

imclean’s picture

#45,

Now that I’ve beaten the lando and composer level bosses, the next step is figuring out a Continuous Integration strategy that does not require subscription fees for private repos. (I'm leaning toward GitLab-CI, but open to suggestions.)

Bitbucket has Pipelines for CI/CD and has unlimited private repos. We haven't used it as we use a different service but it might work for you.

greg.1.anderson’s picture

Issue summary: View changes

Updating issue summary per discussion with @mile23 on slack. My update didn't go quite as discussed, but I did follow the general goal of rewriting the goals of this issue to do only one thing, and make that one thing the MVP for the Composer conversion tool.

mile23’s picture

StatusFileSize
new130.03 KB

Ok, we're trying to narrow the scope here so the patch can be reviewable. :-)

We have a number of things going on...

It's clear that we want a tool that can look for non-Composer-y extensions and then add them. This is not terribly difficult for extensions on the Drupal Composer facade.

When we do that, we also have to make sure that the Drupal Composer facade is also a repository in composer.json.

And also when we do that, we have to make sure that composer/installers is present, so it can place the extensions in a reasonable default place.

Then we also delete the user's copy of those extensions that we replaced.

Then we can optionally perform the install/update phase.

Keep in mind this is the *limited scope* version of this plugin. :-)

So I present a patch that is still WIP, and which is required in drupal/drupal, just so we can see it in action. This is not the final patch, but you can see it work like this:

  • Apply the patch.
  • composer install
  • drush dl examples
  • composer install
  • Now you see: "Extensions: This project has Drupal extensions on the file system which are not reflected in its composer.json file. Run 'composer drupal:reconcile-extensions --help' to fix this." We'll forgo the help message and just dive in.
  • composer drupal:reconcile-extensions --dry-run --prefer-projects
  • This will present you with a screen of info and ask you if you want to proceed with the dry run. Answer y. Now you see this:
 - Scanning the file system for extensions not in the composer.json file...
Using version ^1.0 for drupal/examples
 - (Dry run) Add these packages: drupal/examples ^1.0
 - (Dry run) Remove these extensions from the file system: examples
Finished!

Up in the IS we see this requirement (which I disagree with, but more on that later):

Validate: The site must be using drupal/core-composer-scaffold,

So let's see that in action. Given that you've performed the above steps, do this:

  • composer remove drupal/core-composer-scaffold
  • composer drupal:reconcile-extensions --dry-run

You should see this message:

  [RuntimeException]                                                           
  Unable to reconcile extensions because: This package does not use the drupa  
  l/core-composer-scaffold package. Type 'composer require drupal/core-compos  
  er-scaffold' and try again.      

Since we also need the Drupal Composer facade, we can do this to remove the facade from our composer.json:

  • composer config --unset repositories.0
  • composer drupal:reconcile-extensions --dry-run

Which results in this message:

  [RuntimeException]                                                           
  Unable to reconcile extensions because: This package does not use the drupa  
  l/core-composer-scaffold package. Type 'composer require drupal/core-compos  
  er-scaffold' and try again. This package does not use the Drupal Composer f  
  acade. Type 'composer config repositories.drupal-facade composer https://pa  
  ckages.drupal.org/8' and try again.           

So, as to the requirement for drupal/core-composer-scaffold in the first place... I think it's good to limit our use-cases as much as we can so we're not chasing our tail here. But it's also true that a pre-8.8.0 tarball won't have that package in it, and there's no benefit in only adding the scaffolding plugin. If our scope is limited only to picking up stray non-Composer extensions, then we shouldn't care if the project is scaffolded or not.

Nevertheless... Here's the patch. It might have extra cruft that I didn't delete after this narrowing of scope, and it has plenty of @todos left. But it's late on Sunday.

mile23’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 48: 3047468_48.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

ressa’s picture

Is it possible to go the other way, from Composer-based Drupal 8.8.2 to tarball?

I ask because I want to migrate a site from Drupal 7 to Drupal 8, which is probably easiest done with Composer, but I want to use Automatic Updates, which only works with non-Composer ("tarball") installations ...

Will it require a tool to convert a Composer-based Drupal 8.8.2 installation (assuming no Composer-requiring modules like Solr are used) to a non-Composer-based one, or can it be done by simply downloading the tarball-based file structure (no "web") copy the contrib modules over and run drush cache:rebuild?

greg.1.anderson’s picture

#51: Out of scope for this issue. I'd say the biggest challenge here is that it's more difficult to go back to tarballs if any of your contrib modules have Composer dependencies (at least not easily). There are a number of solutions for managing Composer dependencies without Composer (Ludwig et. al.), so it might be possible to detect-and-repair this.

My motivation for adding the requirement for drupal/core-composer scaffold to be present was so that we could remove the need to have a template composer.json file, and instead just do minor fixups of the composer.json already present in the site. Another way to restate this is that the site must already be on Drupal 8.8.0+ (or perhaps 8.9.0+) before using this extension. While we could consider converting Drupal 8.7.x and even older, there is the additional complication that we have to give the user instructions on adding this tool, etc.

If we instead require the user to `drush pm-update` (or autoupdate) their site to the latest version of Drupal core first, then we know that drupal/core-composer-scaffold is present, composer/installers is present, etc., and anything that is always going to be present is one less thing that we need to do in the patch.

I also think that it would be easiest to have this operate just by running `composer update` (with a prompt before doing anything), although keeping the message-plus-conversion-command is perhaps safer (as we know, running code in a Composer update hook can be dangerous).

Haven't had a chance to look at the actual patch yet. Thanks for pushing this forward.

ressa’s picture

I agree, and like I stated, going from Composer-based to tarball should ideally only be tried with none-Composer requiring modules.

I just had a go at it, and it went fine. Just copy over modules and settings.php, import the database, run drush cache:rebuild and you're good to go. I don't want to side track this issue, but insert the steps below (using Lando with Drush 8.2.3) for others who are interested in this:

  1. Create Drupal installation with Composer, and export database

    cd ~/dev
    composer create-project drupal/recommended-project comp
    cd comp/
    lando init --source cwd --recipe drupal8 --webroot web --name comp
    composer require drush/drush drupal/admin_toolbar
    drush en admin_toolbar admin_toolbar_tools
    drush uli -l http://comp.lndo.site
    lando db-export
    
  2. Create Drupal installation with Drush ("tarball") and import modules and database

    cd ~/dev
    mkdir tar && cd tar
    lando init --source cwd --recipe drupal8 --webroot public_html --name tar
    drush dl drupal --drupal-project-rename=public_html
    
    # copy database, modules and settings.php, and import
    cp ~/dev/comp/drupal8.2020-02-17-1581958317.sql.gz ~/dev/tar
    cp -R ~/dev/comp/web/modules/* ~/dev/tar/public_html/modules/
    cp ~/dev/comp/web/sites/default/settings.php ~/dev/tar/public_html/sites/default
    lando db-import drupal8.2020-02-17-1581958317.sql.gz
    cd public_html/
    drush cache:rebuild
    drush uli -l http://tar.lndo.site
    
hotwebmatter’s picture

Re #52:

If we instead require the user to drush pm-update (or autoupdate) their site to the latest version of Drupal core first, then we know that drupal/core-composer-scaffold is present, composer/installers is present, etc., and anything that is always going to be present is one less thing that we need to do in the patch.

Based on my recent experience, I agree. I was unable to composerize 8.7.8 tarball until I used drush pm-update to upgarde core to 8.8.x version, no matter what tool I used. (I also tried to just do it manually by starting with a drupal/recommended-project and using composer require to add my dependencies.)

The only way that worked for me was to update the tarball site to the latest version and then use Mile23's core tool.

As ever, your mileage may vary.

greg.1.anderson’s picture

For the record, if anyone did have a strong desire to composerize an 8.7.x version of Drupal, what I would recommend is:

1. `drush pm-updatecode` to get to 8.8.0+ (n.b. do not run updatedb)
2. Composerize the site
3. Downgrade `drupal/core-recommended` to 8.7.8 or desired version (and likewise with any pinned module versions)
4. Run updatedb if needed
5. Undo any pins in composer.json from step 3 if necessary

I can imagine a tool to do that, but I think that's out of scope for this issue.

greg.1.anderson’s picture

So, I am reviewing this more (still not all the way through), and I'm noticing that this patch is doing a lot of complex things with Composer internals in order to get a good list of modules to `require`. All of this code is hard to review, and furthermore introduces technical debt in that it would require more work to support Composer 2 in Drupal if we had all of this in core.

I am wondering if it would be reasonable to just scan he filesystem for `modulename.info.yml`, and if there is a `project:` entry injected by d.o infrastructure, then we may presume that `drupal/modulename` exists. We could also build a version constraint from the `version:` entry in the same yml file.

For extensions that do not have injected project information (e.g. see the lightning distro), we could check to see whether the project identified in the composer.json file was registered in Packagist. (This is probably a bad example, as Lightning already requires Composer. I'm not sure if there is anything for us to find in Packagist that doesn't require Composer already, so this particular optimization might not even be necessary.)

Anything that didn't meet one of these two criteria could simply be left in the filesystem / repo as an item to fix by hand.

Does that sound like a viable strategy? We could simplify this code quite a bit if we went this route, and put off the more rigorous attempts at discovery for either the contrib tool, or a least a later version of the patch

greg.1.anderson’s picture

A few more review notes (still incomplete):

  1. +++ b/composer/Plugin/ComposerConverter/Command/ExtensionReconcileCommand.php
    @@ -0,0 +1,273 @@
    +          $message = 'Adding composer/installers to manage Drupal extensions.';
    

    If we tighten down our validation (require Drupal 8.9.x+), then we can reduce the number of checks here; composer/installers, for example, will always be present.

  2. +++ b/composer/Plugin/ComposerConverter/templates/template.composer.json
    @@ -0,0 +1,52 @@
    +    "name": "drupal/converted-project",
    

    I don't think we need to rely on a template. If we validate that we are converting Drupal 8.9.x+, then the project being converted will already have a composer.json that follows this structure. We should read the site's existing composer.json file, validate that it matches our expectations, and then inject the "require" section that we want.

mile23’s picture

I don't think we need to rely on a template. If we validate that we are converting Drupal 8.9.x+, then...

If we're speccing a conversion tool which declares that it only converts core which doesn't need conversion, then I'm not sure why this is even a thing we're doing.

greg.1.anderson’s picture

My proposal is that we start with a tool that only does conversion of non-Composer contrib modules into Composer-managed contrib modules for sites that are already Composer-ready. That seems like the right increment of work, and gives most folks a `drush pm-update` -> `composer update` upgrade path. More complicated use-cases can be handled in contrib.

hotwebmatter’s picture

Re #55:

I wonder if my current problem is caused by running updatedb too soon. I might have to do the whole thing again.

mile23’s picture

We devoted a composer in core initiative meeting to trying to scope this topic: #3115344: Composer in Core Meeting: 02-19-2020

mile23’s picture

I spent a little time digesting #3115344: Composer in Core Meeting: 02-19-2020

I think the big picture plan here is as follows:

🐮If your site is pre-8.8.0 and you’ve never touched Composer, then you’ll use drush pm-update (or equivalent set of manual instructions) to get to 8.8.0+. Once you’ve downloaded that tarball, you can now Composer all you want.

🍒If you used an earlier community-supplied Composer template, or rolled your own and you want to convert to drupal/recommended-project (or legacy-project), then we have some instructions for you to use. Core can’t promise that your community or bespoke project template will continue to work, but we will have some docs you can follow to get it all working again per core best practices if it breaks.

🐳If you have a Composer-managed site (using a core template or otherwise), you run the risk of having unmanaged contrib extensions in your filesystem. The ‘risk’ here is that you’ll install using Composer and end up without that extension, and then have to figure out what happened. This is the use-case we're covering in this issue.

In that case, we can add a plugin that tells you there are unmanaged extensions. This allows you to reconcile the extensions listed in your composer.json file with extensions in the file system which could be managed by composer.

So now we get to an issue of scope. In addition to discovering this for Drupal core ^8.8, we could also just do this for any Drupal file system.

In this scenario, the user could reconcile extensions for any Drupal site, and get a useful report of what extensions are present. They would composer require drupal/core-extension-reconciler if their version of core does not supply it.

When I was working on some of the earlier patches, I even discovered a bug in the plugin because I accidentally had D7 extensions in my own personal site. Ewps.

So I propose that the extension reconcile tool be able to do the following:

  • On install/update, it can scan your site and tell you whether there are extensions that are not accounted-for within the file system. This is just a thumbs-up/thumbs-down type message that tells you what to do next if you want to.
  • If you do what that message says, then you get a report of extensions. This report tells you all the extensions it can find (including D7). As as site administrator, you now know how many extensions could belong to a D.O project, and how many are your own custom modules, and how many are left over from three years ago when you converted this site to D8, hidden in the darkest corners of your file system.
  • You can now tell the plugin to reconcile these extensions. You can specify that it prefer d.o project names, or not.
  • The plugin will figure out whether you’re on a D7 or D8/9 site, so it can add the appropriate Drupal Composer facade URL to the repositories if needed.
  • It will gather all the extensions and group them into two categories: Those it can reconcile, and ‘exotic’. Exotic means you’ll have to figure out what to do with these extensions. This includes site-specific custom extensions, themes, etc. We could suggest path repos, but also: Why? :-)
  • The extension reconciler could also store a list of extensions it can’t reconcile. This would be a list in the extra section of composer.json. Given this, the user could then leave the plugin installed and then only be notified if another unreconciled extension cropped up somehow.
Mixologic’s picture

Nice. Thanks for capturing all that.

So, I think there was one thing lost in translation, but I think it might be immaterial, and only relevant in the documentation side of things:

🐳If you have a Composer-managed site (using a core template or otherwise), you run the risk of having unmanaged contrib extensions in your filesystem.

The main use case I see for this tool is that loose contract we've had with the community where "You do not have to use composer to manage your site unless you require an extension that requires it."

So, theres a large number of folks who are in the category of 🐮above, they've never used composer, they've used drush to manage their site and they're doing fine that way. And then... they need to install a module that does require composer.

So those users are on a 'composer ready tarball' maybe even 8.9.? when they find they need to "Convert"

In any case, all of the proposed solutions above also solve for this use case, so I dont think this alters any of the proposals. But I *do* think the tool should be geared towards a novice to composer, with super clear 'next steps' etc.

Mixologic’s picture

As a bonus, if this tool exists, we can point to it when/if we ever solve: https://www.drupal.org/project/drupal/issues/2494073 -> tell users hey, you have to use composer now. Sorry. Use this tool to get up to speed.

mile23’s picture

greg.1.anderson’s picture

I am kind of -1 on having an "exotic extension registry". The main use-case for keeping this tool installed is to catch mistakes from users who do not fully understand who make a mistake after conversion (e.g. keep using drush dl / drush pm-update). To benefit from this protection, a naive user must:

1. Leave the plugin installed (need to decide how to phrase the "how to remove" documentation, so folks know whether to keep it or not)
2. Understand how to configure the exotic extension registry
3. Bother to configure the exotic extension registry

I think we should simplify further, and only notify about exotic extensions once.

Definitions:

1. Drupal site is not "Composer ready" - drupal/core-composer-scaffold not in composer.json
2. Drupal site is "Composer ready" but not "Composer managed" - using drupal/core-composer-scaffold, but no "drupal/*" contrib extensions in composer.json
3. Drupal site is "Composer managed" - at least one "drupal/*" contrib extension in composer.json.

Definition of a contrib extension:

1. Starts with "drupal/"
2. Is not "drupal/core"
3. Does not start with "drupal/core-"

With those definitions, then, we could:

1. Only warn about exotic extensions when doing the first conversion from "Composer ready" to "Composer managed".
2. Offer to convert sites from "Composer ready" to "Composer managed" on "composer update" / "composer require" (Probably as a follow-on issue, maybe Drupal 9+ only)

As an independent effort, we (ahem, I) could also make Drush dl / pm-update detect "Composer managed" sites and refuse to operate. This would only protect folks who upgrade their Drush, but it would be something.

mile23’s picture

To benefit from this protection, a naive user must:

1. Leave the plugin installed (need to decide how to phrase the "how to remove" documentation, so folks know whether to keep it or not)
2. Understand how to configure the exotic extension registry
3. Bother to configure the exotic extension registry

There is no such thing as the exotic extension registry.

'Exotic' means it doesn't have a project: key in the info file or otherwise can't be found by the composer facade. That's how you figure out if it's contrib or not.

Plenty of sites have lots of 'exotic' extensions, such as custom modules and themes.

The list of ones we've already found will be added to the extra when we do the conversion. Then the user has a handy list of extensions in addition to not having us warn about them.

1. Only warn about exotic extensions when doing the first conversion from "Composer ready" to "Composer managed".
2. Offer to convert sites from "Composer ready" to "Composer managed" on "composer update" / "composer require" (Probably as a follow-on issue, maybe Drupal 9+ only)

So you want an interaction like this:

$ composer update
You have unmanaged extensions. Do you want to convert all your extensions to be composerific? y/n

I want an interaction like this:

$ composer update
[composer update stuff happens]
You have unmanaged extensions. If you want to convert them to be composerific, do 'composer drupal:reconcile-extensions'
$ composer drupal:reconcile-extensions
You made the right choice. Let's add all the rest of the extensions to composer.json.

For require I also want to step out of the way unless summoned. If you require drupal/some-contrib-project, you get the same message: Run the thing to reconcile your other modules if you want to.

If they require contrib, then they get a warning saying they have both types and it's not great to have both types. But we should let them do that anyway.

I don't want to have to try to explain to a Drupal developer why Composer won't let a developer ad hoc require a contrib module if there are non-composer-managed extensions. If we place that restriction, the solution is to remove the plugin because it's annoying.

If they remove the plugin, then we've failed because they have both composer-managed extension and non-composer-managed extensions *and* no nag to tell them what's about to break.

greg.1.anderson’s picture

I don't want Composer to stop people with Drush-managed extensions from using composer require. I might like to go the other way, and break drush dl for sites that have Composer-managed extensions.

We could actually do that without changing Drush 8. We'd just have to write a policy file to the site when we convert extensions to be Composer managed (or maybe even when we detect the use of composer require with a contrib module). This is probably not a good idea, though, as it is unnecessary (and therefore annoying) to folks who don't use Drush 8, and is perhaps too aggressive even for those who would benefit.

Maybe I'll just update Drush to warn the user whenever using dl / pm-update with a module whose composer.json has a non-empty require section.

The list of ones we've already found will be added to the extra when we do the conversion.

This is what I'm calling the exotic extension registry, after the terminology used in #62. I suppose it would work fine to do the interaction steps described in #67, and perhaps warn about exotic extensions the first time they are encountered only (and add them to a list in "extra" after the user is presented with their options).

aaronmchale’s picture

Re @greg.1.anderson in #66:

Definition of a contrib extension:

1. Starts with "drupal/"
2. Is not "drupal/core"
3. Does not start with "drupal/core-"

Custom extensions might also define a composer.json where the namespace is "drupal" ("drupal/custom-module"), there's nothing which guarantees they won't. Honestly I've done that myself simply because every extension on D.o does so it kind of subconsciously sets a precedent that a Drupal extension has the namespace starting with Drupal, and because the class path would start with Drupal\custom-extension. Whether it's right or wrong is a different discussion, but my point is "drupal/" doesn't guarentee that a contrib project exists for that extension.

greg.1.anderson’s picture

Maybe it's pointless to try and make assumptions about the status of an extension based on its name or org. We could get a pretty good notion of which projects are contrib extensions by looking for # Information added by Drupal.org packaging script in the info.yml site. Of course, there's no guarantee that a custom module might have copied that comment from a contrib module and just left it there.

It is still my thought that I'd rather make such heavy use of Composer APIs just to determine if an extension is contrib or not. Maybe we could do three checks:

1. Name starts with "drupal/"
2. .info.yml contains information from Drupal.org packaging
3. The url https://dropa.org/project/foo exists, where "foo" is the part of the name afer "drupal/"

No Composer APIs, relatively quick, determines whether the module is a contrib extension. Maybe this is good enough, for less code?

imclean’s picture

@AaronMcHale,

Custom extensions might also define a composer.json where the namespace is "drupal" ("drupal/custom-module"),

I'm not sure how this would work. Can you update a custom module with composer update drupal/custom-module?

greg.1.anderson’s picture

@imclean The point here is that custom modules should remain in the repository, whereas contrib modules are removed and added to composer.json. So, for example, if a site had only two modules, drupal/ctools and drupal/my-custom-module, both in the modules directory, then ideally we would add drupal/ctools to composer.json (configured to install to modules/contrib) and move my-custom-module to modules/custom.

imclean’s picture

@greg.1.anderson, I understand, I'm just wondering why custom modules would define a composer.json file at all. Could it be used to manage dependencies even if it can't be installed via composer?

One option could be to provide some interactivity. For example, provide a list of discovered modules and ask to confirm any custom modules which couldn't be automatically detected.

aaronmchale’s picture

@greg.1.anderson, I understand, I'm just wondering why custom modules would define a composer.json file at all. Could it be used to manage dependencies even if it can't be installed via composer?

One option could be to provide some interactivity. For example, provide a list of discovered modules and ask to confirm any custom modules which couldn't be automatically detected.

Well, yeah a custom module could define a composer.json if it had specific dependencies, that might be an architectural decision, the custom module might be added to the root composer.json by defining it as a path repository. This also applies (and is probably more useful) when using a custom developed Drupal profile.

Additionally, there might be cases where Drupal modules come from a remote repository that isn't Drupal.org, you can easily define Git repositories, or there might be a private Composer server in use.

All of these are technically valid use cases that probably exist in the wild, some of these I've worked with before.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

tedbow’s picture

adding "Automatic Updates Initiative" tag because we will probably need something like this for some site to use the new Automatic Updates because it will be composer based.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.