Browse Source

Add deleted Updater

venyii 10 years ago
parent
commit
f0aea79d38
1 changed files with 384 additions and 0 deletions
  1. 384 0
      src/Packagist/WebBundle/Package/Updater.php

+ 384 - 0
src/Packagist/WebBundle/Package/Updater.php

@@ -0,0 +1,384 @@
+<?php
+
+/*
+ * This file is part of Packagist.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *     Nils Adermann <naderman@naderman.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Packagist\WebBundle\Package;
+
+use Composer\Package\AliasPackage;
+use Composer\Package\PackageInterface;
+use Composer\Repository\RepositoryInterface;
+use Composer\Repository\InvalidRepositoryException;
+use Composer\Util\ErrorHandler;
+use Packagist\WebBundle\Entity\Author;
+use Packagist\WebBundle\Entity\Package;
+use Packagist\WebBundle\Entity\Tag;
+use Packagist\WebBundle\Entity\Version;
+use Packagist\WebBundle\Entity\SuggestLink;
+use Symfony\Bridge\Doctrine\RegistryInterface;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class Updater
+{
+    const UPDATE_EQUAL_REFS = 1;
+    const DELETE_BEFORE = 2;
+
+    /**
+     * Doctrine
+     * @var RegistryInterface
+     */
+    protected $doctrine;
+
+    /**
+     * Supported link types
+     * @var array
+     */
+    protected $supportedLinkTypes = array(
+        'require'     => array(
+            'method' => 'getRequires',
+            'entity' => 'RequireLink',
+        ),
+        'conflict'    => array(
+            'method' => 'getConflicts',
+            'entity' => 'ConflictLink',
+        ),
+        'provide'     => array(
+            'method' => 'getProvides',
+            'entity' => 'ProvideLink',
+        ),
+        'replace'     => array(
+            'method' => 'getReplaces',
+            'entity' => 'ReplaceLink',
+        ),
+        'devRequire' => array(
+            'method' => 'getDevRequires',
+            'entity' => 'DevRequireLink',
+        ),
+    );
+
+    /**
+     * Constructor
+     *
+     * @param RegistryInterface $doctrine
+     */
+    public function __construct(RegistryInterface $doctrine)
+    {
+        $this->doctrine = $doctrine;
+
+        ErrorHandler::register();
+    }
+
+    /**
+     * Update a project
+     *
+     * @param \Packagist\WebBundle\Entity\Package $package
+     * @param RepositoryInterface $repository the repository instance used to update from
+     * @param int $flags a few of the constants of this class
+     * @param \DateTime $start
+     */
+    public function update(Package $package, RepositoryInterface $repository, $flags = 0, \DateTime $start = null)
+    {
+        $blacklist = '{^symfony/symfony (2.0.[456]|dev-charset|dev-console)}i';
+
+        if (null === $start) {
+            $start = new \DateTime();
+        }
+        $pruneDate = clone $start;
+        $pruneDate->modify('-8days');
+
+        $versions = $repository->getPackages();
+        $em = $this->doctrine->getManager();
+
+        if ($repository->hadInvalidBranches()) {
+            throw new InvalidRepositoryException('Some branches contained invalid data and were discarded, it is advised to review the log and fix any issues present in branches');
+        }
+
+        usort($versions, function ($a, $b) {
+            $aVersion = $a->getVersion();
+            $bVersion = $b->getVersion();
+            if ($aVersion === '9999999-dev' || 'dev-' === substr($aVersion, 0, 4)) {
+                $aVersion = 'dev';
+            }
+            if ($bVersion === '9999999-dev' || 'dev-' === substr($bVersion, 0, 4)) {
+                $bVersion = 'dev';
+            }
+            $aIsDev = $aVersion === 'dev' || substr($aVersion, -4) === '-dev';
+            $bIsDev = $bVersion === 'dev' || substr($bVersion, -4) === '-dev';
+
+            // push dev versions to the end
+            if ($aIsDev !== $bIsDev) {
+                return $aIsDev ? 1 : -1;
+            }
+
+            // equal versions are sorted by date
+            if ($aVersion === $bVersion) {
+                return $a->getReleaseDate() > $b->getReleaseDate() ? 1 : -1;
+            }
+
+            // the rest is sorted by version
+            return version_compare($aVersion, $bVersion);
+        });
+
+        $versionRepository = $this->doctrine->getRepository('PackagistWebBundle:Version');
+
+        if ($flags & self::DELETE_BEFORE) {
+            foreach ($package->getVersions() as $version) {
+                $versionRepository->remove($version);
+            }
+
+            $em->flush();
+            $em->refresh($package);
+        }
+
+        $lastUpdated = true;
+        foreach ($versions as $version) {
+            if ($version instanceof AliasPackage) {
+                continue;
+            }
+
+            if (preg_match($blacklist, $version->getName().' '.$version->getPrettyVersion())) {
+                continue;
+            }
+
+            $lastUpdated = $this->updateInformation($package, $version, $flags);
+            if ($lastUpdated) {
+                $em->flush();
+            }
+        }
+
+        if (!$lastUpdated) {
+            $em->flush();
+        }
+
+        // remove outdated versions
+        foreach ($package->getVersions() as $version) {
+            if ($version->getUpdatedAt() < $pruneDate) {
+                $versionRepository->remove($version);
+            }
+        }
+
+        $package->setUpdatedAt(new \DateTime);
+        $package->setCrawledAt(new \DateTime);
+        $em->flush();
+    }
+
+    private function updateInformation(Package $package, PackageInterface $data, $flags)
+    {
+        $em = $this->doctrine->getManager();
+        $version = new Version();
+
+        $normVersion = $data->getVersion();
+
+        $existingVersion = $package->getVersion($normVersion);
+        if ($existingVersion) {
+            $source = $existingVersion->getSource();
+            // update if the right flag is set, or the source reference has changed (re-tag or new commit on branch)
+            if ($source['reference'] !== $data->getSourceReference() || ($flags & self::UPDATE_EQUAL_REFS)) {
+                $version = $existingVersion;
+            } else {
+                // mark it updated to avoid it being pruned
+                $existingVersion->setUpdatedAt(new \DateTime);
+
+                return false;
+            }
+        }
+
+        $version->setName($package->getName());
+        $version->setVersion($data->getPrettyVersion());
+        $version->setNormalizedVersion($normVersion);
+        $version->setDevelopment($data->isDev());
+
+        $em->persist($version);
+
+        $version->setDescription($data->getDescription());
+        $package->setDescription($data->getDescription());
+        $version->setHomepage($data->getHomepage());
+        $version->setLicense($data->getLicense() ?: array());
+
+        $version->setPackage($package);
+        $version->setUpdatedAt(new \DateTime);
+        $version->setReleasedAt($data->getReleaseDate());
+
+        if ($data->getSourceType()) {
+            $source['type'] = $data->getSourceType();
+            $source['url'] = $data->getSourceUrl();
+            $source['reference'] = $data->getSourceReference();
+            $version->setSource($source);
+        } else {
+            $version->setSource(null);
+        }
+
+        if ($data->getDistType()) {
+            $dist['type'] = $data->getDistType();
+            $dist['url'] = $data->getDistUrl();
+            $dist['reference'] = $data->getDistReference();
+            $dist['shasum'] = $data->getDistSha1Checksum();
+            $version->setDist($dist);
+        } else {
+            $version->setDist(null);
+        }
+
+        if ($data->getType()) {
+            $version->setType($data->getType());
+            if ($data->getType() && $data->getType() !== $package->getType()) {
+                $package->setType($data->getType());
+            }
+        }
+
+        $version->setTargetDir($data->getTargetDir());
+        $version->setAutoload($data->getAutoload());
+        $version->setExtra($data->getExtra());
+        $version->setBinaries($data->getBinaries());
+        $version->setIncludePaths($data->getIncludePaths());
+        $version->setSupport($data->getSupport());
+
+        $version->getTags()->clear();
+        if ($data->getKeywords()) {
+            $keywords = array();
+            foreach ($data->getKeywords() as $keyword) {
+                $keywords[mb_strtolower($keyword, 'UTF-8')] = $keyword;
+            }
+            foreach ($keywords as $keyword) {
+                $tag = Tag::getByName($em, $keyword, true);
+                if (!$version->getTags()->contains($tag)) {
+                    $version->addTag($tag);
+                }
+            }
+        }
+
+        $authorRepository = $this->doctrine->getRepository('PackagistWebBundle:Author');
+
+        $version->getAuthors()->clear();
+        if ($data->getAuthors()) {
+            foreach ($data->getAuthors() as $authorData) {
+                $author = null;
+
+                foreach (array('email', 'name', 'homepage', 'role') as $field) {
+                    if (isset($authorData[$field])) {
+                        $authorData[$field] = trim($authorData[$field]);
+                        if ('' === $authorData[$field]) {
+                            $authorData[$field] = null;
+                        }
+                    } else {
+                        $authorData[$field] = null;
+                    }
+                }
+
+                // skip authors with no information
+                if (!isset($authorData['email']) && !isset($authorData['name'])) {
+                    continue;
+                }
+
+                $author = $authorRepository->findOneBy(array(
+                    'email' => $authorData['email'],
+                    'name' => $authorData['name'],
+                    'homepage' => $authorData['homepage'],
+                    'role' => $authorData['role'],
+                ));
+
+                if (!$author) {
+                    $author = new Author();
+                    $em->persist($author);
+                }
+
+                foreach (array('email', 'name', 'homepage', 'role') as $field) {
+                    if (isset($authorData[$field])) {
+                        $author->{'set'.$field}($authorData[$field]);
+                    }
+                }
+
+                $author->setUpdatedAt(new \DateTime);
+                if (!$version->getAuthors()->contains($author)) {
+                    $version->addAuthor($author);
+                }
+                if (!$author->getVersions()->contains($version)) {
+                    $author->addVersion($version);
+                }
+            }
+        }
+
+        // handle links
+        foreach ($this->supportedLinkTypes as $linkType => $opts) {
+            $links = array();
+            foreach ($data->{$opts['method']}() as $link) {
+                $constraint = $link->getPrettyConstraint();
+                if (false !== strpos($constraint, ',') && false !== strpos($constraint, '@')) {
+                    $constraint = preg_replace_callback('{([><]=?\s*[^@]+?)@([a-z]+)}i', function ($matches) {
+                        if ($matches[2] === 'stable') {
+                            return $matches[1];
+                        }
+
+                        return $matches[1].'-'.$matches[2];
+                    }, $constraint);
+                }
+
+                $links[$link->getTarget()] = $constraint;
+            }
+
+            foreach ($version->{'get'.$linkType}() as $link) {
+                // clear links that have changed/disappeared (for updates)
+                if (!isset($links[$link->getPackageName()]) || $links[$link->getPackageName()] !== $link->getPackageVersion()) {
+                    $version->{'get'.$linkType}()->removeElement($link);
+                    $em->remove($link);
+                } else {
+                    // clear those that are already set
+                    unset($links[$link->getPackageName()]);
+                }
+            }
+
+            foreach ($links as $linkPackageName => $linkPackageVersion) {
+                $class = 'Packagist\WebBundle\Entity\\'.$opts['entity'];
+                $link = new $class;
+                $link->setPackageName($linkPackageName);
+                $link->setPackageVersion($linkPackageVersion);
+                $version->{'add'.$linkType.'Link'}($link);
+                $link->setVersion($version);
+                $em->persist($link);
+            }
+        }
+
+        // handle suggests
+        if ($suggests = $data->getSuggests()) {
+            foreach ($version->getSuggest() as $link) {
+                // clear links that have changed/disappeared (for updates)
+                if (!isset($suggests[$link->getPackageName()]) || $suggests[$link->getPackageName()] !== $link->getPackageVersion()) {
+                    $version->getSuggest()->removeElement($link);
+                    $em->remove($link);
+                } else {
+                    // clear those that are already set
+                    unset($suggests[$link->getPackageName()]);
+                }
+            }
+
+            foreach ($suggests as $linkPackageName => $linkPackageVersion) {
+                $link = new SuggestLink;
+                $link->setPackageName($linkPackageName);
+                $link->setPackageVersion($linkPackageVersion);
+                $version->addSuggestLink($link);
+                $link->setVersion($version);
+                $em->persist($link);
+            }
+        } elseif (count($version->getSuggest())) {
+            // clear existing suggests if present
+            foreach ($version->getSuggest() as $link) {
+                $em->remove($link);
+            }
+            $version->getSuggest()->clear();
+        }
+
+        if (!$package->getVersions()->contains($version)) {
+            $package->addVersions($version);
+        }
+
+        return true;
+    }
+}