PageRenderTime 42ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/library/setup/ComposerHelper.php

http://github.com/vanillaforums/Garden
PHP | 206 lines | 116 code | 32 blank | 58 comment | 14 complexity | 2116e36c7be9d985f9132d860a857d98 MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0, AGPL-1.0, BSD-3-Clause, MIT
  1. <?php
  2. /**
  3. * @author Todd Burry <todd@vanillaforums.com>
  4. * @copyright 2009-2019 Vanilla Forums Inc.
  5. * @license GPL-2.0-only
  6. */
  7. namespace Vanilla\Setup;
  8. use Composer\Script\Event;
  9. use Composer\Factory;
  10. /**
  11. * Contains helper methods for Vanilla's composer integration.
  12. */
  13. class ComposerHelper {
  14. const NODE_ARGS_ENV = "VANILLA_BUILD_NODE_ARGS";
  15. const DISABLE_VALIDATION_ENV = "VANILLA_BUILD_DISABLE_CODE_VALIDATION";
  16. const LOW_MEMORY_ENV = "VANILLA_BUILD_LOW_MEMORY";
  17. const DISABLE_AUTO_BUILD = "VANILLA_BUILD_DISABLE_AUTO_BUILD";
  18. /**
  19. * Clear the addon manager cache.
  20. */
  21. private static function clearAddonManagerCache() {
  22. $cacheDir = realpath(__DIR__.'/../../cache');
  23. $paths = array_merge(
  24. [$cacheDir.'/addon.php', $cacheDir.'/openapi.php'],
  25. glob($cacheDir.'/locale/*.php'),
  26. glob($cacheDir.'/theme/*.php'),
  27. glob($cacheDir.'/*-index.php')
  28. );
  29. foreach ($paths as $path) {
  30. if (file_exists($path)) {
  31. unlink($path);
  32. }
  33. }
  34. }
  35. /**
  36. * Clear the twig cache.
  37. */
  38. private static function clearTwigCache() {
  39. $cacheDir = realpath(__DIR__.'/../../cache');
  40. // Clear twig cache if it exists.
  41. $twigCache = $cacheDir . '/twig';
  42. if (file_exists($twigCache)) {
  43. self::deleteRecursively($twigCache);
  44. }
  45. // Due to a previous bug, the twig cache may have lived in the conf directory.
  46. if (file_exists($twigCache)) {
  47. self::deleteRecursively($twigCache);
  48. }
  49. }
  50. /**
  51. * Recursively delete a directory.
  52. *
  53. * @param string $root
  54. */
  55. private static function deleteRecursively(string $root) {
  56. $files = new \RecursiveIteratorIterator(
  57. new \RecursiveDirectoryIterator($root, \RecursiveDirectoryIterator::SKIP_DOTS),
  58. \RecursiveIteratorIterator::CHILD_FIRST
  59. );
  60. foreach ($files as $fileinfo) {
  61. $deleteFunction = ($fileinfo->isDir() ? 'rmdir' : 'unlink');
  62. $deleteFunction($fileinfo->getRealPath());
  63. }
  64. // Final directory delete.
  65. rmdir($root);
  66. }
  67. /**
  68. * Trigger builds of frontend assets after a composer install.
  69. *
  70. * - Installs node_modules
  71. * - Builds frontend assets
  72. *
  73. * There are some environmental variables that can alter the way this command run.
  74. *
  75. * - VANILLA_BUILD_NODE_ARGS - Arguments to pass to the node process.
  76. * "--max-old-space-size" in particular can be used to set a memory limit.
  77. * - VANILLA_BUILD_DISABLE_CODE_VALIDATION - Disables type-checking and linting. This speeds up the build and
  78. * reduces memory usage.
  79. * - VANILLA_BUILD_DISABLE_AUTO_BUILD - Prevent the build from running on composer install.
  80. */
  81. public static function postUpdate() {
  82. $vanillaRoot = realpath(__DIR__ . "/../../");
  83. $skipBuild = getenv(self::DISABLE_AUTO_BUILD) === 'true';
  84. if ($skipBuild) {
  85. printf("\nSkipping automatic JS build because " . self::DISABLE_AUTO_BUILD . " env variable is set to \"true\".\n");
  86. return;
  87. }
  88. printf("\nInstalling core node_modules\n");
  89. // --ignore-engines is used until https://github.com/vanilla/dev-inter-ops/issues/38 is resolved.
  90. // Node 10.11.0 is run there and our linter has an engine requirement of 10.13.0
  91. // We don't even run the linter as part of this process.
  92. // It even technically works but many packages that support node 10 only want to support the LTS version (10.13.x).
  93. passthru('yarn install --pure-lockfile --ignore-engines', $installReturn);
  94. // Generate our vendor license file.
  95. $distDir = $vanillaRoot . '/dist';
  96. $licensePath = $distDir . '/VENDOR_LICENSES.txt';
  97. if (!file_exists($distDir)) {
  98. mkdir($distDir);
  99. }
  100. printf("\nGererating Vendor Licenses for build\n");
  101. passthru("yarn licenses generate-disclaimer --prod --ignore-engines > $licensePath");
  102. if ($installReturn !== 0) {
  103. printf("Installing core node_modules failed\n");
  104. exit($installReturn);
  105. }
  106. $buildScript = realpath($vanillaRoot . "/build/scripts/build.ts");
  107. $tsNodeRegister = realpath($vanillaRoot . "/node_modules/ts-node/register");
  108. $tsConfig = realpath($vanillaRoot . "/build/tsconfig.json");
  109. // Build bootstrap can be used to configure this build if env variables are not available.
  110. $buildBootstrap = realpath($vanillaRoot . "/conf/build-bootstrap.php");
  111. if (file_exists($buildBootstrap)) {
  112. include $buildBootstrap;
  113. }
  114. $nodeArgs = getenv(self::NODE_ARGS_ENV) ?: "";
  115. // The disable validation flag was used to enable low memory optimizations.
  116. // The build no longer does any validation, however, so a new env variable has been added.
  117. // So, we check for both.
  118. $lowMemoryFlag = getenv(self::DISABLE_VALIDATION_ENV) || getenv(self::LOW_MEMORY_ENV) ? "--low-memory" : "";
  119. $buildCommand = "TS_NODE_PROJECT=$tsConfig node $nodeArgs -r $tsNodeRegister $buildScript -i $lowMemoryFlag";
  120. printf("\nBuilding frontend assets\n");
  121. printf("\n$buildCommand\n");
  122. system($buildCommand, $buildResult);
  123. if ($buildResult !== 0) {
  124. printf("The build failed with code $buildResult");
  125. exit($buildResult);
  126. }
  127. }
  128. /**
  129. * Merge repositories and requirements from a separate composer-local.json.
  130. *
  131. * This allows static development dependencies to be shipped with Vanilla, but can be customized with a
  132. * composer-local.json file that specifies additional dependencies such as plugins or compatibility libraries.
  133. *
  134. * @param Event $event The event being fired.
  135. */
  136. public static function preUpdate(Event $event) {
  137. self::clearAddonManagerCache();
  138. self::clearTwigCache();
  139. // Check for a composer-local.json.
  140. $composerLocalPath = './composer-local.json';
  141. if (!file_exists($composerLocalPath)) {
  142. return;
  143. }
  144. $composer = $event->getComposer();
  145. $factory = new Factory();
  146. $localComposer = $factory->createComposer(
  147. $event->getIO(),
  148. $composerLocalPath,
  149. true,
  150. null,
  151. false
  152. );
  153. // Merge repositories.
  154. $localRepositories = $localComposer->getRepositoryManager()->getRepositories();
  155. foreach ($localRepositories as $repository) {
  156. /* @var \Composer\Repository\RepositoryInterface $repository */
  157. if (method_exists($repository, 'getRepoConfig')) {
  158. $config = $repository->getRepoConfig();
  159. } else {
  160. $config = ['url' => ''];
  161. }
  162. // Skip the packagist repo.
  163. if (strpos($config['url'], 'packagist.org') !== false) {
  164. continue;
  165. }
  166. $composer->getRepositoryManager()->addRepository($repository);
  167. }
  168. // Merge requirements.
  169. $requires = array_merge($composer->getPackage()->getRequires(), $localComposer->getPackage()->getRequires());
  170. $composer->getPackage()->setRequires($requires);
  171. $devRequires = array_merge($composer->getPackage()->getDevRequires(), $localComposer->getPackage()->getDevRequires());
  172. $composer->getPackage()->setDevRequires($devRequires);
  173. }
  174. }