Improve error messages for PostCSS etc.

Fixes #9730
This commit is contained in:
Bjørn Erik Pedersen 2023-07-17 19:15:48 +02:00
parent c406fd3a0e
commit 387c5f60f9
7 changed files with 61 additions and 7 deletions

View file

@ -59,11 +59,34 @@ func GetGID() uint64 {
return n 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. // ErrFeatureNotAvailable denotes that a feature is unavailable.
// //
// We will, at least to begin with, make some Hugo features (SCSS with libsass) optional, // We will, at least to begin with, make some Hugo features (SCSS with libsass) optional,
// and this error is used to signal those situations. // 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. // Must panics if err != nil.
func Must(err error) { func Must(err error) {

View file

@ -14,6 +14,7 @@
package herrors package herrors
import ( import (
"errors"
"fmt" "fmt"
"testing" "testing"
@ -34,3 +35,12 @@ func TestIsNotExist(t *testing.T) {
// os.IsNotExist returns false for wrapped errors. // os.IsNotExist returns false for wrapped errors.
c.Assert(IsNotExist(fmt.Errorf("foo: %w", afero.ErrFileNotFound)), qt.Equals, true) 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)
}

View file

@ -148,7 +148,7 @@ func (b BuildConfig) UseResourceCache(err error) bool {
} }
if b.UseResourceCacheWhen == "fallback" { if b.UseResourceCacheWhen == "fallback" {
return err == herrors.ErrFeatureNotAvailable return herrors.IsFeatureNotAvailableError(err)
} }
return true return true

View file

@ -181,7 +181,7 @@ func (t *babelTransformation) Transform(ctx *resources.ResourceTransformationCtx
if err != nil { if err != nil {
if hexec.IsNotFound(err) { if hexec.IsNotFound(err) {
// This may be on a CI server etc. Will fall back to pre-built assets. // 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 return err
} }
@ -200,7 +200,7 @@ func (t *babelTransformation) Transform(ctx *resources.ResourceTransformationCtx
err = cmd.Run() err = cmd.Run()
if err != nil { if err != nil {
if hexec.IsNotFound(err) { if hexec.IsNotFound(err) {
return herrors.ErrFeatureNotAvailable return &herrors.FeatureNotAvailableError{Cause: err}
} }
return fmt.Errorf(errBuf.String()+": %w", err) return fmt.Errorf(errBuf.String()+": %w", err)
} }

View file

@ -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 // #9895
func TestTransformPostCSSImportError(t *testing.T) { func TestTransformPostCSSImportError(t *testing.T) {
if !htesting.IsCI() { if !htesting.IsCI() {

View file

@ -205,7 +205,7 @@ func (t *postcssTransformation) Transform(ctx *resources.ResourceTransformationC
if err != nil { if err != nil {
if hexec.IsNotFound(err) { if hexec.IsNotFound(err) {
// This may be on a CI server etc. Will fall back to pre-built assets. // 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 return err
} }
@ -240,7 +240,9 @@ func (t *postcssTransformation) Transform(ctx *resources.ResourceTransformationC
err = cmd.Run() err = cmd.Run()
if err != nil { if err != nil {
if hexec.IsNotFound(err) { if hexec.IsNotFound(err) {
return herrors.ErrFeatureNotAvailable return &herrors.FeatureNotAvailableError{
Cause: err,
}
} }
return imp.toFileError(errBuf.String()) return imp.toFileError(errBuf.String())
} }

View file

@ -449,7 +449,7 @@ func (r *resourceAdapter) transform(publish, setContent bool) error {
newErr := func(err error) 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) 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 var errMsg string
if tr.Key().Name == "postcss" { if tr.Key().Name == "postcss" {
// This transformation is not available in this // This transformation is not available in this