Skip to content

Commit

Permalink
Merge pull request #4 from ninech/support-nodejs
Browse files Browse the repository at this point in the history
feat: support detection of node/npm apps
  • Loading branch information
ctrox authored Dec 14, 2023
2 parents 13aaea7 + a57dde8 commit 0d43ee3
Show file tree
Hide file tree
Showing 17 changed files with 28,065 additions and 1 deletion.
34 changes: 34 additions & 0 deletions detect.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package static

import (
"errors"
"os"
"path/filepath"

require "github.com/ninech/buildpack-static-require"
"github.com/paketo-buildpacks/libnodejs"
"github.com/paketo-buildpacks/packit/v2"
"github.com/paketo-buildpacks/packit/v2/scribe"
)
Expand All @@ -26,6 +31,14 @@ func Detect(logger scribe.Emitter) packit.DetectFunc {
return packit.DetectResult{}, err
}

ok, nodeAppPath, err := isNodeApp(context.WorkingDir)
if err != nil {
return packit.DetectResult{}, err
}
if ok {
webRoot = filepath.Join(nodeAppPath, "build")
}

result := packit.DetectResult{
Plan: packit.BuildPlan{
Provides: []packit.BuildPlanProvision{{Name: name}},
Expand All @@ -41,3 +54,24 @@ func Detect(logger scribe.Emitter) packit.DetectFunc {
return result, nil
}
}

// isNodeApp checks if there is a package.json in the "projectPath". By
// default that is the workspace but it could also be overridden using
// BP_NODE_PROJECT_PATH.
func isNodeApp(workingDir string) (bool, string, error) {
projectPath, err := libnodejs.FindProjectPath(workingDir)
if err != nil {
return false, "", err
}

if _, err := libnodejs.ParsePackageJSON(projectPath); err == nil {
relativePath, err := filepath.Rel(workingDir, projectPath)
return true, relativePath, err
} else {
if errors.Is(err, os.ErrNotExist) {
return false, "", nil
}

return false, "", err
}
}
57 changes: 57 additions & 0 deletions detect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ func testDetect(t *testing.T, context spec.G, it spec.S) {
indexFile = `
<!DOCTYPE html>
<html></html>`
packageJSONFile = `
{
"engines": {
"node": "1.2.3"
}
}`
)

it.Before(func() {
Expand Down Expand Up @@ -96,4 +102,55 @@ func testDetect(t *testing.T, context spec.G, it spec.S) {
Expect(err).To(MatchError(packit.Fail.WithMessage("no index.html found")))
})
})

context("when a package.json is present", func() {
it.Before(func() {
var err error
workingDir, err = os.MkdirTemp(t.TempDir(), "working-dir-*")
Expect(err).NotTo(HaveOccurred())
err = os.WriteFile(filepath.Join(workingDir, "index.html"), []byte(indexFile), os.ModePerm)
Expect(err).NotTo(HaveOccurred())
err = os.WriteFile(filepath.Join(workingDir, "package.json"), []byte(packageJSONFile), os.ModePerm)
Expect(err).NotTo(HaveOccurred())
})

it("sets the webroot accordingly", func() {
result, err := Detect(scribe.NewEmitter(buffer))(packit.DetectContext{
WorkingDir: workingDir,
})
Expect(err).NotTo(HaveOccurred())
Expect(result.Plan.Provides).To(ContainElement(packit.BuildPlanProvision{Name: name}))
Expect(result.Plan.Requires).To(ContainElement(packit.BuildPlanRequirement{
Name: name, Metadata: BuildPlanMetadata{WebRoot: "build"},
}))
})
})

