|
|
@@ -9,6 +9,7 @@ import (
|
|
|
|
|
|
"github.com/Masterminds/semver/v3"
|
|
|
"github.com/google/go-github/v41/github"
|
|
|
+ "github.com/xanzy/go-gitlab"
|
|
|
)
|
|
|
|
|
|
var (
|
|
|
@@ -28,7 +29,7 @@ func NewNodeRuntime() Runtime {
|
|
|
return &nodejsRuntime{}
|
|
|
}
|
|
|
|
|
|
-func (runtime *nodejsRuntime) detectYarn(results chan struct {
|
|
|
+func (runtime *nodejsRuntime) detectYarnGithub(results chan struct {
|
|
|
string
|
|
|
bool
|
|
|
}, directoryContent []*github.RepositoryContent) {
|
|
|
@@ -54,7 +55,33 @@ func (runtime *nodejsRuntime) detectYarn(results chan struct {
|
|
|
runtime.wg.Done()
|
|
|
}
|
|
|
|
|
|
-func (runtime *nodejsRuntime) detectNPM(results chan struct {
|
|
|
+func (runtime *nodejsRuntime) detectYarnGitlab(results chan struct {
|
|
|
+ string
|
|
|
+ bool
|
|
|
+}, tree []*gitlab.TreeNode) {
|
|
|
+ yarnLockFound := false
|
|
|
+ packageJSONFound := false
|
|
|
+ for i := 0; i < len(tree); i++ {
|
|
|
+ name := tree[i].Name
|
|
|
+ if name == "yarn.lock" {
|
|
|
+ yarnLockFound = true
|
|
|
+ } else if name == "package.json" {
|
|
|
+ packageJSONFound = true
|
|
|
+ }
|
|
|
+ if yarnLockFound && packageJSONFound {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if yarnLockFound && packageJSONFound {
|
|
|
+ results <- struct {
|
|
|
+ string
|
|
|
+ bool
|
|
|
+ }{yarn, true}
|
|
|
+ }
|
|
|
+ runtime.wg.Done()
|
|
|
+}
|
|
|
+
|
|
|
+func (runtime *nodejsRuntime) detectNPMGithub(results chan struct {
|
|
|
string
|
|
|
bool
|
|
|
}, directoryContent []*github.RepositoryContent) {
|
|
|
@@ -75,7 +102,28 @@ func (runtime *nodejsRuntime) detectNPM(results chan struct {
|
|
|
runtime.wg.Done()
|
|
|
}
|
|
|
|
|
|
-func (runtime *nodejsRuntime) detectStandalone(results chan struct {
|
|
|
+func (runtime *nodejsRuntime) detectNPMGitlab(results chan struct {
|
|
|
+ string
|
|
|
+ bool
|
|
|
+}, tree []*gitlab.TreeNode) {
|
|
|
+ packageJSONFound := false
|
|
|
+ for i := 0; i < len(tree); i++ {
|
|
|
+ name := tree[i].Name
|
|
|
+ if name == "package.json" {
|
|
|
+ packageJSONFound = true
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if packageJSONFound {
|
|
|
+ results <- struct {
|
|
|
+ string
|
|
|
+ bool
|
|
|
+ }{npm, true}
|
|
|
+ }
|
|
|
+ runtime.wg.Done()
|
|
|
+}
|
|
|
+
|
|
|
+func (runtime *nodejsRuntime) detectStandaloneGithub(results chan struct {
|
|
|
string
|
|
|
bool
|
|
|
}, directoryContent []*github.RepositoryContent) {
|
|
|
@@ -96,6 +144,27 @@ func (runtime *nodejsRuntime) detectStandalone(results chan struct {
|
|
|
runtime.wg.Done()
|
|
|
}
|
|
|
|
|
|
+func (runtime *nodejsRuntime) detectStandaloneGitlab(results chan struct {
|
|
|
+ string
|
|
|
+ bool
|
|
|
+}, tree []*gitlab.TreeNode) {
|
|
|
+ jsFileFound := false
|
|
|
+ for i := 0; i < len(tree); i++ {
|
|
|
+ name := tree[i].Name
|
|
|
+ if name == "server.js" || name == "app.js" || name == "main.js" || name == "index.js" {
|
|
|
+ jsFileFound = true
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if jsFileFound {
|
|
|
+ results <- struct {
|
|
|
+ string
|
|
|
+ bool
|
|
|
+ }{standalone, true}
|
|
|
+ }
|
|
|
+ runtime.wg.Done()
|
|
|
+}
|
|
|
+
|
|
|
// copied directly from https://github.com/paketo-buildpacks/node-engine/blob/main/nvmrc_parser.go
|
|
|
func validateNvmrc(content string) (string, error) {
|
|
|
content = strings.TrimSpace(strings.ToLower(content))
|
|
|
@@ -157,7 +226,7 @@ func validateNodeVersion(content string) (string, error) {
|
|
|
return content, nil
|
|
|
}
|
|
|
|
|
|
-func (runtime *nodejsRuntime) Detect(
|
|
|
+func (runtime *nodejsRuntime) DetectGithub(
|
|
|
client *github.Client,
|
|
|
directoryContent []*github.RepositoryContent,
|
|
|
owner, name, path string,
|
|
|
@@ -170,9 +239,9 @@ func (runtime *nodejsRuntime) Detect(
|
|
|
}, 3)
|
|
|
|
|
|
runtime.wg.Add(3)
|
|
|
- go runtime.detectYarn(results, directoryContent)
|
|
|
- go runtime.detectNPM(results, directoryContent)
|
|
|
- go runtime.detectStandalone(results, directoryContent)
|
|
|
+ go runtime.detectYarnGithub(results, directoryContent)
|
|
|
+ go runtime.detectNPMGithub(results, directoryContent)
|
|
|
+ go runtime.detectStandaloneGithub(results, directoryContent)
|
|
|
runtime.wg.Wait()
|
|
|
close(results)
|
|
|
|
|
|
@@ -337,3 +406,169 @@ func (runtime *nodejsRuntime) Detect(
|
|
|
|
|
|
return nil
|
|
|
}
|
|
|
+
|
|
|
+func (runtime *nodejsRuntime) DetectGitlab(
|
|
|
+ client *gitlab.Client,
|
|
|
+ tree []*gitlab.TreeNode,
|
|
|
+ owner, name, path, ref string,
|
|
|
+ paketo, heroku *BuilderInfo,
|
|
|
+) error {
|
|
|
+ results := make(chan struct {
|
|
|
+ string
|
|
|
+ bool
|
|
|
+ }, 3)
|
|
|
+
|
|
|
+ runtime.wg.Add(3)
|
|
|
+ go runtime.detectYarnGitlab(results, tree)
|
|
|
+ go runtime.detectNPMGitlab(results, tree)
|
|
|
+ go runtime.detectStandaloneGitlab(results, tree)
|
|
|
+ runtime.wg.Wait()
|
|
|
+ close(results)
|
|
|
+
|
|
|
+ paketoBuildpackInfo := BuildpackInfo{
|
|
|
+ Name: "NodeJS",
|
|
|
+ Buildpack: "gcr.io/paketo-buildpacks/nodejs",
|
|
|
+ }
|
|
|
+ herokuBuildpackInfo := BuildpackInfo{
|
|
|
+ Name: "NodeJS",
|
|
|
+ Buildpack: "heroku/nodejs",
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(results) == 0 {
|
|
|
+ paketo.Others = append(paketo.Others, paketoBuildpackInfo)
|
|
|
+ heroku.Others = append(heroku.Others, herokuBuildpackInfo)
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ foundYarn := false
|
|
|
+ foundNPM := false
|
|
|
+ foundStandalone := false
|
|
|
+ for result := range results {
|
|
|
+ if result.string == yarn {
|
|
|
+ foundYarn = true
|
|
|
+ } else if result.string == npm {
|
|
|
+ foundNPM = true
|
|
|
+ } else if result.string == standalone {
|
|
|
+ foundStandalone = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if foundYarn || foundNPM {
|
|
|
+ // it is safe to assume that the project contains a package.json
|
|
|
+ fileContent, _, err := client.RepositoryFiles.GetRawFile(
|
|
|
+ fmt.Sprintf("%s/%s", owner, name), fmt.Sprintf("%s/package.json", path),
|
|
|
+ &gitlab.GetRawFileOptions{
|
|
|
+ Ref: gitlab.String(ref),
|
|
|
+ },
|
|
|
+ )
|
|
|
+ if err != nil {
|
|
|
+ paketo.Others = append(paketo.Others, paketoBuildpackInfo)
|
|
|
+ heroku.Others = append(heroku.Others, herokuBuildpackInfo)
|
|
|
+ return fmt.Errorf("error fetching contents of package.json: %v", err)
|
|
|
+ }
|
|
|
+ var packageJSON struct {
|
|
|
+ Scripts map[string]string `json:"scripts"`
|
|
|
+ Engines struct {
|
|
|
+ Node string `json:"node"`
|
|
|
+ } `json:"engines"`
|
|
|
+ }
|
|
|
+
|
|
|
+ data := string(fileContent)
|
|
|
+
|
|
|
+ err = json.NewDecoder(strings.NewReader(data)).Decode(&packageJSON)
|
|
|
+ if err != nil {
|
|
|
+ paketo.Others = append(paketo.Others, paketoBuildpackInfo)
|
|
|
+ heroku.Others = append(heroku.Others, herokuBuildpackInfo)
|
|
|
+ return fmt.Errorf("error decoding package.json contents to struct: %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ if packageJSON.Engines.Node == "" {
|
|
|
+ // we should now check for the node engine version in .nvmrc and then .node-version
|
|
|
+ nvmrcFound := false
|
|
|
+ nodeVersionFound := false
|
|
|
+ for i := 0; i < len(tree); i++ {
|
|
|
+ name := tree[i].Name
|
|
|
+ if name == ".nvmrc" {
|
|
|
+ nvmrcFound = true
|
|
|
+ } else if name == ".node-version" {
|
|
|
+ nodeVersionFound = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if nvmrcFound {
|
|
|
+ // copy exact behavior of https://github.com/paketo-buildpacks/node-engine/blob/main/nvmrc_parser.go
|
|
|
+ fileContent, _, err = client.RepositoryFiles.GetRawFile(
|
|
|
+ fmt.Sprintf("%s/%s", owner, name), fmt.Sprintf("%s/.nvmrc", path),
|
|
|
+ &gitlab.GetRawFileOptions{
|
|
|
+ Ref: gitlab.String(ref),
|
|
|
+ },
|
|
|
+ )
|
|
|
+ if err != nil {
|
|
|
+ paketo.Others = append(paketo.Others, paketoBuildpackInfo)
|
|
|
+ heroku.Others = append(heroku.Others, herokuBuildpackInfo)
|
|
|
+ return fmt.Errorf("error fetching contents of .nvmrc: %v", err)
|
|
|
+ }
|
|
|
+ data = string(fileContent)
|
|
|
+
|
|
|
+ nvmrcVersion, err := validateNvmrc(data)
|
|
|
+ if err != nil {
|
|
|
+ paketo.Others = append(paketo.Others, paketoBuildpackInfo)
|
|
|
+ heroku.Others = append(heroku.Others, herokuBuildpackInfo)
|
|
|
+ return fmt.Errorf("error validating .nvmrc: %v", err)
|
|
|
+ }
|
|
|
+ nvmrcVersion = formatNvmrcContent(nvmrcVersion)
|
|
|
+
|
|
|
+ if nvmrcVersion != "*" {
|
|
|
+ packageJSON.Engines.Node = data
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if packageJSON.Engines.Node == "" && nodeVersionFound {
|
|
|
+ // copy exact behavior of https://github.com/paketo-buildpacks/node-engine/blob/main/node_version_parser.go
|
|
|
+ fileContent, _, err = client.RepositoryFiles.GetRawFile(
|
|
|
+ fmt.Sprintf("%s/%s", owner, name), fmt.Sprintf("%s/.node-version", path),
|
|
|
+ &gitlab.GetRawFileOptions{
|
|
|
+ Ref: gitlab.String(ref),
|
|
|
+ },
|
|
|
+ )
|
|
|
+ if err != nil {
|
|
|
+ paketo.Others = append(paketo.Others, paketoBuildpackInfo)
|
|
|
+ heroku.Others = append(heroku.Others, herokuBuildpackInfo)
|
|
|
+ return fmt.Errorf("error fetching contents of .node-version: %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ data = string(fileContent)
|
|
|
+
|
|
|
+ nodeVersion, err := validateNodeVersion(data)
|
|
|
+ if err != nil {
|
|
|
+ paketo.Others = append(paketo.Others, paketoBuildpackInfo)
|
|
|
+ heroku.Others = append(heroku.Others, herokuBuildpackInfo)
|
|
|
+ return fmt.Errorf("error validating .node-version: %v", err)
|
|
|
+ }
|
|
|
+ if nodeVersion != "" {
|
|
|
+ packageJSON.Engines.Node = nodeVersion
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if packageJSON.Engines.Node == "" {
|
|
|
+ // use the default node engine version from https://github.com/paketo-buildpacks/node-engine/blob/main/buildpack.toml
|
|
|
+ packageJSON.Engines.Node = "16.*.*"
|
|
|
+ }
|
|
|
+
|
|
|
+ paketoBuildpackInfo.Config = make(map[string]interface{})
|
|
|
+ paketoBuildpackInfo.Config["scripts"] = packageJSON.Scripts
|
|
|
+ paketoBuildpackInfo.Config["node_engine"] = packageJSON.Engines.Node
|
|
|
+ paketo.Detected = append(paketo.Detected, paketoBuildpackInfo)
|
|
|
+
|
|
|
+ herokuBuildpackInfo.Config = make(map[string]interface{})
|
|
|
+ herokuBuildpackInfo.Config["scripts"] = packageJSON.Scripts
|
|
|
+ herokuBuildpackInfo.Config["node_engine"] = packageJSON.Engines.Node
|
|
|
+ heroku.Detected = append(heroku.Detected, herokuBuildpackInfo)
|
|
|
+ } else if foundStandalone {
|
|
|
+ paketo.Detected = append(paketo.Detected, paketoBuildpackInfo)
|
|
|
+ heroku.Detected = append(heroku.Detected, herokuBuildpackInfo)
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|