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

FIX Don't rely on inaccessible packages when fetching update info. #56

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ will need to have the necessary permissions to access the private VCS repositori
update information about necessary updates to the module.

If the process looking for available updates fails (for example, due to an authentication failure against a private
repository) the process will fail gracefully and allow the rest of the report generation to continue.
repository) the process will fail gracefully and allow the rest of the report generation to continue. However, this
can result in incomplete information being fetched about non-private repositories due to the way composer checks for
conflicts between packages. For this reason, if you cannot supply authentication details for private repositories,
you should mark those repositories as inaccessible as per the documentation in the [SilverStripe Maintenance module](https://github.com/bringyourownideas/silverstripe-maintenance#private-repositories).

Users on the [Common Web Platform](https://cwp.govt.nz) will currently not be able to retrieve information about
updates to private repositories.
Expand Down
74 changes: 74 additions & 0 deletions src/DriverReflection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

namespace BringYourOwnIdeas\UpdateChecker;

use Composer\Config;
use Composer\IO\IOInterface;
use Composer\Repository\VcsRepository;
use ReflectionObject;
use RuntimeException;

class DriverReflection
{
public static function getDriverWithoutException(VcsRepository $repo, IOInterface $io, Config $config)
{
$reflectedRepo = new ReflectionObject($repo);
$drivers = static::getRepoField($repo, $reflectedRepo, 'drivers');

if (isset($drivers[static::getRepoField($repo, $reflectedRepo, 'type')])) {
$class = $drivers[static::getRepoField($repo, $reflectedRepo, 'type')];
$driver = new $class($repo->getRepoConfig(), $io, $config);
try {
$driver->initialize();
} catch (RuntimeException $e) {
// no-op - this is probably caused due to insufficient permissions when trying to create /var/www/.ssh
// but since we're just getting the driver as a shortcut to getting the repository name, we can ignore this for now.
}
return $driver;
}

foreach ($drivers as $driver) {
if ($driver::supports($io, $config, static::getRepoField($repo, $reflectedRepo, 'url'))) {
$driver = new $driver($repo->getRepoConfig(), $io, $config);
try {
$driver->initialize();
} catch (RuntimeException $e) {
// no-op - this is probably caused due to insufficient permissions when trying to create /var/www/.ssh
// but since we're just getting the driver as a shortcut to getting the repository name, we can ignore this for now.
}
return $driver;
}
}

foreach ($drivers as $driver) {
if ($driver::supports($io, $config, static::getRepoField($repo, $reflectedRepo, 'url'), true)) {
$driver = new $driver($repo->getRepoConfig(), $io, $config);
try {
$driver->initialize();
} catch (RuntimeException $e) {
// no-op - this is probably caused due to insufficient permissions when trying to create /var/www/.ssh
// but since we're just getting the driver as a shortcut to getting the repository name, we can ignore this for now.
}
return $driver;
}
}
}

public static function getSshUrl($driver)
{
$reflectedDriver = new ReflectionObject($driver);
if ($reflectedDriver->hasMethod('generateSshUrl')) {
$reflectedMethod = $reflectedDriver->getMethod('generateSshUrl');
$reflectedMethod->setAccessible(true);
return $reflectedMethod->invoke($driver);
}
return null;
}

protected static function getRepoField(VcsRepository $repo, ReflectionObject $reflectedRepo, string $field)
{
$reflectedUrl = $reflectedRepo->getProperty($field);
$reflectedUrl->setAccessible(true);
return $reflectedUrl->getValue($repo);
}
}
73 changes: 66 additions & 7 deletions src/Extensions/ComposerLoaderExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace BringYourOwnIdeas\UpdateChecker\Extensions;

use BringYourOwnIdeas\Maintenance\Tasks\UpdatePackageInfoTask;
use BringYourOwnIdeas\UpdateChecker\DriverReflection;
use Composer\Composer;
use Composer\Factory;
use Composer\IO\NullIO;
Expand All @@ -10,6 +12,9 @@
use Composer\Repository\BaseRepository;
use Composer\Repository\CompositeRepository;
use Composer\Repository\RepositoryInterface;
use Composer\Repository\RepositoryManager;
use Composer\Repository\Vcs\VcsDriverInterface;
use Composer\Repository\VcsRepository;
use SilverStripe\Core\Environment;
use SilverStripe\Core\Extension;

Expand Down Expand Up @@ -120,17 +125,71 @@ public function onAfterBuild()
}

$originalDir = getcwd();

if ($originalDir !== BASE_PATH) {
chdir(BASE_PATH);
chdir(BASE_PATH);
/** @var Composer $composer */
$composer = Factory::create($io = new NullIO());

// Don't include inaccessible repositories.
$inaccessiblePackages = (array)UpdatePackageInfoTask::config()->get('inaccessible_packages');
$inaccessibleHosts = (array)UpdatePackageInfoTask::config()->get('inaccessible_repository_hosts');
if (!empty($inaccessiblePackages) || !empty($inaccessibleHosts)) {
$oldManager = $composer->getRepositoryManager();
$manager = new RepositoryManager(
$io,
$composer->getConfig(),
$composer->getEventDispatcher(),
Factory::createRemoteFilesystem($io, $composer->getConfig())
);
$manager->setLocalRepository($oldManager->getLocalRepository());
foreach ($oldManager->getRepositories() as $repo) {
if ($repo instanceof VcsRepository) {
/** @var VcsDriverInterface $driver */
$driver = DriverReflection::getDriverWithoutException($repo, $io, $composer->getConfig());
$sshUrl = DriverReflection::getSshUrl($driver);
$sourceURL = $driver->getUrl();
$package = $this->findPackageByUrl($sourceURL);
if (!$package && $sshUrl) {
$package = $this->findPackageByUrl($sshUrl);
}
// Don't add the repository if we can confirm it's inaccessible.
// Otherwise the UpdateChecker will attempt to fetch packages using the VcsDriver.
if (
($package && in_array($package->name, $inaccessiblePackages))
|| in_array(parse_url($sourceURL, PHP_URL_HOST), $inaccessibleHosts)
|| ($sshUrl && in_array(preg_replace('/^([^@]+@)?([^:]+):.*/', '$2', $sshUrl), $inaccessibleHosts))
) {
continue;
}
}
$manager->addRepository($repo);
}
$composer->setRepositoryManager($manager);
}

/** @var Composer $composer */
$composer = Factory::create(new NullIO());
$this->setComposer($composer);
chdir($originalDir);
}

if ($originalDir !== BASE_PATH) {
chdir($originalDir);
public function findPackageByUrl(string $url, bool $includeDev = true)
{
$lock = $this->owner->getLock();
foreach ($lock->packages as $package) {
if (isset($package->source->url) && $package->source->url === $url) {
return $package;
}
if (isset($package->dist->url) && $package->dist->url === $url) {
return $package;
}
}
if ($includeDev) {
foreach ($lock->{'packages-dev'} as $package) {
if (isset($package->source->url) && $package->source->url === $url) {
return $package;
}
if (isset($package->dist->url) && $package->dist->url === $url) {
return $package;
}
}
}
}
}