cli_nodejs.go 6.6 KB

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