Skip to content

Commit

Permalink
Adds support for a WithMessage modifier on packit.Fail
Browse files Browse the repository at this point in the history
For example, a call to `packit.Fail.WithMessage("my error")` will result
in an error that triggers the correct fail exit code for the buildpack
lifecycle, and also included the given message. This change is backwards
compatible and so should not impact any buildpacks currently just using
`packit.Fail`.
  • Loading branch information
Ryan Moran authored and joshzarrabi committed Jun 23, 2020
1 parent ccfe769 commit 9a35314
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 14 deletions.
5 changes: 0 additions & 5 deletions detect.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@ import (
"github.com/paketo-buildpacks/packit/internal"
)

// Fail is a sentinal value that can be used to indicate a failure to detect
// during the detect phase. Fail implements the Error interface and should be
// returned as the error value in the DetectFunc signature.
var Fail = internal.Fail

// DetectContext provides the contextual details that are made available by the
// buildpack lifecycle during the detect phase. This context is populated by
// the Detect function and passed to the DetectFunc during execution.
Expand Down
19 changes: 15 additions & 4 deletions detect_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package packit_test

import (
"bytes"
"errors"
"io/ioutil"
"os"
Expand Down Expand Up @@ -230,14 +231,24 @@ some-key = "some-value"
context("when the DetectFunc fails", func() {
it("calls the ExitHandler with the correct exit code", func() {
var exitCode int
buffer := bytes.NewBuffer(nil)

packit.Detect(func(ctx packit.DetectContext) (packit.DetectResult, error) {
return packit.DetectResult{}, packit.Fail
}, packit.WithArgs([]string{binaryPath, "", ""}), packit.WithExitHandler(internal.NewExitHandler(internal.WithExitHandlerExitFunc(func(code int) {
exitCode = code
}))))
return packit.DetectResult{}, packit.Fail.WithMessage("failure message")
},
packit.WithArgs([]string{binaryPath, "", ""}),
packit.WithExitHandler(
internal.NewExitHandler(
internal.WithExitHandlerExitFunc(func(code int) {
exitCode = code
}),
internal.WithExitHandlerStderr(buffer),
),
),
)

Expect(exitCode).To(Equal(100))
Expect(buffer.String()).To(Equal("failure message\n"))
})
})

Expand Down
12 changes: 12 additions & 0 deletions fail.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package packit

import "github.com/paketo-buildpacks/packit/internal"

// Fail is a sentinal value that can be used to indicate a failure to detect
// during the detect phase. Fail implements the Error interface and should be
// returned as the error value in the DetectFunc signature. Fail also supports
// a modifier function, WithMessage, that allows the caller to set a custom
// failure message. The WithMessage function supports a fmt.Printf-like format
// string and variadic arguments to build a message, eg:
// packit.Fail.WithMessage("failed: %w", err).
var Fail = internal.Fail
7 changes: 2 additions & 5 deletions internal/exit_handler.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
package internal

import (
"errors"
"fmt"
"io"
"os"
)

var Fail = errors.New("failed")

type Option func(handler ExitHandler) ExitHandler

func WithExitHandlerStderr(stderr io.Writer) Option {
Expand Down Expand Up @@ -57,8 +54,8 @@ func (h ExitHandler) Error(err error) {
fmt.Fprintln(h.stderr, err)

var code int
switch err {
case Fail:
switch err.(type) {
case failError:
code = 100
case nil:
code = 0
Expand Down
16 changes: 16 additions & 0 deletions internal/fail.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package internal

import (
"errors"
"fmt"
)

var Fail = failError{error: errors.New("failed")}

type failError struct {
error
}

func (f failError) WithMessage(format string, v ...interface{}) failError {
return failError{error: fmt.Errorf(format, v...)}
}
28 changes: 28 additions & 0 deletions internal/fail_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package internal_test

import (
"testing"

"github.com/paketo-buildpacks/packit/internal"
"github.com/sclevine/spec"

. "github.com/onsi/gomega"
)

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

it("acts as an error", func() {
fail := internal.Fail
Expect(fail).To(MatchError("failed"))
})

context("when given a message", func() {
it("acts as an error with that message", func() {
fail := internal.Fail.WithMessage("this is a %s", "failure message")
Expect(fail).To(MatchError("this is a failure message"))
})
})
}
1 change: 1 addition & 0 deletions internal/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ func TestUnitInternal(t *testing.T) {
suite := spec.New("packit/internal", spec.Report(report.Terminal{}))
suite("EnvironmentWriter", testEnvironmentWriter)
suite("ExitHandler", testExitHandler)
suite("Fail", testFail)
suite("TOMLWriter", testTOMLWriter)
suite.Run(t)
}

0 comments on commit 9a35314

Please sign in to comment.