Skip to content

Commit

Permalink
move function to find applications into libnodejs
Browse files Browse the repository at this point in the history
Move function from to find applications from node-start
into libnodejs so that it can be used across buildpacks
and extensions.

API has been refined from what was in node-start to
make it better as a shared function.

Signed-off-by: Michael Dawson <[email protected]>
  • Loading branch information
mhdawson committed Jun 1, 2023
1 parent 1055d49 commit cc37aae
Show file tree
Hide file tree
Showing 4 changed files with 235 additions and 0 deletions.
46 changes: 46 additions & 0 deletions find_node_application.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package libnodejs

import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"
)

func FindNodeApplication(workingDir string) (string, error) {

projectPath, err := FindProjectPath(workingDir)
if err != nil {
return "", err
}

launchpoint := os.Getenv(LaunchPointEnvName)
if launchpoint != "" {
if _, err := os.Stat(filepath.Join(workingDir, launchpoint)); err != nil {
if errors.Is(err, os.ErrNotExist) {
return "", fmt.Errorf("expected value derived from BP_LAUNCHPOINT [%s] to be an existing file", launchpoint)
}

return "", err
}

return filepath.Clean(launchpoint), nil
}

files := []string{"server.js", "app.js", "main.js", "index.js"}
for _, file := range files {
_, err := os.Stat(filepath.Join(projectPath, file))
if err != nil {
if errors.Is(err, os.ErrNotExist) {
continue
}

return "", err
}

return filepath.Join(os.Getenv(ProjectPathEnvName), file), nil
}

return "", fmt.Errorf("could not find app in %s: expected one of %s", filepath.Clean(projectPath), strings.Join(files, " | "))
}
187 changes: 187 additions & 0 deletions find_node_application_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
package libnodejs_test

import (
"fmt"
"os"
"path/filepath"
"testing"

"github.com/paketo-buildpacks/libnodejs"
"github.com/sclevine/spec"

. "github.com/onsi/gomega"
)

