Skip to content

Commit

Permalink
Changes the detection logic
Browse files Browse the repository at this point in the history
- The buildpack now searches the applicatiod root for a set of files and
selects one of them based off the following prioriy: `server.js > app.js > main.js > index.js`
  • Loading branch information
ForestEckhardt authored and arjun024 committed Dec 4, 2020
1 parent d4fe586 commit cd2f010
Showing 15 changed files with 238 additions and 143 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -49,3 +49,13 @@ and thus it ignores any signal with the default action. As a result, the
process will not terminate on `SIGINT` or `SIGTERM` unless it is coded to do
so. You can also use docker's `--init` flag to wrap your node process with an
init system that will properly handle signals.

## Application Detection
This buildpack searches your application root for the following files:
1. `server.js`
1. `app.js`
1. `main.js`
1. `index.js`
If you have multiple of the above files in you application root then the
highest priority file (`server.js > app.js > main.js > index.js`) will be
choosen for the start command.
11 changes: 9 additions & 2 deletions build.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
package nodestart

import (
"fmt"

"github.com/paketo-buildpacks/packit"
"github.com/paketo-buildpacks/packit/scribe"
)

func Build(logger scribe.Logger) packit.BuildFunc {
func Build(applicationDetector ApplicationDetector, logger scribe.Logger) packit.BuildFunc {
return func(context packit.BuildContext) (packit.BuildResult, error) {
logger.Title("%s %s", context.BuildpackInfo.Name, context.BuildpackInfo.Version)

command := "node server.js"
file, err := applicationDetector.Detect(context.WorkingDir)
if err != nil {
return packit.BuildResult{}, err
}

command := fmt.Sprintf("node %s", file)

logger.Process("Assigning launch processes")
logger.Subprocess("web: %s", command)
36 changes: 35 additions & 1 deletion build_test.go
Original file line number Diff line number Diff line change
@@ -2,11 +2,13 @@ package nodestart_test

import (
"bytes"
"errors"
"io/ioutil"
"os"
"testing"

nodestart "github.com/paketo-buildpacks/node-start"
"github.com/paketo-buildpacks/node-start/fakes"
"github.com/paketo-buildpacks/packit"
"github.com/paketo-buildpacks/packit/scribe"
"github.com/sclevine/spec"
@@ -23,6 +25,8 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
cnbDir string
buffer *bytes.Buffer

applicationDetector *fakes.ApplicationDetector

build packit.BuildFunc
)

@@ -37,9 +41,12 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
workingDir, err = ioutil.TempDir("", "working-dir")
Expect(err).NotTo(HaveOccurred())

applicationDetector = &fakes.ApplicationDetector{}
applicationDetector.DetectCall.Returns.String = "server.js"

buffer = bytes.NewBuffer(nil)
logger := scribe.NewLogger(buffer)
build = nodestart.Build(logger)
build = nodestart.Build(applicationDetector, logger)
})

it.After(func() {
@@ -77,8 +84,35 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
},
}))

Expect(applicationDetector.DetectCall.Receives.WorkingDir).To(Equal(workingDir))

Expect(buffer.String()).To(ContainSubstring("Some Buildpack some-version"))
Expect(buffer.String()).To(ContainSubstring("Assigning launch processes"))
Expect(buffer.String()).To(ContainSubstring("node server.js"))
})

context("failure cases", func() {
context("when the application detection fails", func() {
it.Before(func() {
applicationDetector.DetectCall.Returns.Error = errors.New("failed application detection")

})
it("returns an error", func() {
_, err := build(packit.BuildContext{
WorkingDir: workingDir,
CNBPath: cnbDir,
Stack: "some-stack",
BuildpackInfo: packit.BuildpackInfo{
Name: "Some Buildpack",
Version: "some-version",
},
Plan: packit.BuildpackPlan{
Entries: []packit.BuildpackPlanEntry{},
},
Layers: packit.Layers{Path: layersDir},
})
Expect(err).To(MatchError("failed application detection"))
})
})
})
}
17 changes: 9 additions & 8 deletions detect.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
package nodestart

import (
"fmt"
"github.com/paketo-buildpacks/packit"
"path/filepath"
)

