PageRenderTime 50ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/library/setup/ComposerHelper.php

https://github.com/vanilla/vanilla
PHP | 218 lines | 126 code | 32 blank | 60 comment | 15 complexity | 9b9064a8bcdf7c053a5dd5b23c088fae MD5 | raw file
Possible License(s): GPL-2.0, MIT, AGPL-1.0
  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. public static function clearAddonManagerCache() {
  22. $cacheDir = realpath(__DIR__.'/../../cache');
  23. $paths = array_merge(
  24. [$cacheDir.'/addon.php', $cacheDir.'/openapi.php', $cacheDir.'/config-schema.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. public 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. if ($installReturn !== 0) {
  95. printf("Installing core node_modules failed\n");
  96. exit($installReturn);
  97. }
  98. // Build bootstrap can be used to configure this build if env variables are not available.
  99. $buildBootstrap = realpath($vanillaRoot . "/conf/build-bootstrap.php");
  100. if (file_exists($buildBootstrap)) {
  101. include $buildBootstrap;
  102. }
  103. $buildScript = realpath($vanillaRoot . "/build/scripts/build.ts");
  104. $buildDocsScript = realpath($vanillaRoot . "/build/scripts/variables/buildVariableDocs.ts");
  105. $tsNodeRegister = realpath($vanillaRoot . "/node_modules/ts-node/register");
  106. $tsConfig = realpath($vanillaRoot . "/build/tsconfig.json");
  107. $nodeArgs = getenv(self::NODE_ARGS_ENV) ?: "";
  108. $lowMemoryFlag = getenv(self::DISABLE_VALIDATION_ENV) || getenv(self::LOW_MEMORY_ENV) ? "--low-memory" : "";
  109. // Stderr gets swalled in some environments.
  110. $stdoutRedirect = " 2>&1";
  111. // Run build
  112. $buildCommand = "TS_NODE_PROJECT=$tsConfig node $nodeArgs -r $tsNodeRegister $buildScript -i $lowMemoryFlag $stdoutRedirect";
  113. printf("\nBuilding frontend assets\n");
  114. printf("\n$buildCommand\n");
  115. system($buildCommand, $buildResult);
  116. if ($buildResult !== 0) {
  117. printf("The build failed with code $buildResult");
  118. exit($buildResult);
  119. }
  120. // Generate our vendor license file.
  121. $distDir = $vanillaRoot . '/dist';
  122. $licensePath = $distDir . '/VENDOR_LICENSES.txt';
  123. if (!file_exists($distDir)) {
  124. mkdir($distDir);
  125. }
  126. printf("\nGererating Vendor Licenses for build\n");
  127. passthru("yarn licenses generate-disclaimer --prod --ignore-engines > $licensePath");
  128. // The disable validation flag was used to enable low memory optimizations.
  129. // The build no longer does any validation, however, so a new env variable has been added.
  130. // So, we check for both.
  131. $docsCommand = "TS_NODE_PROJECT=$tsConfig node $nodeArgs -r $tsNodeRegister $buildDocsScript -i $lowMemoryFlag $stdoutRedirect";
  132. printf("\nBuilding variable documentation\n");
  133. printf("\n$docsCommand\n");
  134. system($docsCommand, $buildResult);
  135. if ($buildResult !== 0) {
  136. printf("The build failed with code $buildResult");
  137. exit($buildResult);
  138. }
  139. }
  140. /**
  141. * Merge repositories and requirements from a separate composer-local.json.
  142. *
  143. * This allows static development dependencies to be shipped with Vanilla, but can be customized with a
  144. * composer-local.json file that specifies additional dependencies such as plugins or compatibility libraries.
  145. *
  146. * @param Event $event The event being fired.
  147. */
  148. public static function preUpdate(Event $event) {
  149. self::clearAddonManagerCache();
  150. self::clearTwigCache();
  151. // Check for a composer-local.json.
  152. $composerLocalPath = './composer-local.json';
  153. if (!file_exists($composerLocalPath)) {
  154. return;
  155. }
  156. $composer = $event->getComposer();
  157. $factory = new Factory();
  158. $localComposer = $factory->createComposer(
  159. $event->getIO(),
  160. $composerLocalPath,
  161. true,
  162. null,
  163. false
  164. );
  165. // Merge repositories.
  166. $localRepositories = $localComposer->getRepositoryManager()->getRepositories();
  167. foreach ($localRepositories as $repository) {
  168. /* @var \Composer\Repository\RepositoryInterface $repository */
  169. if (method_exists($repository, 'getRepoConfig')) {
  170. $config = $repository->getRepoConfig();
  171. } else {
  172. $config = ['url' => ''];
  173. }
  174. // Skip the packagist repo.
  175. if (strpos($config['url'], 'packagist.org') !== false) {
  176. continue;
  177. }
  178. $composer->getRepositoryManager()->addRepository($repository);
  179. }
  180. // Merge requirements.
  181. $requires = array_merge($composer->getPackage()->getRequires(), $localComposer->getPackage()->getRequires());
  182. $composer->getPackage()->setRequires($requires);
  183. $devRequires = array_merge($composer->getPackage()->getDevRequires(), $localComposer->getPackage()->getDevRequires());
  184. $composer->getPackage()->setDevRequires($devRequires);
  185. }
  186. }