func testFindNodeApplication(t *testing.T, context spec.G, it spec.S) {
var (
Expect = NewWithT(t).Expect

workingDir string
)

it.Before(func() {
workingDir = t.TempDir()
})

it.After(func() {
Expect(os.RemoveAll(workingDir)).To(Succeed())
})

context("finds the server.js application entrypoint", func() {
it.Before(func() {
Expect(os.WriteFile(filepath.Join(workingDir, "server.js"), nil, 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(workingDir, "app.js"), nil, 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(workingDir, "main.js"), nil, 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(workingDir, "index.js"), nil, 0600)).To(Succeed())
})

it("finds the server.js application entrypoint successfully", func() {
file, err := libnodejs.FindNodeApplication(workingDir)
Expect(err).NotTo(HaveOccurred())
Expect(file).To(Equal(filepath.Join("server.js")))
})
})

context("finds the app.js application entrypoint", func() {
it.Before(func() {
Expect(os.WriteFile(filepath.Join(workingDir, "app.js"), nil, 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(workingDir, "main.js"), nil, 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(workingDir, "index.js"), nil, 0600)).To(Succeed())
})

it("finds the app.js application entrypoint successfully", func() {
file, err := libnodejs.FindNodeApplication(workingDir)
Expect(err).NotTo(HaveOccurred())
Expect(file).To(Equal(filepath.Join("app.js")))
})
})

context("finds the main.js application entrypoint", func() {
it.Before(func() {
Expect(os.WriteFile(filepath.Join(workingDir, "main.js"), nil, 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(workingDir, "index.js"), nil, 0600)).To(Succeed())
})

it("finds the main.js application entrypoint successfully", func() {
file, err := libnodejs.FindNodeApplication(workingDir)
Expect(err).NotTo(HaveOccurred())
Expect(file).To(Equal(filepath.Join("main.js")))
})
})

context("finds the index.js application entrypoint", func() {
it.Before(func() {
Expect(os.WriteFile(filepath.Join(workingDir, "index.js"), nil, 0600)).To(Succeed())
})

it("finds the index.js application entrypoint", func() {
file, err := libnodejs.FindNodeApplication(workingDir)
Expect(err).NotTo(HaveOccurred())
Expect(file).To(Equal(filepath.Join("index.js")))
})
})

context("when there is a launchpoint", func() {
it.Before(func() {
Expect(os.Mkdir(filepath.Join(workingDir, "src"), os.ModePerm)).To(Succeed())
Expect(os.WriteFile(filepath.Join(workingDir, "src", "launchpoint.js"), nil, 0600)).To(Succeed())
})

context("when the launchpoint file exists", func() {
it("returns the highest priority file", func() {
t.Setenv("BP_LAUNCHPOINT", "./src/launchpoint.js")
file, err := libnodejs.FindNodeApplication(workingDir)
Expect(err).NotTo(HaveOccurred())
Expect(file).To(Equal(filepath.Join("src", "launchpoint.js")))
})
})

context("when the launchpoint file does not exist", func() {
it("returns the empty string and no error", func() {
t.Setenv("BP_LAUNCHPOINT", "./no-such-file.js")
file, err := libnodejs.FindNodeApplication(workingDir)
Expect(err).To(MatchError(ContainSubstring("expected value derived from BP_LAUNCHPOINT [./no-such-file.js] to be an existing file")))
Expect(file).To(Equal(""))
})
})
})

context("when there is a project path", func() {
it.Before(func() {
Expect(os.Mkdir(filepath.Join(workingDir, "frontend"), os.ModePerm)).To(Succeed())
Expect(os.WriteFile(filepath.Join(workingDir, "frontend", "server.js"), nil, 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(workingDir, "frontend", "app.js"), nil, 0600)).To(Succeed())
})

it("returns the highest priority file", func() {
t.Setenv("BP_NODE_PROJECT_PATH", "frontend")
file, err := libnodejs.FindNodeApplication(workingDir)
Expect(err).NotTo(HaveOccurred())
Expect(file).To(Equal(filepath.Join("frontend", "server.js")))
})
})

context("when there is a project path but value specified does not exist", func() {
it("returns a failure", func() {
t.Setenv("BP_NODE_PROJECT_PATH", "frontend")
_, err := libnodejs.FindNodeApplication(workingDir)
Expect(err).To(MatchError(ContainSubstring("no such file or directory")))
})
})

context("when no application can be found", func() {
it("returns that application could not be found", func() {
_, err := libnodejs.FindNodeApplication(workingDir)
Expect(err).To(MatchError(fmt.Errorf("could not find app in %s: expected one of server.js | app.js | main.js | index.js", workingDir)))
})
})

context("failure cases", func() {
context("when the launchpoint cannot be stat'd", func() {
it.Before(func() {
Expect(os.Chmod(workingDir, 0000)).To(Succeed())
})

it.After(func() {
Expect(os.Chmod(workingDir, os.ModePerm)).To(Succeed())
})

it("fails with helpful error", func() {
t.Setenv("BP_LAUNCHPOINT", "something.js")
_, err := libnodejs.FindNodeApplication(workingDir)
Expect(err).To(MatchError(ContainSubstring("permission denied")))
})
})

context("when the working dir cannot be stat'd", func() {
it.Before(func() {
Expect(os.Chmod(workingDir, 0000)).To(Succeed())
})

it.After(func() {
Expect(os.Chmod(workingDir, os.ModePerm)).To(Succeed())
})

it("fails with helpful error", func() {
_, err := libnodejs.FindNodeApplication(workingDir)
Expect(err).To(MatchError(ContainSubstring("permission denied")))
})
})

context("when the project path cannot be stat'd", func() {
it.Before(func() {
Expect(os.Chmod(workingDir, 0000)).To(Succeed())
})

it.After(func() {
Expect(os.Chmod(workingDir, os.ModePerm)).To(Succeed())
})

it("fails with helpful error", func() {
t.Setenv("BP_NODE_PROJECT_PATH", "frontend")
_, err := libnodejs.FindNodeApplication(workingDir)
Expect(err).To(MatchError(ContainSubstring("permission denied")))
})
})
})
}
1 change: 1 addition & 0 deletions init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ func TestUnitLibnodejs(t *testing.T) {
suite := spec.New("libnodejs", spec.Report(report.Terminal{}), spec.Sequential())
suite("FindProjectPath", testFindProjectPath)
suite("PackageJSON", testPackageJSON)
suite("FindNodeApplication", testFindNodeApplication)
suite.Run(t)
}
1 change: 1 addition & 0 deletions spec_constants.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
package libnodejs

const ProjectPathEnvName = "BP_NODE_PROJECT_PATH"
const LaunchPointEnvName = "BP_LAUNCHPOINT"

0 comments on commit cc37aae

Please sign in to comment.