nodejs.go 5.9 KB

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