cli_nodejs.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. package buildpacks
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "sync"
  7. noderunscript "github.com/paketo-buildpacks/node-run-script"
  8. nodestart "github.com/paketo-buildpacks/node-start"
  9. npminstall "github.com/paketo-buildpacks/npm-install"
  10. "github.com/paketo-buildpacks/packit"
  11. yarninstall "github.com/paketo-buildpacks/yarn-install"
  12. "github.com/pelletier/go-toml"
  13. )
  14. const nodejsTomlFile = "nodejs.buildpack.toml"
  15. type cliNodeRuntime struct {
  16. packs map[string]*BuildpackInfo
  17. wg sync.WaitGroup
  18. }
  19. func NewCLINodeRuntime() *cliNodeRuntime {
  20. packs := make(map[string]*BuildpackInfo)
  21. buildpackToml, err := toml.LoadFile(filepath.Join(getExecPath(), nodejsTomlFile))
  22. if err != nil {
  23. fmt.Printf("Error while reading %s: %v\n", nodejsTomlFile, err)
  24. os.Exit(1)
  25. }
  26. order := buildpackToml.Get("order").([]*toml.Tree)
  27. // yarn
  28. packs[yarn] = newBuildpackInfo()
  29. yarnGroup := order[0].GetArray("group").([]*toml.Tree)
  30. for i := 0; i < len(yarnGroup); i++ {
  31. packs[yarn].addPack(
  32. buildpackOrderGroupInfo{
  33. ID: yarnGroup[i].Get("id").(string),
  34. Optional: yarnGroup[i].GetDefault("optional", false).(bool),
  35. Version: yarnGroup[i].Get("version").(string),
  36. },
  37. )
  38. }
  39. packs[yarn].addEnvVar("SSL_CERT_DIR", "")
  40. packs[yarn].addEnvVar("SSL_CERT_FILE", "")
  41. packs[yarn].addEnvVar("BP_NODE_OPTIMIZE_MEMORY", "")
  42. packs[yarn].addEnvVar("BP_NODE_PROJECT_PATH", "")
  43. packs[yarn].addEnvVar("BP_NODE_VERSION", "")
  44. packs[yarn].addEnvVar("BP_NODE_RUN_SCRIPTS", "")
  45. // npm
  46. packs[npm] = newBuildpackInfo()
  47. npmGroup := order[1].GetArray("group").([]*toml.Tree)
  48. for i := 0; i < len(npmGroup); i++ {
  49. packs[npm].addPack(
  50. buildpackOrderGroupInfo{
  51. ID: npmGroup[i].Get("id").(string),
  52. Optional: npmGroup[i].GetDefault("optional", false).(bool),
  53. Version: npmGroup[i].Get("version").(string),
  54. },
  55. )
  56. }
  57. packs[npm].addEnvVar("SSL_CERT_DIR", "")
  58. packs[npm].addEnvVar("SSL_CERT_FILE", "")
  59. packs[npm].addEnvVar("BP_NODE_OPTIMIZE_MEMORY", "")
  60. packs[npm].addEnvVar("BP_NODE_PROJECT_PATH", "")
  61. packs[npm].addEnvVar("BP_NODE_VERSION", "")
  62. packs[npm].addEnvVar("BP_NODE_RUN_SCRIPTS", "")
  63. // no package manager
  64. packs[standalone] = newBuildpackInfo()
  65. standaloneGroup := order[2].GetArray("group").([]*toml.Tree)
  66. for i := 0; i < len(standaloneGroup); i++ {
  67. packs[standalone].addPack(
  68. buildpackOrderGroupInfo{
  69. ID: standaloneGroup[i].Get("id").(string),
  70. Optional: standaloneGroup[i].GetDefault("optional", false).(bool),
  71. Version: standaloneGroup[i].Get("version").(string),
  72. },
  73. )
  74. }
  75. packs[standalone].addEnvVar("SSL_CERT_DIR", "")
  76. packs[standalone].addEnvVar("SSL_CERT_FILE", "")
  77. packs[standalone].addEnvVar("BP_NODE_OPTIMIZE_MEMORY", "")
  78. packs[standalone].addEnvVar("BP_NODE_PROJECT_PATH", "")
  79. packs[standalone].addEnvVar("BP_NODE_VERSION", "")
  80. packs[standalone].addEnvVar("BP_LAUNCHPOINT", "")
  81. packs[standalone].addEnvVar("BP_LIVE_RELOAD_ENABLED", "")
  82. return &cliNodeRuntime{
  83. packs: packs,
  84. }
  85. }
  86. func (runtime *cliNodeRuntime) detectYarn(results chan struct {
  87. string
  88. bool
  89. }, workingDir string) {
  90. yarnProjectPathParser := yarninstall.NewProjectPathParser()
  91. yarnVersionParser := yarninstall.NewPackageJSONParser()
  92. detect := yarninstall.Detect(yarnProjectPathParser, yarnVersionParser)
  93. _, err := detect(packit.DetectContext{
  94. WorkingDir: workingDir,
  95. })
  96. if err == nil {
  97. results <- struct {
  98. string
  99. bool
  100. }{yarn, true}
  101. } else {
  102. results <- struct {
  103. string
  104. bool
  105. }{yarn, false}
  106. }
  107. runtime.wg.Done()
  108. }
  109. func (runtime *cliNodeRuntime) detectNPM(results chan struct {
  110. string
  111. bool
  112. }, workingDir string) {
  113. npmProjectPathParser := npminstall.NewProjectPathParser()
  114. npmVersionParser := npminstall.NewPackageJSONParser()
  115. detect := npminstall.Detect(npmProjectPathParser, npmVersionParser)
  116. _, err := detect(packit.DetectContext{
  117. WorkingDir: workingDir,
  118. })
  119. if err == nil {
  120. results <- struct {
  121. string
  122. bool
  123. }{npm, true}
  124. } else {
  125. results <- struct {
  126. string
  127. bool
  128. }{npm, false}
  129. }
  130. runtime.wg.Done()
  131. }
  132. func (runtime *cliNodeRuntime) detectStandalone(results chan struct {
  133. string
  134. bool
  135. }, workingDir string) {
  136. appFinder := nodestart.NewNodeApplicationFinder()
  137. detect := nodestart.Detect(appFinder)
  138. _, err := detect(packit.DetectContext{
  139. WorkingDir: workingDir,
  140. })
  141. if err == nil {
  142. results <- struct {
  143. string
  144. bool
  145. }{standalone, true}
  146. } else {
  147. results <- struct {
  148. string
  149. bool
  150. }{standalone, false}
  151. }
  152. runtime.wg.Done()
  153. }
  154. func (runtime *cliNodeRuntime) Detect(workingDir string) (BuildpackInfo, map[string]interface{}) {
  155. results := make(chan struct {
  156. string
  157. bool
  158. }, 3)
  159. runtime.wg.Add(3)
  160. go runtime.detectYarn(results, workingDir)
  161. go runtime.detectNPM(results, workingDir)
  162. go runtime.detectStandalone(results, workingDir)
  163. runtime.wg.Wait()
  164. close(results)
  165. atLeastOne := false
  166. detected := make(map[string]bool)
  167. for result := range results {
  168. if result.bool {
  169. atLeastOne = true
  170. }
  171. detected[result.string] = result.bool
  172. }
  173. if atLeastOne {
  174. if detected[yarn] || detected[npm] {
  175. // it is safe to assume that the project contains a package.json
  176. packageJSONPath := filepath.Join(workingDir, "package.json")
  177. scriptManager := noderunscript.NewScriptManager()
  178. scripts, err := scriptManager.GetPackageScripts(workingDir)
  179. if err != nil {
  180. fmt.Printf("Error reading %s: %v\n", packageJSONPath, err)
  181. os.Exit(1)
  182. }
  183. packageJSONParser := npminstall.NewPackageJSONParser()
  184. engineVersion, err := packageJSONParser.ParseVersion(packageJSONPath)
  185. if err != nil {
  186. fmt.Printf("Error reading %s: %v\n", packageJSONPath, err)
  187. os.Exit(1)
  188. }
  189. if detected[yarn] {
  190. return *runtime.packs[yarn], map[string]interface{}{"scripts": scripts, "engine_version": engineVersion}
  191. } else {
  192. return *runtime.packs[npm], map[string]interface{}{"scripts": scripts, "engine_version": engineVersion}
  193. }
  194. }
  195. if detected[standalone] {
  196. return *runtime.packs[standalone], map[string]interface{}{}
  197. }
  198. }
  199. return BuildpackInfo{}, nil
  200. }