Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support detection of node/npm apps #4

Merged
merged 1 commit into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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