Skip to content

Commit

Permalink
Fix plugin upgrade handling (#71)
Browse files Browse the repository at this point in the history
* Fix plugin upgrade handling
  • Loading branch information
SanderSander authored Dec 7, 2024
1 parent 4c1bd4d commit 1c04fab
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 8 deletions.
9 changes: 8 additions & 1 deletion ruleset.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@
<rule ref="rulesets/unusedcode.xml" />
<rule ref="rulesets/codesize.xml" />
<rule ref="rulesets/cleancode.xml" />
<rule ref="rulesets/design.xml" />
<rule ref="rulesets/design.xml">
<exclude name="CouplingBetweenObjects" />
</rule>
<rule ref="rulesets/design.xml/CouplingBetweenObjects">
<properties>
<property name="maximum" value="16" />
</properties>
</rule>
<rule ref="rulesets/controversial.xml">
<exclude name="CamelCaseMethodName" />
</rule>
Expand Down
33 changes: 28 additions & 5 deletions src/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@
use Composer\Plugin\Capability\CommandProvider as ComposerCommandProvider;
use Composer\Plugin\Capable;
use Composer\Plugin\PluginInterface;
use Composer\Script\Event;
use Composer\Script\ScriptEvents;
use Composer\Util\Filesystem as ComposerFileSystem;
use ComposerLink\Actions\LinkPackages;
use ComposerLink\Repository\Repository;
use ComposerLink\Repository\RepositoryFactory;
use RuntimeException;
use Throwable;

class Plugin implements PluginInterface, Capable, EventSubscriberInterface
{
Expand All @@ -40,6 +42,15 @@ class Plugin implements PluginInterface, Capable, EventSubscriberInterface

protected Composer $composer;

/**
* It can happen that activation doesn't work, this happens when this plugin is upgraded.
* Composer runs this file through an eval() with renamed class names, but all other classes
* in this library are still the old ones loaded in memory.
*
* We try to detect this, and skip the event callbacks if it happens
*/
protected bool $couldNotActivate = false;

public function __construct(
?ComposerFileSystem $filesystem = null,
protected ?LinkPackages $linkPackages = null,
Expand Down Expand Up @@ -73,10 +84,15 @@ public function activate(Composer $composer, IOInterface $io): void
$io->debug("[ComposerLink]\tPlugin is activating");
$this->composer = $composer;

$this->initializeRepository();
$this->initializeLinkedPackageFactory();
$this->initializeLinkManager();
$this->initializeLinkPackages();
try {
$this->initializeRepository();
$this->initializeLinkedPackageFactory();
$this->initializeLinkManager();
$this->initializeLinkPackages();
} catch (Throwable $e) {
$io->debug("[ComposerLink]\tException: " . $e->getMessage());
$this->couldNotActivate = true;
}
}

protected function initializeRepository(): void
Expand Down Expand Up @@ -136,8 +152,15 @@ public function getLinkManager(): LinkManager
return $this->linkManager;
}

public function linkLinkedPackages(): void
public function linkLinkedPackages(Event $event): void
{
// Plugin couldn't be activated probably because the plugin was updated
if ($this->couldNotActivate) {
$event->getIO()->warning('<warning>Composer link couldn\'t be activated because it was probably upgraded, run `composer install` again to link packages</warning>');

return;
}

if (is_null($this->linkPackages)) {
throw new RuntimeException('Plugin not activated');
}
Expand Down
32 changes: 30 additions & 2 deletions tests/Unit/PluginTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,17 @@
use Composer\Plugin\Capability\CommandProvider as ComposerCommandProvider;
use Composer\Repository\InstalledRepositoryInterface;
use Composer\Repository\RepositoryManager;
use Composer\Script\Event;
use Composer\Script\ScriptEvents;
use Composer\Util\Filesystem;
use Composer\Util\Loop;
use ComposerLink\Actions\LinkPackages;
use ComposerLink\CommandProvider;
use ComposerLink\Plugin;
use ComposerLink\Repository\RepositoryFactory;
use PHPUnit\Framework\MockObject\MockObject;
use RuntimeException;
use TypeError;

/**
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
Expand Down Expand Up @@ -103,6 +106,30 @@ public function test_if_plugin_can_be_utilized(): void
$plugin->uninstall($this->composer, $this->io);
}

public function test_unable_to_activate_plugin(): void
{
$repositoryFactory = $this->createMock(RepositoryFactory::class);
$linkPackages = $this->createMock(LinkPackages::class);
$event = $this->createMock(Event::class);
$event->method('getIO')->willReturn($this->io);

$repositoryFactory->method('create')
->willThrowException(new TypeError('test error'));

$plugin = new Plugin(
$this->filesystem,
$linkPackages,
$repositoryFactory,
);

$plugin->activate($this->composer, $this->io);

$this->io->expects(static::once())->method('warning')->with(
static::stringContains('Composer link couldn\'t be activated')
);
$plugin->linkLinkedPackages($event);
}

public function test_is_global(): void
{
$this->config->method('get')
Expand Down Expand Up @@ -143,13 +170,14 @@ public function test_plugin_throws_exception_repository(): void

public function test_plugin_link_linked_packages(): void
{
$event = $this->createMock(Event::class);
$linkPackages = $this->createMock(LinkPackages::class);
$linkPackages->expects(static::once())->method('execute');
$plugin = new Plugin($this->createMock(Filesystem::class), $linkPackages);
$plugin->linkLinkedPackages();
$plugin->linkLinkedPackages($event);

static::expectException(RuntimeException::class);
$plugin = new Plugin($this->createMock(Filesystem::class));
$plugin->linkLinkedPackages();
$plugin->linkLinkedPackages($event);
}
}

0 comments on commit 1c04fab

Please sign in to comment.