Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Config import single command #5146

Closed
malcomio opened this issue May 13, 2022 · 14 comments
Closed

Config import single command #5146

malcomio opened this issue May 13, 2022 · 14 comments

Comments

@malcomio
Copy link
Contributor

As per https://drupal.stackexchange.com/questions/221592/import-a-single-yml-configuration-file, there is not currently a way to import a single configuration file via drush, equivalent to the config:import:single command provided by Drupal Console.

It would be good to have either a flag for the config:import command, or a separate command.

As far as I'm aware, the other options are:

  • import via the UI, copying and pasting from the file(s)
  • copy the file(s) into a separate directory and use the --partial flag

Both of those are time-consuming.

@fede-green
Copy link

This would be so nice to have!

@JPustkuchen
Copy link

JPustkuchen commented Jun 24, 2022

Just ran into a situation where this would have been very helpful!

In my case something like:
drush cim --single cookies.cookies_service.etracker cookies.cookies_service.google_analytics
would have been very helpful.

Single should not mean here, that it must be just a single config, but can also be a list of config names, but ONLY these should be imported.

So perhaps there's a better standard name typically used for such things? Perhaps other linux tools give an idea? sftp file transfer by file name or others?

@weitzman would you prefer to use "cim" for this (I'd do) or a separate one like "cims"...

Edit: what about for example:
drush cim --only cookies.cookies_service.etracker cookies.cookies_service.google_analytics

@weitzman
Copy link
Member

copy the file(s) into a separate directory and use the --partial flag

Thats the recommended way.

Both of those are time-consuming.

Its one cp. I would not characterize it as "time consuming".

@weitzman weitzman closed this as completed Jul 3, 2022
@ctrladel
Copy link

I also landed here looking for a way to import a single file and think this should be reopened.

To say that a single cp is sufficient because it's not "time consuming" assumes a lot about what is trying to be done and where the command is trying to be run. When trying to run a command against a remote host finding a writeable directory and making a temp directory is quite time consuming. For my current situation I'm trying to correct a tricky order of operations config import bug on Acquia so to do "one cp" I have to

  • ssh into the server
  • find a writeable directory on Acquia
  • locate the config file on the Acquia server
  • create a temporary directory in the writeable area of Acquia
  • copy the config file to the temporary directory
  • run the drush command with the full path the to the temporary folder
  • if my fix works great, if not I'll have to deploy new code and remember to repeat the copy step in order to get the latest change
  • once everything is fixed I'll then have to remove the temporary directory

It's a non trivial amount of work for something that feels like it should already be supported by the --partial flag.

@Lancelight
Copy link

Lancelight commented Oct 11, 2022

Agree with @ctrladel. The answer given by @weitzman is very assume'ish and also seems to include an uneducated opinion at the tail end stating that it's "just a cp". Assuming that it's "just a cp" is pretty ridiculous when you toss in 21st century hosting methods, git repositories (pull requests and the like), etc. Not every site is hosted on your own machine. Making multiple copies of something just to limp around lack of a required feature can be quite an involved process depending upon the hosting situation.

The correct fix is to actually implement a single config import.

IE: Drupal console has this in the form of config:import:single (alias of cis), drush is lagging behind in a lot of areas compared to drupal console: https://drupalconsole.com/docs/vn/commands/config-import-single

@weitzman
Copy link
Member

PRs welcome for this.

@weitzman weitzman reopened this Nov 18, 2022
@weitzman
Copy link
Member

Until then config:set can replace a whole config object https://www.drush.org/latest/commands/config_set/. Satisfies some use cases.

@Lancelight
Copy link

Lancelight commented Nov 19, 2022

As far as I am aware, config:set doesnt work with files or does it? I am pretty certain config:set is just a command line only command which has nothing to do with the contents of a config yml file. Am I missing something? Maybe there is some sort of way to get it to load from file, idk. Would be welcome news to me :D Perhaps this might work? drush config:set --input-format=yaml < some_filename.yml I have no idea if that would even work or not.

EDIT: Nope, that didnt work either :/ It still asks for a key argument.

@gitressa
Copy link
Contributor

The --partial flag does work, but I had trouble finding the right command, until I found How to import a specific config files map with drush. It is not clear that you need to supply both --partial and --source:

$ drush config:import -h
Import config from a config directory.

Options:
 --source=SOURCE An arbitrary directory that holds the configuration files.                                        
 --partial       Allows for partial config imports from the source directory. Only updates and new configs will be 
                 processed with this flag (missing configs will not be deleted). No config transformation happens. 

So the full command which works for me in Lando is:

drush config:import --partial --source=/app/config

With this structure:

drupal10/
├── composer.json
├── composer.lock
├── config
├── vendor
└── web

@weitzman
Copy link
Member

I added the example from @gitressa to config:import.

As for config:import for a single file without creating a directory, I just added documentation to both config:import and config:set that shows how to do this.

Both these docs improvements are in #5543

weitzman added a commit that referenced this issue Apr 23, 2023
* Add Usage to cim

* More usages examples for single file config import
@weitzman
Copy link
Member

The docs in #3343 should work in earlier versions of Drush as well as Drush 12.

@gitressa
Copy link
Contributor

Thanks for maintaining Drush @weitzman and @greg-1-anderson, I appreciate it very much. It's an incredible tool.

@Paulmicha
Copy link

Paulmicha commented Aug 29, 2023

If anyone else needed a port of drupal console config:import:single in drush :

<?php

namespace Drupal\drush_cis\Drush\Commands;

use Consolidation\OutputFormatters\StructuredData\RowsOfFields;
use Drush\Attributes as CLI;
use Drush\Commands\DrushCommands;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\config\StorageReplaceDataWrapper;
use Drupal\Core\Config\CachedStorage;
use Drupal\Core\Config\ConfigImporter;
use Drupal\Core\Config\ConfigImporterException;
use Drupal\Core\Config\ConfigManager;
use Drupal\Core\Config\StorageComparer;
use Symfony\Component\Yaml\Parser;
use Webmozart\PathUtil\Path;

/**
 * A Drush commandfile.
 *
 * In addition to this file, you need a drush.services.yml
 * in root of your module, and a composer.json file that provides the name
 * of the services file to use.
 */
final class DrushCisCommands extends DrushCommands {

  /**
   * Imports given config file(s).
   */
  #[CLI\Command(name: 'drush_cis:config-import-single', aliases: ['cis'])]
  #[CLI\Option(name: 'file', description: 'Repeatable. Specify 1 or more paths to files as needed.')]
  #[CLI\Usage(name: 'drush cis --file=path/to/config.file.yml', description: 'Imports given config file.')]
  public function configImportSingle($options = ['file' => ['default']]) {
    $configStorage = \Drupal::service('config.storage');
    $sourceStorage = new StorageReplaceDataWrapper($configStorage);
    $names = [];

    foreach ($options['file'] as $configFile) {
      if (!file_exists($configFile)) {
        $this->logger()->error(dt('Error : config file does not exist') . " : '$configFile'");
        return 1;
      }
      $name = Path::getFilenameWithoutExtension($configFile);
      $ymlFile = new Parser();
      $value = $ymlFile->parse(file_get_contents($configFile));
      $sourceStorage->replaceData($name, $value);
      $names[] = $name;
    }

    $storageComparer = new StorageComparer(
      $sourceStorage,
      $configStorage,
      \Drupal::service('config.manager')
    );

    $configImporter = new ConfigImporter(
      $storageComparer,
      \Drupal::service('event_dispatcher'),
      \Drupal::service('config.manager'),
      \Drupal::lock(),
      \Drupal::service('config.typed'),
      \Drupal::moduleHandler(),
      \Drupal::service('module_installer'),
      \Drupal::service('theme_handler'),
      \Drupal::service('string_translation'),
      \Drupal::service('extension.list.module')
    );

    if ($configImporter->alreadyImporting()) {
      $this->logger()->warning(dt('Already importing.'));
      return 0;
    }

    try {
      if ($configImporter->validate()) {
        $sync_steps = $configImporter->initialize();
        foreach ($sync_steps as $step) {
          $context = [];
          do {
            $configImporter->doSyncStep($step, $context);
          }
          while ($context['finished'] < 1);
        }
      }
    }
    catch (ConfigImporterException $e) {
      $feedback = "Error: unable to import specified config file(s)."
        . PHP_EOL
        . strip_tags(implode(PHP_EOL, $configImporter->getErrors()))
        . PHP_EOL;
      $this->logger()->error($feedback);
      return 2;
    }
    catch (\Exception $e) {
      $this->logger()->error($e->getMessage());
      return 3;
    }

    $this->logger()->success(
      dt('Config file(s) successfully imported. Config names imported :')
      . " "
      . join(', ', $names)
    );
  }

}

@loopy3025
Copy link

Just thought I'd throw a friendly note that, if you're running this with --source, you're likely going to have to specify a level down to your config folder because drush is technically executing from docroot.

fin drush config-import --partial --source="../config/migrate/"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants