From 387c5f60f97141d638327f84ee426fb44f6c554e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Mon, 17 Jul 2023 19:15:48 +0200 Subject: [PATCH] Improve error messages for PostCSS etc. Fixes #9730 --- common/herrors/errors.go | 25 ++++++++++++++++++- common/herrors/errors_test.go | 10 ++++++++ config/commonConfig.go | 2 +- .../resource_transformers/babel/babel.go | 4 +-- .../postcss/integration_test.go | 19 ++++++++++++++ .../resource_transformers/postcss/postcss.go | 6 +++-- resources/transform.go | 2 +- 7 files changed, 61 insertions(+), 7 deletions(-) diff --git a/common/herrors/errors.go b/common/herrors/errors.go index 4d8642362..598c50b32 100644 --- a/common/herrors/errors.go +++ b/common/herrors/errors.go @@ -59,11 +59,34 @@ func GetGID() uint64 { return n } +// IsFeatureNotAvailableError returns true if the given error is or contains a FeatureNotAvailableError. +func IsFeatureNotAvailableError(err error) bool { + return errors.Is(err, &FeatureNotAvailableError{}) +} + // ErrFeatureNotAvailable denotes that a feature is unavailable. // // We will, at least to begin with, make some Hugo features (SCSS with libsass) optional, // and this error is used to signal those situations. -var ErrFeatureNotAvailable = errors.New("this feature is not available in your current Hugo version, see https://goo.gl/YMrWcn for more information") +var ErrFeatureNotAvailable = &FeatureNotAvailableError{Cause: errors.New("this feature is not available in your current Hugo version, see https://goo.gl/YMrWcn for more information")} + +// FeatureNotAvailableError is an error type used to signal that a feature is not available. +type FeatureNotAvailableError struct { + Cause error +} + +func (e *FeatureNotAvailableError) Unwrap() error { + return e.Cause +} + +func (e *FeatureNotAvailableError) Error() string { + return e.Cause.Error() +} + +func (e *FeatureNotAvailableError) Is(target error) bool { + _, ok := target.(*FeatureNotAvailableError) + return ok +} // Must panics if err != nil. func Must(err error) { diff --git a/common/herrors/errors_test.go b/common/herrors/errors_test.go index 1e0730028..223782e23 100644 --- a/common/herrors/errors_test.go +++ b/common/herrors/errors_test.go @@ -14,6 +14,7 @@ package herrors import ( + "errors" "fmt" "testing" @@ -34,3 +35,12 @@ func TestIsNotExist(t *testing.T) { // os.IsNotExist returns false for wrapped errors. c.Assert(IsNotExist(fmt.Errorf("foo: %w", afero.ErrFileNotFound)), qt.Equals, true) } + +func TestIsFeatureNotAvailableError(t *testing.T) { + c := qt.New(t) + + c.Assert(IsFeatureNotAvailableError(ErrFeatureNotAvailable), qt.Equals, true) + c.Assert(IsFeatureNotAvailableError(&FeatureNotAvailableError{}), qt.Equals, true) + c.Assert(IsFeatureNotAvailableError(errors.New("asdf")), qt.Equals, false) + +} diff --git a/config/commonConfig.go b/config/commonConfig.go index 2c6497b34..5cf526708 100644 --- a/config/commonConfig.go +++ b/config/commonConfig.go @@ -148,7 +148,7 @@ func (b BuildConfig) UseResourceCache(err error) bool { } if b.UseResourceCacheWhen == "fallback" { - return err == herrors.ErrFeatureNotAvailable + return herrors.IsFeatureNotAvailableError(err) } return true diff --git a/resources/resource_transformers/babel/babel.go b/resources/resource_transformers/babel/babel.go index 5f8fcb00f..2999d73cb 100644 --- a/resources/resource_transformers/babel/babel.go +++ b/resources/resource_transformers/babel/babel.go @@ -181,7 +181,7 @@ func (t *babelTransformation) Transform(ctx *resources.ResourceTransformationCtx if err != nil { if hexec.IsNotFound(err) { // This may be on a CI server etc. Will fall back to pre-built assets. - return herrors.ErrFeatureNotAvailable + return &herrors.FeatureNotAvailableError{Cause: err} } return err } @@ -200,7 +200,7 @@ func (t *babelTransformation) Transform(ctx *resources.ResourceTransformationCtx err = cmd.Run() if err != nil { if hexec.IsNotFound(err) { - return herrors.ErrFeatureNotAvailable + return &herrors.FeatureNotAvailableError{Cause: err} } return fmt.Errorf(errBuf.String()+": %w", err) } diff --git a/resources/resource_transformers/postcss/integration_test.go b/resources/resource_transformers/postcss/integration_test.go index c920fe17d..74aaa2661 100644 --- a/resources/resource_transformers/postcss/integration_test.go +++ b/resources/resource_transformers/postcss/integration_test.go @@ -168,6 +168,25 @@ func TestTransformPostCSSError(t *testing.T) { } +func TestTransformPostCSSNotInstalledError(t *testing.T) { + if !htesting.IsCI() { + t.Skip("Skip long running test when running locally") + } + + c := qt.New(t) + + s, err := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: c, + NeedsOsFS: true, + TxtarString: postCSSIntegrationTestFiles, + }).BuildE() + + s.AssertIsFileError(err) + c.Assert(err.Error(), qt.Contains, `binary with name "npx" not found`) + +} + // #9895 func TestTransformPostCSSImportError(t *testing.T) { if !htesting.IsCI() { diff --git a/resources/resource_transformers/postcss/postcss.go b/resources/resource_transformers/postcss/postcss.go index 083607246..a65fa3783 100644 --- a/resources/resource_transformers/postcss/postcss.go +++ b/resources/resource_transformers/postcss/postcss.go @@ -205,7 +205,7 @@ func (t *postcssTransformation) Transform(ctx *resources.ResourceTransformationC if err != nil { if hexec.IsNotFound(err) { // This may be on a CI server etc. Will fall back to pre-built assets. - return herrors.ErrFeatureNotAvailable + return &herrors.FeatureNotAvailableError{Cause: err} } return err } @@ -240,7 +240,9 @@ func (t *postcssTransformation) Transform(ctx *resources.ResourceTransformationC err = cmd.Run() if err != nil { if hexec.IsNotFound(err) { - return herrors.ErrFeatureNotAvailable + return &herrors.FeatureNotAvailableError{ + Cause: err, + } } return imp.toFileError(errBuf.String()) } diff --git a/resources/transform.go b/resources/transform.go index 9e5b57625..eb7d8fb0b 100644 --- a/resources/transform.go +++ b/resources/transform.go @@ -449,7 +449,7 @@ func (r *resourceAdapter) transform(publish, setContent bool) error { newErr := func(err error) error { msg := fmt.Sprintf("%s: failed to transform %q (%s)", strings.ToUpper(tr.Key().Name), tctx.InPath, tctx.InMediaType.Type) - if err == herrors.ErrFeatureNotAvailable { + if herrors.IsFeatureNotAvailableError(err) { var errMsg string if tr.Key().Name == "postcss" { // This transformation is not available in this