FossilDriver.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. <?php
  2. /*
  3. * This file is part of Composer.
  4. *
  5. * (c) Nils Adermann <naderman@naderman.de>
  6. * Jordi Boggiano <j.boggiano@seld.be>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. namespace Composer\Repository\Vcs;
  12. use Composer\Cache;
  13. use Composer\Config;
  14. use Composer\Util\ProcessExecutor;
  15. use Composer\Util\Filesystem;
  16. use Composer\IO\IOInterface;
  17. /**
  18. * @author BohwaZ <http://bohwaz.net/>
  19. */
  20. class FossilDriver extends VcsDriver
  21. {
  22. protected $tags;
  23. protected $branches;
  24. protected $rootIdentifier;
  25. protected $repoFile;
  26. protected $checkoutDir;
  27. protected $infoCache = array();
  28. /**
  29. * {@inheritDoc}
  30. */
  31. public function initialize()
  32. {
  33. // Make sure fossil is installed and reachable.
  34. $this->checkFossil();
  35. // Ensure we are allowed to use this URL by config.
  36. $this->config->prohibitUrlByConfig($this->url, $this->io);
  37. // Only if url points to a locally accessible directory, assume it's the checkout directory.
  38. // Otherwise, it should be something fossil can clone from.
  39. if (Filesystem::isLocalPath($this->url) && is_dir($this->url)) {
  40. $this->checkoutDir = $this->url;
  41. } else {
  42. if (!Cache::isUsable($this->config->get('cache-repo-dir')) || !Cache::isUsable($this->config->get('cache-vcs-dir'))) {
  43. throw new \RuntimeException('FossilDriver requires a usable cache directory, and it looks like you set it to be disabled');
  44. }
  45. $localName = preg_replace('{[^a-z0-9]}i', '-', $this->url);
  46. $this->repoFile = $this->config->get('cache-repo-dir') . '/' . $localName . '.fossil';
  47. $this->checkoutDir = $this->config->get('cache-vcs-dir') . '/' . $localName . '/';
  48. $this->updateLocalRepo();
  49. }
  50. $this->getTags();
  51. $this->getBranches();
  52. }
  53. /**
  54. * Check that fossil can be invoked via command line.
  55. */
  56. protected function checkFossil()
  57. {
  58. if (0 !== $this->process->execute('fossil version', $ignoredOutput)) {
  59. throw new \RuntimeException("fossil was not found, check that it is installed and in your PATH env.\n\n" . $this->process->getErrorOutput());
  60. }
  61. }
  62. /**
  63. * Clone or update existing local fossil repository.
  64. */
  65. protected function updateLocalRepo()
  66. {
  67. $fs = new Filesystem();
  68. $fs->ensureDirectoryExists($this->checkoutDir);
  69. if (!is_writable(dirname($this->checkoutDir))) {
  70. throw new \RuntimeException('Can not clone '.$this->url.' to access package information. The "'.$this->checkoutDir.'" directory is not writable by the current user.');
  71. }
  72. // update the repo if it is a valid fossil repository
  73. if (is_file($this->repoFile) && is_dir($this->checkoutDir) && 0 === $this->process->execute('fossil info', $output, $this->checkoutDir)) {
  74. if (0 !== $this->process->execute('fossil pull', $output, $this->checkoutDir)) {
  75. $this->io->writeError('<error>Failed to update '.$this->url.', package information from this repository may be outdated ('.$this->process->getErrorOutput().')</error>');
  76. }
  77. } else {
  78. // clean up directory and do a fresh clone into it
  79. $fs->removeDirectory($this->checkoutDir);
  80. $fs->remove($this->repoFile);
  81. $fs->ensureDirectoryExists($this->checkoutDir);
  82. if (0 !== $this->process->execute(sprintf('fossil clone %s %s', ProcessExecutor::escape($this->url), ProcessExecutor::escape($this->repoFile)), $output)) {
  83. $output = $this->process->getErrorOutput();
  84. throw new \RuntimeException('Failed to clone '.$this->url.' to repository ' . $this->repoFile . "\n\n" .$output);
  85. }
  86. if (0 !== $this->process->execute(sprintf('fossil open %s --nested', ProcessExecutor::escape($this->repoFile)), $output, $this->checkoutDir)) {
  87. $output = $this->process->getErrorOutput();
  88. throw new \RuntimeException('Failed to open repository '.$this->repoFile.' in ' . $this->checkoutDir . "\n\n" .$output);
  89. }
  90. }
  91. }
  92. /**
  93. * {@inheritDoc}
  94. */
  95. public function getRootIdentifier()
  96. {
  97. if (null === $this->rootIdentifier) {
  98. $this->rootIdentifier = 'trunk';
  99. }
  100. return $this->rootIdentifier;
  101. }
  102. /**
  103. * {@inheritDoc}
  104. */
  105. public function getUrl()
  106. {
  107. return $this->url;
  108. }
  109. /**
  110. * {@inheritDoc}
  111. */
  112. public function getSource($identifier)
  113. {
  114. return array('type' => 'fossil', 'url' => $this->getUrl(), 'reference' => $identifier);
  115. }
  116. /**
  117. * {@inheritDoc}
  118. */
  119. public function getDist($identifier)
  120. {
  121. return null;
  122. }
  123. /**
  124. * {@inheritdoc}
  125. */
  126. public function getFileContent($file, $identifier)
  127. {
  128. $command = sprintf('fossil cat -r %s %s', ProcessExecutor::escape($identifier), ProcessExecutor::escape($file));
  129. $this->process->execute($command, $content, $this->checkoutDir);
  130. if (!trim($content)) {
  131. return null;
  132. }
  133. return $content;
  134. }
  135. /**
  136. * {@inheritdoc}
  137. */
  138. public function getChangeDate($identifier)
  139. {
  140. $this->process->execute('fossil finfo -b -n 1 composer.json', $output, $this->checkoutDir);
  141. list($ckout, $date, $message) = explode(' ', trim($output), 3);
  142. return new \DateTime($date, new \DateTimeZone('UTC'));
  143. }
  144. /**
  145. * {@inheritDoc}
  146. */
  147. public function getTags()
  148. {
  149. if (null === $this->tags) {
  150. $tags = array();
  151. $this->process->execute('fossil tag list', $output, $this->checkoutDir);
  152. foreach ($this->process->splitLines($output) as $tag) {
  153. $tags[$tag] = $tag;
  154. }
  155. $this->tags = $tags;
  156. }
  157. return $this->tags;
  158. }
  159. /**
  160. * {@inheritDoc}
  161. */
  162. public function getBranches()
  163. {
  164. if (null === $this->branches) {
  165. $branches = array();
  166. $bookmarks = array();
  167. $this->process->execute('fossil branch list', $output, $this->checkoutDir);
  168. foreach ($this->process->splitLines($output) as $branch) {
  169. $branch = trim(preg_replace('/^\*/', '', trim($branch)));
  170. $branches[$branch] = $branch;
  171. }
  172. $this->branches = $branches;
  173. }
  174. return $this->branches;
  175. }
  176. /**
  177. * {@inheritDoc}
  178. */
  179. public static function supports(IOInterface $io, Config $config, $url, $deep = false)
  180. {
  181. if (preg_match('#(^(?:https?|ssh)://(?:[^@]@)?(?:chiselapp\.com|fossil\.))#i', $url)) {
  182. return true;
  183. }
  184. if (preg_match('!/fossil/|\.fossil!', $url)) {
  185. return true;
  186. }
  187. // local filesystem
  188. if (Filesystem::isLocalPath($url)) {
  189. $url = Filesystem::getPlatformPath($url);
  190. if (!is_dir($url)) {
  191. return false;
  192. }
  193. $process = new ProcessExecutor($io);
  194. // check whether there is a fossil repo in that path
  195. if ($process->execute('fossil info', $output, $url) === 0) {
  196. return true;
  197. }
  198. }
  199. return false;
  200. }
  201. }