Skip to content

Commit

Permalink
stream log output from 'npm' commands
Browse files Browse the repository at this point in the history
  • Loading branch information
Sophie Wigmore authored and thitch97 committed Apr 19, 2023
1 parent 89ceefa commit 56c8312
Show file tree
Hide file tree
Showing 11 changed files with 243 additions and 210 deletions.
7 changes: 2 additions & 5 deletions ci_build_process.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package npminstall

import (
"bytes"
"errors"
"fmt"
"os"
Expand Down Expand Up @@ -80,16 +79,14 @@ func (r CIBuildProcess) Run(modulesDir, cacheDir, workingDir, npmrcPath string,
args := []string{"ci", "--unsafe-perm", "--cache", cacheDir}
r.logger.Subprocess("Running 'npm %s'", strings.Join(args, " "))

buffer := bytes.NewBuffer(nil)
err = r.executable.Execute(pexec.Execution{
Args: args,
Dir: workingDir,
Stdout: buffer,
Stderr: buffer,
Stdout: r.logger.ActionWriter,
Stderr: r.logger.ActionWriter,
Env: environment,
})
if err != nil {
r.logger.Subprocess("%s", buffer.String())
return fmt.Errorf("npm ci failed: %w", err)
}

Expand Down
57 changes: 32 additions & 25 deletions ci_build_process_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,21 @@ import (
"github.com/sclevine/spec"

. "github.com/onsi/gomega"
. "github.com/paketo-buildpacks/occam/matchers"
)

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

modulesDir string
cacheDir string
workingDir string
executable *fakes.Executable
executions []pexec.Execution
summer *fakes.Summer
environment *fakes.EnvironmentConfig
buffer *bytes.Buffer
commandOutput *bytes.Buffer
modulesDir string
cacheDir string
workingDir string
executable *fakes.Executable
executions []pexec.Execution
summer *fakes.Summer
environment *fakes.EnvironmentConfig
buffer *bytes.Buffer

process npminstall.CIBuildProcess
)
Expand All @@ -48,6 +48,8 @@ func testCIBuildProcess(t *testing.T, context spec.G, it spec.S) {
executable = &fakes.Executable{}
executable.ExecuteCall.Stub = func(execution pexec.Execution) error {
executions = append(executions, execution)
fmt.Fprintln(execution.Stdout, "stdout output")
fmt.Fprintln(execution.Stderr, "stderr output")
return nil
}

Expand All @@ -58,7 +60,6 @@ func testCIBuildProcess(t *testing.T, context spec.G, it spec.S) {
environment.LookupCall.Returns.Found = true

buffer = bytes.NewBuffer(nil)
commandOutput = bytes.NewBuffer(nil)

process = npminstall.NewCIBuildProcess(executable, summer, environment, scribe.NewLogger(buffer))
})
Expand Down Expand Up @@ -192,13 +193,14 @@ func testCIBuildProcess(t *testing.T, context spec.G, it spec.S) {
it("succeeds", func() {
Expect(process.Run(modulesDir, cacheDir, workingDir, "some-npmrc-path", false)).To(Succeed())

Expect(executable.ExecuteCall.Receives.Execution).To(Equal(pexec.Execution{
Args: []string{"ci", "--unsafe-perm", "--cache", cacheDir},
Dir: workingDir,
Stdout: commandOutput,
Stderr: commandOutput,
Env: append(os.Environ(), "NPM_CONFIG_LOGLEVEL=some-val", "NPM_CONFIG_GLOBALCONFIG=some-npmrc-path", "NODE_ENV=development"),
}))
Expect(executable.ExecuteCall.Receives.Execution.Args).To(Equal([]string{"ci", "--unsafe-perm", "--cache", cacheDir}))
Expect(executable.ExecuteCall.Receives.Execution.Dir).To(Equal(workingDir))
Expect(executable.ExecuteCall.Receives.Execution.Env).To(Equal(append(os.Environ(), "NPM_CONFIG_LOGLEVEL=some-val", "NPM_CONFIG_GLOBALCONFIG=some-npmrc-path", "NODE_ENV=development")))
Expect(buffer.String()).To(ContainLines(
fmt.Sprintf(" Running 'npm ci --unsafe-perm --cache %s'", cacheDir),
" stdout output",
" stderr output",
))

path, err := os.Readlink(filepath.Join(workingDir, "node_modules"))
Expect(err).NotTo(HaveOccurred())
Expand All @@ -210,13 +212,14 @@ func testCIBuildProcess(t *testing.T, context spec.G, it spec.S) {
it("succeeds", func() {
Expect(process.Run(modulesDir, cacheDir, workingDir, "some-npmrc-path", true)).To(Succeed())

Expect(executable.ExecuteCall.Receives.Execution).To(Equal(pexec.Execution{
Args: []string{"ci", "--unsafe-perm", "--cache", cacheDir},
Dir: workingDir,
Stdout: commandOutput,
Stderr: commandOutput,
Env: append(os.Environ(), "NPM_CONFIG_LOGLEVEL=some-val", "NPM_CONFIG_GLOBALCONFIG=some-npmrc-path"),
}))
Expect(executable.ExecuteCall.Receives.Execution.Args).To(Equal([]string{"ci", "--unsafe-perm", "--cache", cacheDir}))
Expect(executable.ExecuteCall.Receives.Execution.Dir).To(Equal(workingDir))
Expect(executable.ExecuteCall.Receives.Execution.Env).To(Equal(append(os.Environ(), "NPM_CONFIG_LOGLEVEL=some-val", "NPM_CONFIG_GLOBALCONFIG=some-npmrc-path")))
Expect(buffer.String()).To(ContainLines(
fmt.Sprintf(" Running 'npm ci --unsafe-perm --cache %s'", cacheDir),
" stdout output",
" stderr output",
))

path, err := os.Readlink(filepath.Join(workingDir, "node_modules"))
Expect(err).NotTo(HaveOccurred())
Expand Down Expand Up @@ -266,7 +269,11 @@ func testCIBuildProcess(t *testing.T, context spec.G, it spec.S) {

it("returns an error", func() {
err := process.Run(modulesDir, cacheDir, workingDir, "", false)
Expect(buffer.String()).To(ContainSubstring(" ci failure on stdout\n ci failure on stderr"))
Expect(buffer.String()).To(ContainLines(
fmt.Sprintf(" Running 'npm ci --unsafe-perm --cache %s'", cacheDir),
" ci failure on stdout",
" ci failure on stderr",
))
Expect(err).To(MatchError("npm ci failed: failed to execute"))
})
})
Expand Down
7 changes: 2 additions & 5 deletions install_build_process.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package npminstall

import (
"bytes"
"errors"
"fmt"
"os"
Expand Down Expand Up @@ -53,16 +52,14 @@ func (r InstallBuildProcess) Run(modulesDir, cacheDir, workingDir, npmrcPath str
args := []string{"install", "--unsafe-perm", "--cache", cacheDir}
r.logger.Subprocess("Running 'npm %s'", strings.Join(args, " "))

buffer := bytes.NewBuffer(nil)
err = r.executable.Execute(pexec.Execution{
Args: args,
Dir: workingDir,
Stdout: buffer,
Stderr: buffer,
Stdout: r.logger.ActionWriter,
Stderr: r.logger.ActionWriter,
Env: environment,
})
if err != nil {
r.logger.Subprocess("%s", buffer.String())
return fmt.Errorf("npm install failed: %w", err)
}

Expand Down
56 changes: 33 additions & 23 deletions install_build_process_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,19 @@ import (
"github.com/sclevine/spec"

. "github.com/onsi/gomega"
. "github.com/paketo-buildpacks/occam/matchers"
)

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

modulesDir string
cacheDir string
workingDir string
executable *fakes.Executable
environment *fakes.EnvironmentConfig
buffer *bytes.Buffer
commandOutput *bytes.Buffer
modulesDir string
cacheDir string
workingDir string
executable *fakes.Executable
environment *fakes.EnvironmentConfig
buffer *bytes.Buffer

process npminstall.InstallBuildProcess
)
Expand All @@ -44,13 +44,17 @@ func testInstallBuildProcess(t *testing.T, context spec.G, it spec.S) {
Expect(err).NotTo(HaveOccurred())

executable = &fakes.Executable{}
executable.ExecuteCall.Stub = func(execution pexec.Execution) error {
fmt.Fprintln(execution.Stdout, "stdout output")
fmt.Fprintln(execution.Stderr, "stderr output")
return nil
}
environment = &fakes.EnvironmentConfig{}

environment.LookupCall.Returns.Value = "some-val"
environment.LookupCall.Returns.Found = true

buffer = bytes.NewBuffer(nil)
commandOutput = bytes.NewBuffer(nil)

process = npminstall.NewInstallBuildProcess(executable, environment, scribe.NewLogger(buffer))
})
Expand Down Expand Up @@ -78,13 +82,14 @@ func testInstallBuildProcess(t *testing.T, context spec.G, it spec.S) {
context("launch is false", func() {
it("succeeds", func() {
Expect(process.Run(modulesDir, cacheDir, workingDir, "some-npmrc-path", false)).To(Succeed())
Expect(executable.ExecuteCall.Receives.Execution).To(Equal(pexec.Execution{
Args: []string{"install", "--unsafe-perm", "--cache", cacheDir},
Dir: workingDir,
Stdout: commandOutput,
Stderr: commandOutput,
Env: append(os.Environ(), "NPM_CONFIG_LOGLEVEL=some-val", "NPM_CONFIG_GLOBALCONFIG=some-npmrc-path", "NODE_ENV=development"),
}))
Expect(executable.ExecuteCall.Receives.Execution.Args).To(Equal([]string{"install", "--unsafe-perm", "--cache", cacheDir}))
Expect(executable.ExecuteCall.Receives.Execution.Dir).To(Equal(workingDir))
Expect(executable.ExecuteCall.Receives.Execution.Env).To(Equal(append(os.Environ(), "NPM_CONFIG_LOGLEVEL=some-val", "NPM_CONFIG_GLOBALCONFIG=some-npmrc-path", "NODE_ENV=development")))
Expect(buffer.String()).To(ContainLines(
fmt.Sprintf(" Running 'npm install --unsafe-perm --cache %s'", cacheDir),
" stdout output",
" stderr output",
))

path, err := os.Readlink(filepath.Join(workingDir, "node_modules"))
Expect(err).NotTo(HaveOccurred())
Expand All @@ -95,13 +100,14 @@ func testInstallBuildProcess(t *testing.T, context spec.G, it spec.S) {
context("launch is true", func() {
it("succeeds", func() {
Expect(process.Run(modulesDir, cacheDir, workingDir, "some-npmrc-path", true)).To(Succeed())
Expect(executable.ExecuteCall.Receives.Execution).To(Equal(pexec.Execution{
Args: []string{"install", "--unsafe-perm", "--cache", cacheDir},
Dir: workingDir,
Stdout: commandOutput,
Stderr: commandOutput,
Env: append(os.Environ(), "NPM_CONFIG_LOGLEVEL=some-val", "NPM_CONFIG_GLOBALCONFIG=some-npmrc-path"),
}))
Expect(executable.ExecuteCall.Receives.Execution.Args).To(Equal([]string{"install", "--unsafe-perm", "--cache", cacheDir}))
Expect(executable.ExecuteCall.Receives.Execution.Dir).To(Equal(workingDir))
Expect(executable.ExecuteCall.Receives.Execution.Env).To(Equal(append(os.Environ(), "NPM_CONFIG_LOGLEVEL=some-val", "NPM_CONFIG_GLOBALCONFIG=some-npmrc-path")))
Expect(buffer.String()).To(ContainLines(
fmt.Sprintf(" Running 'npm install --unsafe-perm --cache %s'", cacheDir),
" stdout output",
" stderr output",
))

path, err := os.Readlink(filepath.Join(workingDir, "node_modules"))
Expect(err).NotTo(HaveOccurred())
Expand Down Expand Up @@ -148,7 +154,11 @@ func testInstallBuildProcess(t *testing.T, context spec.G, it spec.S) {

it("returns an error", func() {
err := process.Run(modulesDir, cacheDir, workingDir, "", true)
Expect(buffer.String()).To(ContainSubstring(" install error on stdout\n install error on stderr\n"))
Expect(buffer.String()).To(ContainLines(
fmt.Sprintf(" Running 'npm install --unsafe-perm --cache %s'", cacheDir),
" install error on stdout",
" install error on stderr",
))
Expect(err).To(MatchError("npm install failed: failed to execute"))
})
})
Expand Down
9 changes: 5 additions & 4 deletions integration/caching_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,13 @@ func testCaching(t *testing.T, context spec.G, it spec.S) {
" npm-cache -> \"Not found\"",
" package-lock.json -> \"Found\"",
"",
" Selected NPM build process: 'npm ci'",
"",
" Selected NPM build process: 'npm ci'"))
Expect(logs).To(ContainLines(
" Executing launch environment install process",
fmt.Sprintf(" Running 'npm ci --unsafe-perm --cache /layers/%s/npm-cache'", strings.ReplaceAll(settings.Buildpack.ID, "/", "_")),
MatchRegexp(` Completed in (\d+\.\d+|\d{3})`),
"",
))
Expect(logs).To(ContainLines(MatchRegexp(` Completed in (\d+\.\d+|\d{3})`)))
Expect(logs).To(ContainLines(
" Configuring launch environment",
" NODE_PROJECT_PATH -> \"/workspace\"",
" NPM_CONFIG_LOGLEVEL -> \"error\"",
Expand Down
26 changes: 16 additions & 10 deletions integration/logging_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,19 @@ func testLogging(t *testing.T, context spec.G, it spec.S) {
" package-lock.json -> \"Not found\"",
"",
" Selected NPM build process: 'npm install'",
"",
))
Expect(logs).To(ContainLines(
" Executing launch environment install process",
fmt.Sprintf(" Running 'npm install --unsafe-perm --cache /layers/%s/npm-cache'", strings.ReplaceAll(settings.Buildpack.ID, "/", "_")),
))
Expect(logs).To(ContainLines(
MatchRegexp(` Completed in (\d+\.\d+|\d{3})`),
"",
))
Expect(logs).To(ContainLines(
" Configuring launch environment",
" NODE_PROJECT_PATH -> \"/workspace\"",
" NPM_CONFIG_LOGLEVEL -> \"error\"",
fmt.Sprintf(" PATH -> \"$PATH:/layers/%s/launch-modules/node_modules/.bin\"", strings.ReplaceAll(settings.Buildpack.ID, "/", "_")),
"",
))
})

Expand Down Expand Up @@ -110,31 +113,34 @@ func testLogging(t *testing.T, context spec.G, it spec.S) {
" npm-cache -> \"Not found\"",
" package-lock.json -> \"Not found\"",
"",
" Selected NPM build process: 'npm install'",
"",
" Selected NPM build process: 'npm install'"))
Expect(logs).To(ContainLines(
" Executing build environment install process",
fmt.Sprintf(" Running 'npm install --unsafe-perm --cache /layers/%s/npm-cache'", strings.ReplaceAll(settings.Buildpack.ID, "/", "_")),
))
Expect(logs).To(ContainLines(
MatchRegexp(` Completed in (\d+\.\d+|\d{3})`),
"",
))
Expect(logs).To(ContainLines(
" Configuring build environment",
" NODE_ENV -> \"development\"",
fmt.Sprintf(" PATH -> \"$PATH:/layers/%s/build-modules/node_modules/.bin\"", strings.ReplaceAll(settings.Buildpack.ID, "/", "_")),
"",
fmt.Sprintf(` Generating SBOM for /layers/%s/build-modules`, strings.ReplaceAll(settings.Buildpack.ID, "/", "_")),
MatchRegexp(` Completed in (\d+)(\.\d+)?(ms|s)`),
"",
))
Expect(logs).To(ContainLines(
" Executing launch environment install process",
" Running 'npm prune'",
MatchRegexp(` Completed in (\d+\.\d+|\d{3})`),
"",
))
Expect(logs).To(ContainLines(
" Configuring launch environment",
" NODE_PROJECT_PATH -> \"/workspace\"",
" NPM_CONFIG_LOGLEVEL -> \"error\"",
fmt.Sprintf(" PATH -> \"$PATH:/layers/%s/launch-modules/node_modules/.bin\"", strings.ReplaceAll(settings.Buildpack.ID, "/", "_")),
"",
fmt.Sprintf(` Generating SBOM for /layers/%s/launch-modules`, strings.ReplaceAll(settings.Buildpack.ID, "/", "_")),
MatchRegexp(` Completed in (\d+)(\.\d+)?(ms|s)`),
"",
))
})

Expand Down
15 changes: 5 additions & 10 deletions integration/vendored_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,16 +96,11 @@ func testVendored(t *testing.T, context spec.G, it spec.S) {
" npm-cache -> \"Not found\"",
" package-lock.json -> \"Found\"",
"",
" Selected NPM build process: 'npm rebuild'",
"",
" Executing launch environment install process",
" Running 'npm run-script preinstall --if-present'",
MatchRegexp(` Running 'npm rebuild --nodedir=/layers/.+/node'`),
" Running 'npm run-script postinstall --if-present'",
))
Expect(logs).To(ContainLines(
MatchRegexp(` Completed in (\d+\.\d+|\d{3})`),
))
" Selected NPM build process: 'npm rebuild'"))
Expect(logs).To(ContainLines(" Executing launch environment install process"))
Expect(logs).To(ContainLines(" Running 'npm run-script preinstall --if-present'"))
Expect(logs).To(ContainLines(MatchRegexp(` Running 'npm rebuild --nodedir=/layers/.+/node'`)))
Expect(logs).To(ContainLines(" Running 'npm run-script postinstall --if-present'"))
Expect(logs).To(ContainLines(
" Configuring launch environment",
" NODE_PROJECT_PATH -> \"/workspace\"",
Expand Down
7 changes: 2 additions & 5 deletions prune_build_process.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package npminstall

import (
"bytes"
"fmt"
"os"
"strings"
Expand Down Expand Up @@ -41,16 +40,14 @@ func (r PruneBuildProcess) Run(modulesDir, cacheDir, workingDir, npmrcPath strin
args := []string{"prune"}
r.logger.Subprocess("Running 'npm %s'", strings.Join(args, " "))

buffer := bytes.NewBuffer(nil)
err := r.executable.Execute(pexec.Execution{
Args: args,
Dir: workingDir,
Stdout: buffer,
Stderr: buffer,
Stdout: r.logger.ActionWriter,
Stderr: r.logger.ActionWriter,
Env: environment,
})
if err != nil {
r.logger.Subprocess("%s", buffer.String())
return fmt.Errorf("npm install failed: %w", err)
}

Expand Down
Loading

0 comments on commit 56c8312

Please sign in to comment.