Skip to content

Commit

Permalink
feat: support detection of node/npm apps
Browse files Browse the repository at this point in the history
This adds some more logic to also detect node-based frontend apps. The
web-servers buildpack already takes care of running `npm build` but in
case that happens we need to serve the resulting artifacts from
`/workspace/build`. We do that by leveraging the libnodejs package to
detect if we have a nodejs app and then we set the build directory as the
webroot.
  • Loading branch information
ctrox committed Dec 14, 2023
1 parent 13aaea7 commit df86a29
Show file tree
Hide file tree
Showing 13 changed files with 27,998 additions and 0 deletions.
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
}
}
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
27 changes: 27 additions & 0 deletions integration/default_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,5 +101,32 @@ 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.Nginx,
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())

Eventually(container).Should(Serve(Not(ContainSubstring("%PUBLIC%"))).WithEndpoint("/index.html"))
})
})
})
}
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 df86a29

Please sign in to comment.