Hello everyone,
In this article, I'll provide practical examples showcasing how unexpected challenges can arise in Magento 2 & Adobe Commerce projects while building and/or using custom CLI commands from 3rd parties. This becomes especially significant while working with CI/CD and performing project setups and test execution in pipelines.
Introducing the main "hero" of our article: ISSUE: magento setup command is throwing magento.flag does't exist. We'll take a deeper look into this issue, understanding its roots and finding ways to address it.
Disclaimer: Just a quick heads-up on naming—both Adobe Commerce and Magento 2 are covered in this article. After all, Adobe Commerce is Magento. Long Live Magento!
While working on various Magento 2 projects, I frequently encountered snags when performing a `clean` setup of a project using a freshly created database. This usually happens during integration or functional test setups in pipelines & local environments. Still, this issue isn't exclusive to that scenario and can pop up during upgrades, migrations, or deployments.
The culprits? Here are two pesky errors you might recognize:
The default website isn't defined. Set the website and try again.
For Commerce editions, there's also:
SQLSTATE[42S02]: Base table or view not found: 1146 Table
'magento2.flag' doesn't exist, query was:
SELECT flag.* FROM flag WHERE (flag.flag_code='staging')
When you see these during the `php bin/magento setup:install` command execution, they offer little insight into the real issue. After a few hair-pulling hours of research, you'd discover these errors stem from the premature invocation of the DB resource, triggering the initialization of config and state entities from the Magento DB.
But here's the conundrum: You can't fetch data from the DB at this point because there aren't any tables present yet. They only appear and populate during the installation process, not beforehand. ¯\_(ツ)_/¯
So, we're caught in a circular problem: trying to install the system but getting stopped because it's not installed. Confusing, right?🤪
This happens because someone, somewhere, is prematurely calling the DB connection resource. It is obvious, and I hope you would be as grateful as Sam Eliot is for this revelation insight.
This "someone" should be inside `bin/magento` and called before actual command execution. Usually, CLI command constructors are guilty here. Let me explain why.
For clarity, take a look at this code:
class DoSmthing
{
private ?string $dummyConfigValue;
public function __construct(ScopeConfigInterface $storeManager)
{
$this->dummyConfigValue = $storeManager->getValue('some/dummy/path', ScopeInterface::SCOPE_STORE);
}
public function getValue(): ?string
{
return $this->dummyConfigValue;
}
}
class DummyCommand extends Command
{
// ...
public function __construct(DoSmthing $model, string $name = null)
{
$this->model = $model;
parent::__construct($name);
}
// ...
}
The root of the problem lies in the constructors of both the command and model classes.
In the DoSmthing class, the system attempts to retrieve a config value. This action calls the store, store_website, and core_config_data tables when creating the DummyCommand object.
And guess what happens when this code runs before the tables are ready? Yep, errors appear!
Remember this golden rule: Every CLI command class initializes every time you invoke php bin/magento. So, here's how to save yourself future headaches:
We can solve the issue from our previous example with DummyCommand by adding the following lines to di.xml:
<type name="AbcTest\DummyConsoleCommand\Console\Command\DummyCommand">
<arguments>
<argument name="model" xsi:type="object">
\AbcTest\DummyConsoleCommand\Model\DoSmthing\Proxy
</argument>
</arguments>
</type>
Proxy is a well-known pattern, and in Magento, it is widely used to avoid unneeded code execution before we actually request it. In our example — DoSmthing class object will not be instantiated until we call any method of this class.
No config retrieving while CLI class object creation — No error.
More details about proxy here: Proxies.
For those with a small number of extensions on their project, pinpointing the rogue CLI commands isn't too challenging. However, for more extensive, legacy projects or ones brimming with third-party extensions, identifying the culprits becomes an ordeal.
To save time, we've crafted a solution to automate the proxy injection process. Introducing: run-as-root/magento-cli-auto-proxy.
This package injects Proxy into all __construct arguments of CLI commands, eligible for proxying. The result? No errors related to early resource access and, as a side effect, faster execution of php bin/magento and php bin/magento setup:install.
Check out the README for more details. We always appreciate feedback and contributions!
Let's sum it up:
Thank you for diving deep into this article. I welcome all your questions and appreciate any feedback you might have.
Happy coding and... Long Live Magento! 😁
Connect with Vlad, our Adobe Commerce expert, to enhance your development workflow and resolve CLI command issues.