/** * Builds the archives of the repository. * * @param array $packages List of packages to dump */ public function dump(array $packages) { $helper = new ArchiveBuilderHelper($this->output, $this->config['archive']); $directory = $helper->getDirectory($this->outputDir); $this->output->writeln(sprintf("<info>Creating local downloads in '%s'</info>", $directory)); $format = isset($this->config['archive']['format']) ? $this->config['archive']['format'] : 'zip'; $endpoint = isset($this->config['archive']['prefix-url']) ? $this->config['archive']['prefix-url'] : $this->config['homepage']; $includeArchiveChecksum = isset($this->config['archive']['checksum']) ? (bool) $this->config['archive']['checksum'] : true; $composerConfig = Factory::createConfig(); $factory = new Factory(); $io = new ConsoleIO($this->input, $this->output, $this->helperSet); $io->loadConfiguration($composerConfig); /* @var \Composer\Downloader\DownloadManager $downloadManager */ $downloadManager = $factory->createDownloadManager($io, $composerConfig); /* @var \Composer\Package\Archiver\ArchiveManager $archiveManager */ $archiveManager = $factory->createArchiveManager($composerConfig, $downloadManager); $archiveManager->setOverwriteFiles(false); shuffle($packages); /* @var \Composer\Package\CompletePackage $package */ foreach ($packages as $package) { if ($helper->isSkippable($package)) { continue; } $this->output->writeln(sprintf("<info>Dumping '%s'.</info>", $package->getName())); try { if ('pear-library' === $package->getType()) { // PEAR packages are archives already $filesystem = new Filesystem(); $packageName = $archiveManager->getPackageFilename($package); $path = realpath($directory) . '/' . $packageName . '.' . pathinfo($package->getDistUrl(), PATHINFO_EXTENSION); if (!file_exists($path)) { $downloadDir = sys_get_temp_dir() . '/composer_archiver/' . $packageName; $filesystem->ensureDirectoryExists($downloadDir); $downloadManager->download($package, $downloadDir, false); $filesystem->ensureDirectoryExists($directory); $filesystem->rename($downloadDir . '/' . pathinfo($package->getDistUrl(), PATHINFO_BASENAME), $path); $filesystem->removeDirectory($downloadDir); } // Set archive format to `file` to tell composer to download it as is $archiveFormat = 'file'; } else { $path = $archiveManager->archive($package, $format, $directory); $archiveFormat = $format; } $archive = basename($path); $distUrl = sprintf('%s/%s/%s', $endpoint, $this->config['archive']['directory'], $archive); $package->setDistType($archiveFormat); $package->setDistUrl($distUrl); if ($includeArchiveChecksum) { $package->setDistSha1Checksum(hash_file('sha1', $path)); } $package->setDistReference($package->getSourceReference()); } catch (\Exception $exception) { if (!$this->skipErrors) { throw $exception; } $this->output->writeln(sprintf("<error>Skipping Exception '%s'.</error>", $exception->getMessage())); } } }
/** * @param PackageInterface $initial * @param PackageInterface $target */ protected function updateCode(PackageInterface $initial, PackageInterface $target) { $initialDownloadPath = $this->getInstallPath($initial); $targetDownloadPath = $this->getInstallPath($target); if ($targetDownloadPath !== $initialDownloadPath) { // if the target and initial dirs intersect, we force a remove + install // to avoid the rename wiping the target dir as part of the initial dir cleanup if (substr($initialDownloadPath, 0, strlen($targetDownloadPath)) === $targetDownloadPath || substr($targetDownloadPath, 0, strlen($initialDownloadPath)) === $initialDownloadPath) { $this->removeCode($initial); $this->installCode($target); return; } $this->filesystem->rename($initialDownloadPath, $targetDownloadPath); } $this->downloadManager->update($initial, $target, $targetDownloadPath); }
/** * Dumps runonce file to the target file given in the constructor. * * @throws \RuntimeException If the combined runonce file exists but is not writable. * * @return void */ public function dump() { if (empty($this->files)) { return; } $buffer = <<<'PHP' <?php $runonce = function(array $files, $delete = false) { foreach ($files as $file) { try { include $file; } catch (\Exception $e) {} $relpath = str_replace(TL_ROOT . DIRECTORY_SEPARATOR, '', $file); if ($delete && !unlink($file)) { throw new \Exception( 'The file ' . $relpath . ' cannot be deleted. ' . 'Please remove the file manually and correct the file permission settings on your server.' ); } } }; PHP; if (file_exists($this->targetFile)) { if (!is_writable($this->targetFile) || !is_file($this->targetFile)) { throw new \RuntimeException(sprintf('Runonce file "%s" exists but is not writable.', $this->targetFile)); } $current = str_replace('.php', '_' . substr(md5(mt_rand()), 0, 8) . '.php', $this->targetFile); $this->filesystem->rename($this->targetFile, $current); $buffer .= "\n\$runonce(array('" . $current . "'), true)\n"; } $buffer .= "\n\$runonce(" . var_export(array_unique($this->files), true) . ");\n"; $this->filesystem->ensureDirectoryExists(dirname($this->targetFile)); if (false === file_put_contents($this->targetFile, $buffer)) { throw new \RuntimeException(sprintf('Could not write runonce file to "%s"', $this->targetFile)); } $this->files = []; }
/** * Writes includes JSON Files. * * @param array $packages List of packages to dump * * @return array Definition of "includes" block for packages.json */ private function dumpPackageIncludeJson(array $packages) { $repo = array('packages' => array()); $dumper = new ArrayDumper(); foreach ($packages as $package) { $repo['packages'][$package->getName()][$package->getPrettyVersion()] = $dumper->dump($package); } // dump to temporary file $tempFilename = $this->outputDir . '/$include.json'; $repoJson = new JsonFile($tempFilename); $repoJson->write($repo); // rename file accordingly $includeFileHash = hash_file('sha1', $tempFilename); $includeFileName = str_replace('{sha1}', $includeFileHash, $this->includeFileName); $fs = new Filesystem(); $fs->ensureDirectoryExists(dirname($this->outputDir . '/' . $includeFileName)); $fs->rename($tempFilename, $this->outputDir . '/' . $includeFileName); $this->output->writeln("<info>Wrote packages json {$includeFileName}</info>"); $includes = array($includeFileName => array('sha1' => $includeFileHash)); return $includes; }
/** * Create an archive of the specified package. * * @param PackageInterface $package The package to archive * @param string $format The format of the archive (zip, tar, ...) * @param string $targetDir The directory where to build the archive * @param string|null $fileName The relative file name to use for the archive, or null to generate * the package name. Note that the format will be appended to this name * @throws \InvalidArgumentException * @throws \RuntimeException * @return string The path of the created archive */ public function archive(PackageInterface $package, $format, $targetDir, $fileName = null) { if (empty($format)) { throw new \InvalidArgumentException('Format must be specified'); } // Search for the most appropriate archiver $usableArchiver = null; foreach ($this->archivers as $archiver) { if ($archiver->supports($format, $package->getSourceType())) { $usableArchiver = $archiver; break; } } // Checks the format/source type are supported before downloading the package if (null === $usableArchiver) { throw new \RuntimeException(sprintf('No archiver found to support %s format', $format)); } $filesystem = new Filesystem(); if (null === $fileName) { $packageName = $this->getPackageFilename($package); } else { $packageName = $fileName; } // Archive filename $filesystem->ensureDirectoryExists($targetDir); $target = realpath($targetDir) . '/' . $packageName . '.' . $format; $filesystem->ensureDirectoryExists(dirname($target)); if (!$this->overwriteFiles && file_exists($target)) { return $target; } if ($package instanceof RootPackageInterface) { $sourcePath = realpath('.'); } else { // Directory used to download the sources $sourcePath = sys_get_temp_dir() . '/composer_archive' . uniqid(); $filesystem->ensureDirectoryExists($sourcePath); // Download sources $this->downloadManager->download($package, $sourcePath); // Check exclude from downloaded composer.json if (file_exists($composerJsonPath = $sourcePath . '/composer.json')) { $jsonFile = new JsonFile($composerJsonPath); $jsonData = $jsonFile->read(); if (!empty($jsonData['archive']['exclude'])) { $package->setArchiveExcludes($jsonData['archive']['exclude']); } } } // Create the archive $tempTarget = sys_get_temp_dir() . '/composer_archive' . uniqid() . '.' . $format; $filesystem->ensureDirectoryExists(dirname($tempTarget)); $archivePath = $usableArchiver->archive($sourcePath, $tempTarget, $format, $package->getArchiveExcludes()); $filesystem->rename($archivePath, $target); // cleanup temporary download if (!$package instanceof RootPackageInterface) { $filesystem->removeDirectory($sourcePath); } $filesystem->remove($tempTarget); return $target; }
/** * @param array $config Directory where to create the downloads in, prefix-url, etc.. * @param array $packages * @param InputInterface $input * @param OutputInterface $output * @param string $outputDir * @param bool $skipErrors If true, any exception while dumping a package will be ignored. * * @return void */ private function dumpDownloads(array $config, array $packages, InputInterface $input, OutputInterface $output, $outputDir, $skipErrors) { if (isset($config['archive']['absolute-directory'])) { $directory = $config['archive']['absolute-directory']; } else { $directory = sprintf('%s/%s', $outputDir, $config['archive']['directory']); } $output->writeln(sprintf("<info>Creating local downloads in '%s'</info>", $directory)); $format = isset($config['archive']['format']) ? $config['archive']['format'] : 'zip'; $endpoint = isset($config['archive']['prefix-url']) ? $config['archive']['prefix-url'] : $config['homepage']; $skipDev = isset($config['archive']['skip-dev']) ? (bool) $config['archive']['skip-dev'] : false; $whitelist = isset($config['archive']['whitelist']) ? (array) $config['archive']['whitelist'] : array(); $blacklist = isset($config['archive']['blacklist']) ? (array) $config['archive']['blacklist'] : array(); $includeArchiveChecksum = isset($config['archive']['checksum']) ? (bool) $config['archive']['checksum'] : true; $composerConfig = Factory::createConfig(); $factory = new Factory(); $io = new ConsoleIO($input, $output, $this->getApplication()->getHelperSet()); $io->loadConfiguration($composerConfig); /* @var \Composer\Downloader\DownloadManager $downloadManager */ $downloadManager = $factory->createDownloadManager($io, $composerConfig); /* @var \Composer\Package\Archiver\ArchiveManager $archiveManager */ $archiveManager = $factory->createArchiveManager($composerConfig, $downloadManager); $archiveManager->setOverwriteFiles(false); shuffle($packages); /* @var \Composer\Package\CompletePackage $package */ foreach ($packages as $package) { if ('metapackage' === $package->getType()) { continue; } $name = $package->getName(); if (true === $skipDev && true === $package->isDev()) { $output->writeln(sprintf("<info>Skipping '%s' (is dev)</info>", $name)); continue; } $names = $package->getNames(); if ($whitelist && !array_intersect($whitelist, $names)) { $output->writeln(sprintf("<info>Skipping '%s' (is not in whitelist)</info>", $name)); continue; } if ($blacklist && array_intersect($blacklist, $names)) { $output->writeln(sprintf("<info>Skipping '%s' (is in blacklist)</info>", $name)); continue; } $output->writeln(sprintf("<info>Dumping '%s'.</info>", $name)); try { if ('pear-library' === $package->getType()) { // PEAR packages are archives already $filesystem = new Filesystem(); $packageName = $archiveManager->getPackageFilename($package); $path = realpath($directory) . '/' . $packageName . '.' . pathinfo($package->getDistUrl(), PATHINFO_EXTENSION); if (!file_exists($path)) { $downloadDir = sys_get_temp_dir() . '/composer_archiver/' . $packageName; $filesystem->ensureDirectoryExists($downloadDir); $downloadManager->download($package, $downloadDir, false); $filesystem->ensureDirectoryExists($directory); $filesystem->rename($downloadDir . '/' . pathinfo($package->getDistUrl(), PATHINFO_BASENAME), $path); $filesystem->removeDirectory($downloadDir); } // Set archive format to `file` to tell composer to download it as is $archiveFormat = 'file'; } else { $path = $archiveManager->archive($package, $format, $directory); $archiveFormat = $format; } $archive = basename($path); $distUrl = sprintf('%s/%s/%s', $endpoint, $config['archive']['directory'], $archive); $package->setDistType($archiveFormat); $package->setDistUrl($distUrl); if ($includeArchiveChecksum) { $package->setDistSha1Checksum(hash_file('sha1', $path)); } $package->setDistReference($package->getSourceReference()); } catch (\Exception $exception) { if (!$skipErrors) { throw $exception; } $output->writeln(sprintf("<error>Skipping Exception '%s'.</error>", $exception->getMessage())); } } }
/** * {@inheritdoc} */ public function dump(array $packages) { $helper = new ArchiveBuilderHelper($this->output, $this->config['archive']); $basedir = $helper->getDirectory($this->outputDir); $this->output->writeln(sprintf("<info>Creating local downloads in '%s'</info>", $basedir)); $format = isset($this->config['archive']['format']) ? $this->config['archive']['format'] : 'zip'; $endpoint = isset($this->config['archive']['prefix-url']) ? $this->config['archive']['prefix-url'] : $this->config['homepage']; $includeArchiveChecksum = isset($this->config['archive']['checksum']) ? (bool) $this->config['archive']['checksum'] : true; $composerConfig = $this->composer->getConfig(); $factory = new Factory(); /* @var \Composer\Downloader\DownloadManager $downloadManager */ $downloadManager = $this->composer->getDownloadManager(); /* @var \Composer\Package\Archiver\ArchiveManager $archiveManager */ $archiveManager = $factory->createArchiveManager($composerConfig, $downloadManager); $archiveManager->setOverwriteFiles(false); shuffle($packages); $progressBar = null; $hasStarted = false; $verbosity = $this->output->getVerbosity(); $renderProgress = $this->input->getOption('stats') && OutputInterface::VERBOSITY_NORMAL == $verbosity; if ($renderProgress) { $packageCount = 0; foreach ($packages as $package) { if (!$helper->isSkippable($package)) { ++$packageCount; } } $progressBar = new ProgressBar($this->output, $packageCount); $progressBar->setFormat(' %current%/%max% [%bar%] %percent:3s%% - Installing %packageName% (%packageVersion%)'); } /* @var \Composer\Package\CompletePackage $package */ foreach ($packages as $package) { if ($helper->isSkippable($package)) { continue; } if ($renderProgress) { $progressBar->setMessage($package->getName(), 'packageName'); $progressBar->setMessage($package->getPrettyVersion(), 'packageVersion'); if (!$hasStarted) { $progressBar->start(); $hasStarted = true; } else { $progressBar->display(); } } else { $this->output->writeln(sprintf("<info>Dumping '%s'.</info>", $package->getName())); } try { if ($renderProgress) { $this->output->setVerbosity(OutputInterface::VERBOSITY_QUIET); } $intermediatePath = preg_replace('#[^a-z0-9-_/]#i', '-', $package->getName()); $packageName = $archiveManager->getPackageFilename($package); if ('pear-library' === $package->getType()) { // PEAR packages are archives already $filesystem = new Filesystem(); $path = sprintf('%s/%s/%s.%s', realpath($basedir), $intermediatePath, $packageName, pathinfo($package->getDistUrl(), PATHINFO_EXTENSION)); if (!file_exists($path)) { $downloadDir = sys_get_temp_dir() . '/composer_archiver/' . $packageName; $filesystem->ensureDirectoryExists($downloadDir); $downloadManager->download($package, $downloadDir, false); $filesystem->ensureDirectoryExists(dirname($path)); $filesystem->rename($downloadDir . '/' . pathinfo($package->getDistUrl(), PATHINFO_BASENAME), $path); $filesystem->removeDirectory($downloadDir); } // Set archive format to `file` to tell composer to download it as is $archiveFormat = 'file'; } else { $path = $archiveManager->archive($package, $format, sprintf('%s/%s', $basedir, $intermediatePath)); $archiveFormat = $format; } $archive = basename($path); $distUrl = sprintf('%s/%s/%s/%s', $endpoint, $this->config['archive']['directory'], $intermediatePath, $archive); $package->setDistType($archiveFormat); $package->setDistUrl($distUrl); if ($includeArchiveChecksum) { $package->setDistSha1Checksum(hash_file('sha1', $path)); } $package->setDistReference($package->getSourceReference()); if ($renderProgress) { $this->output->setVerbosity($verbosity); } } catch (\Exception $exception) { if ($renderProgress) { $this->output->setVerbosity($verbosity); } if (!$this->skipErrors) { throw $exception; } $this->output->writeln(sprintf("<error>Skipping Exception '%s'.</error>", $exception->getMessage())); } if ($renderProgress) { $progressBar->advance(); } } if ($renderProgress) { $progressBar->finish(); $this->output->writeln(''); } }