context("when a package.json is present in BP_NODE_PROJECT_PATH", func() {
var nodeProjectPath = "node-app"
it.Before(func() {
var err error
workingDir, err = os.MkdirTemp(t.TempDir(), "working-dir-*")
Expect(err).NotTo(HaveOccurred())
err = os.WriteFile(filepath.Join(workingDir, "index.html"), []byte(indexFile), os.ModePerm)
Expect(err).NotTo(HaveOccurred())

Expect(os.MkdirAll(filepath.Join(workingDir, nodeProjectPath), os.ModePerm)).NotTo(HaveOccurred())
err = os.WriteFile(filepath.Join(workingDir, nodeProjectPath, "package.json"), []byte(packageJSONFile), os.ModePerm)
Expect(err).NotTo(HaveOccurred())

t.Setenv("BP_NODE_PROJECT_PATH", nodeProjectPath)
})

it("sets the webroot accordingly", func() {
result, err := Detect(scribe.NewEmitter(buffer))(packit.DetectContext{
WorkingDir: workingDir,
})
Expect(err).NotTo(HaveOccurred())
Expect(result.Plan.Provides).To(ContainElement(packit.BuildPlanProvision{Name: name}))
Expect(result.Plan.Requires).To(ContainElement(packit.BuildPlanRequirement{
Name: name, Metadata: BuildPlanMetadata{WebRoot: filepath.Join(nodeProjectPath, "build")},
}))
})
})
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/BurntSushi/toml v1.3.2
github.com/ninech/buildpack-static-require v0.0.1
github.com/onsi/gomega v1.29.0
github.com/paketo-buildpacks/libnodejs v0.2.0
github.com/paketo-buildpacks/nginx v0.15.7
github.com/paketo-buildpacks/occam v0.17.0
github.com/paketo-buildpacks/packit/v2 v2.12.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2618,6 +2618,8 @@ github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/paketo-buildpacks/libnodejs v0.2.0 h1:y7G6BQIeeMUnIlzREiAipQlAcaXJ82VMPldkTTiv9o4=
github.com/paketo-buildpacks/libnodejs v0.2.0/go.mod h1:JLs6tu5TD6la8p1IKo3UYO1C93/ywGesa74FpM82wPw=
github.com/paketo-buildpacks/nginx v0.15.7 h1:Pcs7NseRS+8RUL1HY5bkvXNp0NgPV+yDr6Ien97Im4E=
github.com/paketo-buildpacks/nginx v0.15.7/go.mod h1:Fl+gBf4iFbY18SrmFUfN4K5jXctXOWXK++I5cPu4J1U=
github.com/paketo-buildpacks/occam v0.17.0 h1:2Xt4Vc4MbSAsLyu+WLttd6gpR4ORsBwcoqAfWz4QaD4=
Expand Down
2 changes: 1 addition & 1 deletion init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
)

func TestUnitBuildpackStatic(t *testing.T) {
suite := spec.New("buildpack-static-confgen", spec.Report(report.Terminal{}), spec.Parallel())
suite := spec.New("buildpack-static-confgen", spec.Report(report.Terminal{}))
suite("Detect", testDetect)
suite("Build", testBuild)
suite.Run(t)
Expand Down
1 change: 1 addition & 0 deletions integration.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"index.docker.io/paketobuildpacks/builder-jammy-buildpackless-base:latest"
],
"nginx": "github.com/paketo-buildpacks/nginx",
"webServers": "github.com/paketo-buildpacks/web-servers",
"staticRequire": "github.com/ninech/buildpack-static-require"
}
30 changes: 30 additions & 0 deletions integration/default_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,5 +101,35 @@ func testDefault(t *testing.T, context spec.G, it spec.S) {
Eventually(container).Should(Serve(ContainSubstring("satic site served from public dir")).WithEndpoint("/index.html"))
})
})

context("when building the react_app", func() {
it("serves up the built index.html", func() {
var err error
source, err = occam.Source(filepath.Join("testdata", "react_app"))
Expect(err).NotTo(HaveOccurred())

var logs fmt.Stringer
image, logs, err = pack.WithNoColor().Build.
WithPullPolicy("never").
WithBuildpacks(
buildpack,
config.WebServers,
config.StaticRequire,
).
Execute(name, source)
Expect(err).NotTo(HaveOccurred(), logs.String())

container, err = docker.Container.Run.
WithEnv(map[string]string{"PORT": "8088"}).
WithPublish("8088").
Execute(image.ID)
Expect(err).ToNot(HaveOccurred())

// we expect the string %PUBLIC% to not be in the index.html
// anymore since npm build should take care of replacing that
// during build.
Eventually(container).Should(Serve(Not(ContainSubstring("%PUBLIC%"))).WithEndpoint("/index.html"))
})
})
})
}
5 changes: 5 additions & 0 deletions integration/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ var (

config struct {
Nginx string `json:"nginx"`
WebServers string `json:"webServers"`
StaticRequire string `json:"staticRequire"`
}
)
Expand Down Expand Up @@ -67,6 +68,10 @@ func TestIntegration(t *testing.T) {
Execute(config.Nginx)
Expect(err).ToNot(HaveOccurred())

config.WebServers, err = buildpackStore.Get.
Execute(config.WebServers)
Expect(err).ToNot(HaveOccurred())

config.StaticRequire, err = buildpackStore.Get.
Execute(config.StaticRequire)
Expect(err).ToNot(HaveOccurred())
Expand Down
23 changes: 23 additions & 0 deletions integration/testdata/react_app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
Loading

0 comments on commit 0d43ee3

Please sign in to comment.