func Detect() packit.DetectFunc {
//go:generate faux --interface ApplicationDetector --output fakes/application_detector.go
type ApplicationDetector interface {
Detect(workingDir string) (string, error)
}

func Detect(applicationDetector ApplicationDetector) packit.DetectFunc {
return func(context packit.DetectContext) (packit.DetectResult, error) {
files, err := filepath.Glob(filepath.Join(context.WorkingDir, "*.js"))
_, err := applicationDetector.Detect(context.WorkingDir)
if err != nil {
return packit.DetectResult{}, fmt.Errorf("file glob function failed: %w", err)
}
if len(files) == 0 {
return packit.DetectResult{}, packit.Fail
return packit.DetectResult{}, err
}

return packit.DetectResult{
Plan: packit.BuildPlan{
Provides: []packit.BuildPlanProvision{},
52 changes: 20 additions & 32 deletions detect_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package nodestart_test

import (
"errors"
"testing"

nodestart "github.com/paketo-buildpacks/node-start"
"github.com/paketo-buildpacks/node-start/fakes"
"github.com/paketo-buildpacks/packit"
"github.com/sclevine/spec"
"io/ioutil"
"os"
"path/filepath"
"testing"

. "github.com/onsi/gomega"
)
@@ -16,28 +16,21 @@ func testDetect(t *testing.T, context spec.G, it spec.S) {
var (
Expect = NewWithT(t).Expect

workingDir string
detect packit.DetectFunc
applicationDetector *fakes.ApplicationDetector

detect packit.DetectFunc
)

it.Before(func() {
var err error
workingDir, err = ioutil.TempDir("", "working-dir")
Expect(err).NotTo(HaveOccurred())

Expect(ioutil.WriteFile(filepath.Join(workingDir, "server.js"), nil, os.ModePerm)).To(Succeed())

detect = nodestart.Detect()
})
applicationDetector = &fakes.ApplicationDetector{}

it.After(func() {
Expect(os.RemoveAll(workingDir)).To(Succeed())
detect = nodestart.Detect(applicationDetector)
})

context("when there is at least one *.js file in the working directory", func() {
context("when an application is detected in the working dir", func() {
it("detects", func() {
result, err := detect(packit.DetectContext{
WorkingDir: workingDir,
WorkingDir: "working-dir",
})
Expect(err).NotTo(HaveOccurred())
Expect(result.Plan).To(Equal(packit.BuildPlan{
@@ -51,27 +44,22 @@ func testDetect(t *testing.T, context spec.G, it spec.S) {
},
},
}))
})
})
context("when there are no *.js files in the working directory", func() {
it.Before(func() {
os.RemoveAll(filepath.Join(workingDir, "server.js"))
})
it("fails detection", func() {
_, err := detect(packit.DetectContext{
WorkingDir: workingDir,
})
Expect(err).To(MatchError(packit.Fail))

Expect(applicationDetector.DetectCall.Receives.WorkingDir).To(Equal("working-dir"))
})
})

context("failure cases", func() {
context("when file glob fails", func() {
context("when the application detector fails", func() {
it.Before(func() {
applicationDetector.DetectCall.Returns.Error = errors.New("detector failed")
})

it("fails with helpful error", func() {
_, err := detect(packit.DetectContext{
WorkingDir: `\`,
WorkingDir: "working-dir",
})
Expect(err).To(MatchError(ContainSubstring("file glob function failed")))
Expect(err).To(MatchError("detector failed"))
})
})
})
29 changes: 29 additions & 0 deletions fakes/application_detector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package fakes

import "sync"

type ApplicationDetector struct {
DetectCall struct {
sync.Mutex
CallCount int
Receives struct {
WorkingDir string
}
Returns struct {
String string
Error error
}
Stub func(string) (string, error)
}
}

func (f *ApplicationDetector) Detect(param1 string) (string, error) {
f.DetectCall.Lock()
defer f.DetectCall.Unlock()
f.DetectCall.CallCount++
f.DetectCall.Receives.WorkingDir = param1
if f.DetectCall.Stub != nil {
return f.DetectCall.Stub(param1)
}
return f.DetectCall.Returns.String, f.DetectCall.Returns.Error
}
1 change: 1 addition & 0 deletions init_test.go
Original file line number Diff line number Diff line change
@@ -11,5 +11,6 @@ func TestUnitNodeStart(t *testing.T) {
suite := spec.New("node-start", spec.Report(report.Terminal{}), spec.Parallel())
suite("Build", testBuild)
suite("Detect", testDetect)
suite("NodeApplicationDetector", testNodeApplicationDetector)
suite.Run(t)
}
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ import (
. "github.com/paketo-buildpacks/occam/matchers"
)

func testSimple(t *testing.T, context spec.G, it spec.S) {
func testDefault(t *testing.T, context spec.G, it spec.S) {
var (
Expect = NewWithT(t).Expect
Eventually = NewWithT(t).Eventually
@@ -29,7 +29,7 @@ func testSimple(t *testing.T, context spec.G, it spec.S) {
docker = occam.NewDocker()
})

context("when building a simple app", func() {
context("when building a default app", func() {
var (
image occam.Image
container occam.Container
@@ -53,7 +53,7 @@ func testSimple(t *testing.T, context spec.G, it spec.S) {

it("builds and runs successfully", func() {
var err error
source, err = occam.Source(filepath.Join("testdata", "simple_app"))
source, err = occam.Source(filepath.Join("testdata", "default"))
Expect(err).NotTo(HaveOccurred())

var logs fmt.Stringer
3 changes: 1 addition & 2 deletions integration/init_test.go
Original file line number Diff line number Diff line change
@@ -62,7 +62,6 @@ func TestIntegration(t *testing.T) {
SetDefaultEventuallyTimeout(10 * time.Second)

suite := spec.New("Integration", spec.Report(report.Terminal{}), spec.Parallel())
suite("Simple", testSimple)
suite("NoServerJS", testNoServerJs)
suite("Default", testDefault)
suite.Run(t)
}
78 changes: 0 additions & 78 deletions integration/no_server_app_test.go

This file was deleted.

File renamed without changes.
Loading

0 comments on commit cd2f010

Please sign in to comment.